import React, { useState } from 'react'; import { Create } from "@refinedev/antd"; import { useCreate, useNotification } from "@refinedev/core"; import { Form, Input, Button, Card, Steps, Alert, Typography, Space, Divider, Tag, Spin } from 'antd'; import { GitlabOutlined, FileTextOutlined, CheckCircleOutlined, ArrowLeftOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router'; import { API_URL } from '../../config'; const { Title, Text, Paragraph } = Typography; const { Step } = Steps; interface ComposeImportForm { app_name?: string; source_url: string; branch: string; compose_path: string; } interface ParsedService { name: string; source: 'build' | 'image'; build_context?: string; dockerfile?: string; image?: string; ports?: string[]; environment?: Record; volumes?: string[]; } interface ComposeReviewResult { app_name: string; valid: boolean; error?: string; services: ParsedService[]; } export const AppImportCompose = () => { const [currentStep, setCurrentStep] = useState(0); const [form] = Form.useForm(); const [parseResult, setParseResult] = useState(null); const [loading, setLoading] = useState(false); const navigate = useNavigate(); const { open } = useNotification(); const { mutate: createApp } = useCreate(); const handleParseCompose = async (values: ComposeImportForm) => { setLoading(true); try { // Call the backend API to parse the docker-compose.yml const response = await fetch(`${API_URL}/apps/import/review`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('byop-auth')}`, }, body: JSON.stringify({ source_url: values.source_url, branch: values.branch, compose_path: values.compose_path, app_name: values.app_name, }), }); if (!response.ok) { throw new Error('Failed to parse docker-compose.yml'); } const result: ComposeReviewResult = await response.json(); if (!result.valid) { open?.({ type: 'error', message: 'Invalid Compose File', description: result.error || 'The docker-compose.yml file could not be parsed.', }); return; } setParseResult(result); setCurrentStep(1); } catch (error) { open?.({ type: 'error', message: 'Parse Error', description: 'Failed to parse the docker-compose.yml file. Please check your repository URL and file path.', }); } finally { setLoading(false); } }; const handleCreateApp = async () => { if (!parseResult) return; setLoading(true); try { // Call the backend API to create the app and components const response = await fetch(`${API_URL}/apps/import/create`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('byop-auth')}`, }, body: JSON.stringify({ source_url: form.getFieldValue('source_url'), branch: form.getFieldValue('branch'), compose_path: form.getFieldValue('compose_path'), app_name: form.getFieldValue('app_name'), confirmed_app_name: parseResult.app_name, }), }); if (!response.ok) { throw new Error('Failed to create app from compose'); } const createdApp = await response.json(); open?.({ type: 'success', message: 'App Created Successfully', description: `Your app "${parseResult.app_name}" has been created with ${parseResult.services.length} components.`, }); // Redirect to the new app's show page navigate(`/apps/show/${createdApp.id}`); } catch (error) { open?.({ type: 'error', message: 'Creation Error', description: 'Failed to create the app. Please try again.', }); } finally { setLoading(false); } }; const renderImportForm = () => ( } onClick={() => navigate('/apps/create')} > Back to Create Options }>
} placeholder="https://github.com/username/repository.git" /> } placeholder="docker-compose.yml" />
); const renderReviewStep = () => (
App Name: {parseResult?.app_name} Services Found: {parseResult?.services.map((service: ParsedService, index: number) => ( {service.name} Service: {service.name} } > {service.source === 'build' && service.build_context && ( Source: Will be built from the {service.build_context} directory. {service.dockerfile && Using {service.dockerfile}} )} {service.source === 'image' && service.image && ( Source: Uses the public image {service.image}. )} {service.ports && service.ports.length > 0 && ( Ports: {service.ports.join(', ')} )} {service.environment && Object.keys(service.environment).length > 0 && ( Environment Variables: {Object.keys(service.environment).length} variables defined )} {service.volumes && service.volumes.length > 0 && ( Volumes: {service.volumes.length} volume mappings )} ))}
); return ( {currentStep === 0 && renderImportForm()} {currentStep === 1 && renderReviewStep()} ); };