diff --git a/src/components/DomainSetupForm.jsx b/src/components/DomainSetupForm.jsx
deleted file mode 100644
index 87e74f2..0000000
--- a/src/components/DomainSetupForm.jsx
+++ /dev/null
@@ -1,810 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { Toast } from './Toast';
-import { TemplatePreview } from './TemplatePreview';
-
-export const DomainSetupForm = ({ defaultSubdomain }) => {
- // Deployment type and app selections
- const [deploymentType, setDeploymentType] = useState('app');
- const [appType, setAppType] = useState('wordpress');
- const [sampleWebAppType, setSampleWebAppType] = useState('developer'); // New state for sample web app type
- const [sourceType, setSourceType] = useState('public');
- const [repoUrl, setRepoUrl] = useState('');
- const [deploymentKey, setDeploymentKey] = useState('');
- const [fileName, setFileName] = useState('');
-
- // Domain configuration
- const [useSubdomain, setUseSubdomain] = useState(true);
- const [useCustomDomain, setUseCustomDomain] = useState(false);
- const [customDomain, setCustomDomain] = useState('');
- const [customSubdomain, setCustomSubdomain] = useState('');
- const [domainType, setDomainType] = useState('domain'); // 'domain' or 'subdomain'
-
- // Domain validation states
- const [isValidating, setIsValidating] = useState(false);
- const [isValidDomain, setIsValidDomain] = useState(false);
- const [validationMessage, setValidationMessage] = useState('');
-
- // DNS configuration
- const [dnsMethod, setDnsMethod] = useState('cname');
- const [showDnsConfig, setShowDnsConfig] = useState(false);
- const [dnsVerified, setDnsVerified] = useState({
- cname: false,
- ns: false,
- a: false,
- ip: false
- });
-
- // Form validation
- const [formValid, setFormValid] = useState(true);
-
- // Toast notification
- const [toast, setToast] = useState({ visible: false, message: '' });
-
- // File upload reference
- const fileInputRef = React.useRef(null);
-
- // Effect for handling domain type changes
- useEffect(() => {
- if (!useCustomDomain) {
- setShowDnsConfig(false);
- setIsValidDomain(false);
- setValidationMessage('');
- setIsValidating(false);
- }
-
- validateForm();
- }, [useCustomDomain, dnsVerified.cname, dnsVerified.ns, dnsVerified.ns, domainType, dnsMethod]);
-
- // Show toast notification
- const showToast = (message) => {
- setToast({ visible: true, message });
- setTimeout(() => setToast({ visible: false, message: '' }), 3000);
- };
-
- // Handle deployment type change
- const handleDeploymentTypeChange = (e) => {
- setDeploymentType(e.target.value);
- };
-
- // Handle source type change
- const handleSourceTypeChange = (e) => {
- setSourceType(e.target.value);
- };
-
- // Handle file upload
- const handleFileChange = (e) => {
- if (e.target.files.length > 0) {
- setFileName(e.target.files[0].name);
- } else {
- setFileName('');
- }
- };
-
- // Handle domain checkbox changes
- const handleUseSubdomainChange = (e) => {
- setUseSubdomain(e.target.checked);
-
- // If CNAME record is selected, SiliconPin subdomain must be enabled
- if (useCustomDomain && dnsMethod === 'cname' && !e.target.checked) {
- setUseCustomDomain(false);
- }
-
- validateForm();
- };
-
- const handleUseCustomDomainChange = (e) => {
- setUseCustomDomain(e.target.checked);
- if (!e.target.checked) {
- setShowDnsConfig(false);
- setIsValidDomain(false);
- setValidationMessage('');
- setIsValidating(false);
- setDnsVerified({
- cname: false,
- ns: false,
- a: false,
- ip: false
- });
- } else {
- // Force SiliconPin subdomain to be checked if custom domain is checked
- setUseSubdomain(true);
- }
-
- validateForm();
- };
-
- // Handle domain type change
- const handleDomainTypeChange = (e) => {
- setDomainType(e.target.value);
-
- // Reset validation when changing domain type
- setIsValidDomain(false);
- setValidationMessage('');
- setDnsVerified({
- cname: false,
- ns: false,
- a: false
- });
-
- validateForm();
- };
-
- // Handle domain and subdomain input changes
- const handleDomainChange = (e) => {
- const cleanedValue = e.target.value.replace(/^(https?:\/\/)?(www\.)?/i, '').replace(/\/+$/, '').trim();
- setCustomDomain(cleanedValue);
- };
-
- const handleSubdomainChange = (e) => {
- const cleanedValue = e.target.value.replace(/^(https?:\/\/)?/i, '').replace(/\/+$/, '').trim();
- setCustomSubdomain(cleanedValue);
- };
-
- // Handle DNS method change
- const handleDnsMethodChange = (e) => {
- setDnsMethod(e.target.value);
-
- // If changing to CNAME, ensure SiliconPin subdomain is enabled
- if (e.target.value === 'cname') {
- setUseSubdomain(true);
- }
-
- // Reset DNS verification
- setDnsVerified(prev => ({
- ...prev,
- [e.target.value]: false
- }));
-
- validateForm();
- };
-
- // Validate domain
- const validateDomain = () => {
- const domain = domainType === 'domain' ? customDomain : customSubdomain;
-
- if (!domain) {
- setValidationMessage('Please enter a domain name.');
- setIsValidDomain(false);
- return;
- }
-
- // Initial validation: check format with regex
- const validFormat = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(domain);
- if (!validFormat) {
- setValidationMessage('Domain format is invalid. Please check your entry.');
- setIsValidDomain(false);
- return;
- }
-
- setIsValidating(true);
- setValidationMessage('');
- setShowDnsConfig(false);
-
-<<<<<<< HEAD
- fetch('/validate-domain', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- domain,
- type: domainType
- })
- })
- .then(response => {
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- return response.json();
- })
- .then(data => {
- const checkResult = data.status === "success";
- console.log("finding:: checkResult:: ", checkResult, data);
-=======
- // Simulate an API call to validate the domain
- setTimeout(() => {
- // Simulate a real domain check - in a real app this would be an API call
- // call /host-api/v1/domains/validate/?domain=domain.com
- const checkResult = true; // Assume domain is valid for demo
->>>>>>> origin/staging
-
- setIsValidating(false);
- setIsValidDomain(checkResult);
-
- if (checkResult) {
- setValidationMessage('Domain is valid and registered.');
- setShowDnsConfig(true);
- } else {
- setValidationMessage('Domain appears to be unregistered or unavailable.');
- }
-
-<<<<<<< HEAD
- validateForm();
- })
- .catch(error => {
- console.error('Error validating domain:', error);
- setIsValidating(false);
- setIsValidDomain(false);
- setValidationMessage('Error checking domain. Please try again.');
- validateForm();
- });
-=======
- validateForm();
- }, 500);
->>>>>>> origin/staging
- };
-
- // Check DNS configuration
- const checkSubDomainCname = () => {
- const domainToCheck = customDomain || customSubdomain;
-
- fetch('http://localhost:2058/host-api/v1/check-c-name/', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: `domain=${encodeURIComponent(domainToCheck)}`
- })
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => {
- if (data.status === 'success') {
- checkDnsConfig('cname');
- showToast(`Checking ${type}... (This would verify DNS in a real app)`);
- console.log('CNAME record:', data.cname);
- // Handle success - update UI
- } else {
- console.error('Error:', data.message);
- // Handle error
- }
- })
- .catch(error => {
- console.error('Fetch error:', error);
- // Show error to user
- });
- };
- const checkDnsConfig = (type) => {
- showToast(`Checking ${type}... (This would verify DNS in a real app)`);
-
- // Simulate DNS check
- setTimeout(() => {
- setDnsVerified(prev => ({
- ...prev,
- [type]: true
- }));
-
- showToast(`${type} verified successfully!`);
- validateForm();
- }, 1500);
- };
-
- // Copy to clipboard
- const copyToClipboard = (text) => {
- navigator.clipboard.writeText(text)
- .then(() => {
- showToast('Copied to clipboard!');
- })
- .catch(err => {
- showToast('Failed to copy: ' + err);
- });
- };
-
- // Validate form
- const validateForm = () => {
- // For custom domain, require DNS verification
- if (useCustomDomain) {
- if (dnsMethod === 'cname' && !dnsVerified.cname) {
- setFormValid(false);
- return;
- }
-
- if (dnsMethod === 'ns' && !dnsVerified.ns) {
- setFormValid(false);
- return;
- }
- if (dnsMethod === 'ip' && !dnsVerified.ip) {
- setFormValid(false);
- return;
- }
- }
-
- setFormValid(true);
- };
-
- // Handle form submission
- const handleSubmit = (e) => {
- e.preventDefault();
-
- if (!formValid) {
- showToast('Please complete DNS verification before deploying.');
- return;
- }
-
- // In a real app, this would submit the form data to the server
- console.log([{ deploymentType, appType, sampleWebAppType, sourceType, repoUrl, deploymentKey, useSubdomain, useCustomDomain, customDomain, customSubdomain, domainType, dnsMethod}]);
-
- showToast('Form submitted successfully!');
- };
-
- return (
-
- );
-};
diff --git a/src/components/DomainSetupForm/DeploymentOptions/AppDeployment.jsx b/src/components/DomainSetupForm/DeploymentOptions/AppDeployment.jsx
new file mode 100644
index 0000000..c6fd7ae
--- /dev/null
+++ b/src/components/DomainSetupForm/DeploymentOptions/AppDeployment.jsx
@@ -0,0 +1,30 @@
+import React from 'react';
+
+/**
+ * Component for app deployment options
+ * @param {Object} props - Component props
+ * @param {string} props.appType - Selected app type
+ * @param {Function} props.onAppTypeChange - Handler for app type change
+ * @returns {JSX.Element} - Rendered component
+ */
+const AppDeployment = ({ appType, onAppTypeChange }) => {
+ return (
+
+
+
+
+ );
+};
+
+export default AppDeployment;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DeploymentOptions/SourceDeployment.jsx b/src/components/DomainSetupForm/DeploymentOptions/SourceDeployment.jsx
new file mode 100644
index 0000000..bc2fbc0
--- /dev/null
+++ b/src/components/DomainSetupForm/DeploymentOptions/SourceDeployment.jsx
@@ -0,0 +1,79 @@
+import React from 'react';
+
+/**
+ * Component for source code deployment options
+ * @param {Object} props - Component props
+ * @param {string} props.sourceType - Source type (public/private)
+ * @param {string} props.repoUrl - Repository URL
+ * @param {string} props.deploymentKey - Deployment key for private repos
+ * @param {Function} props.onSourceTypeChange - Handler for source type change
+ * @param {Function} props.onRepoUrlChange - Handler for repo URL change
+ * @param {Function} props.onDeploymentKeyChange - Handler for deployment key change
+ * @param {Function} props.showToast - Function to show toast notifications
+ * @returns {JSX.Element} - Rendered component
+ */
+const SourceDeployment = ({
+ sourceType,
+ repoUrl,
+ deploymentKey,
+ onSourceTypeChange,
+ onRepoUrlChange,
+ onDeploymentKeyChange,
+ showToast
+ }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {sourceType === 'private' && (
+
+
+
+
+
+
+
Your private key is used only for deploying and is never stored on our servers.
+
+ )}
+
+ );
+};
+
+export default SourceDeployment;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DeploymentOptions/StaticDeployment.jsx b/src/components/DomainSetupForm/DeploymentOptions/StaticDeployment.jsx
new file mode 100644
index 0000000..6754315
--- /dev/null
+++ b/src/components/DomainSetupForm/DeploymentOptions/StaticDeployment.jsx
@@ -0,0 +1,53 @@
+import React, { useRef } from 'react';
+
+/**
+ * Component for static site deployment options
+ * @param {Object} props - Component props
+ * @param {string} props.fileName - Selected file name
+ * @param {Function} props.onFileChange - Handler for file change
+ * @returns {JSX.Element} - Rendered component
+ */
+const StaticDeployment = ({ fileName, onFileChange }) => {
+ const fileInputRef = useRef(null);
+
+ return (
+
+
+ Upload the zip file containing your static website
+
+
+
+
+
+
+
+ {fileName && (
+
+ Selected file: {fileName}
+
+ )}
+
+
+ );
+};
+
+export default StaticDeployment;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DeploymentOptions/TemplateDeployment.jsx b/src/components/DomainSetupForm/DeploymentOptions/TemplateDeployment.jsx
new file mode 100644
index 0000000..b1f344a
--- /dev/null
+++ b/src/components/DomainSetupForm/DeploymentOptions/TemplateDeployment.jsx
@@ -0,0 +1,46 @@
+import React from 'react';
+
+/**
+ * Component for sample web app deployment options
+ * @param {Object} props - Component props
+ * @param {string} props.templateType - Selected template type
+ * @param {Function} props.onTemplateTypeChange - Handler for template type change
+ * @param {React.Component} props.TemplatePreview - Template preview component
+ * @returns {JSX.Element} - Rendered component
+ */
+const TemplateDeployment = ({ templateType, onTemplateTypeChange, TemplatePreview }) => {
+ return (
+
+
+
+
+ {/* Information box */}
+
+
+
âšī¸
+
+
+ Deploy a ready-to-use template that you can customize. We'll set up the basic structure and you can modify it to fit your needs.
+
+
+
+
+
+ {/* Template Preview */}
+
+
+ );
+};
+
+export default TemplateDeployment;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DeploymentOptions/index.jsx b/src/components/DomainSetupForm/DeploymentOptions/index.jsx
new file mode 100644
index 0000000..bfd679d
--- /dev/null
+++ b/src/components/DomainSetupForm/DeploymentOptions/index.jsx
@@ -0,0 +1,98 @@
+import React from 'react';
+import AppDeployment from './AppDeployment';
+import SourceDeployment from './SourceDeployment';
+import StaticDeployment from './StaticDeployment';
+import TemplateDeployment from './TemplateDeployment';
+
+/**
+ * Main component for deployment options
+ * @param {Object} props - Component props
+ * @param {Object} props.deploymentConfig - Deployment configuration state
+ * @param {Object} props.handlers - Event handlers for deployment options
+ * @param {Function} props.showToast - Function to show toast notifications
+ * @param {React.Component} props.TemplatePreview - Template preview component
+ * @returns {JSX.Element} - Rendered component
+ */
+const DeploymentOptions = ({
+ deploymentConfig,
+ handlers,
+ showToast,
+ TemplatePreview
+ }) => {
+ const {
+ type,
+ appType,
+ sampleWebAppType,
+ sourceType,
+ repoUrl,
+ deploymentKey,
+ fileName
+ } = deploymentConfig;
+
+ const {
+ handleDeploymentTypeChange,
+ handleAppTypeChange,
+ handleSampleWebAppTypeChange,
+ handleSourceTypeChange,
+ handleRepoUrlChange,
+ handleDeploymentKeyChange,
+ handleFileChange
+ } = handlers;
+
+ return (
+
+ {/* Deployment Type Selection */}
+
+
+
+
+
+ {/* Render different components based on deployment type */}
+ {type === 'app' && (
+
+ )}
+
+ {type === 'sample-web-app' && (
+
+ )}
+
+ {type === 'source' && (
+
+ )}
+
+ {type === 'static' && (
+
+ )}
+
+ );
+};
+
+export default DeploymentOptions;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DomainConfiguration/CustomDomain.jsx b/src/components/DomainSetupForm/DomainConfiguration/CustomDomain.jsx
new file mode 100644
index 0000000..c3a876b
--- /dev/null
+++ b/src/components/DomainSetupForm/DomainConfiguration/CustomDomain.jsx
@@ -0,0 +1,155 @@
+import React from 'react';
+import DnsConfiguration from './DnsConfiguration';
+
+/**
+ * Component for custom domain configuration
+ * @param {Object} props - Component props
+ * @param {Object} props.domainConfig - Domain configuration state
+ * @param {Object} props.validation - Domain validation state
+ * @param {Object} props.dnsVerified - DNS verification state
+ * @param {Object} props.handlers - Event handlers for domain config
+ * @param {Function} props.checkDnsConfig - Function to check DNS configuration
+ * @param {string} props.defaultSubdomain - Default SiliconPin subdomain
+ * @param {Function} props.showToast - Function to show toast notifications
+ * @returns {JSX.Element} - Rendered component
+ */
+const CustomDomain = ({
+ domainConfig,
+ validation,
+ dnsVerified,
+ handlers,
+ checkDnsConfig,
+ defaultSubdomain,
+ showToast
+ }) => {
+ const {
+ domainType,
+ customDomain,
+ customSubdomain,
+ dnsMethod
+ } = domainConfig;
+
+ const {
+ isValidating,
+ isValidDomain,
+ validationMessage,
+ showDnsConfig
+ } = validation;
+
+ const {
+ handleDomainTypeChange,
+ handleDomainChange,
+ handleSubdomainChange,
+ handleDnsMethodChange,
+ validateDomain
+ } = handlers;
+
+ return (
+
+ {/* Domain Type Selection */}
+
+
+ {/* Domain Input */}
+ {domainType === 'domain' ? (
+
+
+
+ Enter domain without http://, www, or trailing slashes (example.com). You can configure www or other subdomains later.
+
+
+ ) : (
+
+
+
+ Enter the full subdomain without http:// or trailing slashes. www and protocol prefixes will be automatically removed.
+
+
+ )}
+
+ {/* Domain Validation */}
+
+
+ {/* Validation Status */}
+ {isValidating && (
+
+
+
+
Verifying domain registration and availability...
+
+
+ )}
+
+ {!isValidating && validationMessage && (
+
+
+ {validationMessage}
+
+
+ )}
+
+ {/* DNS Configuration Options */}
+ {showDnsConfig && (
+
+ )}
+
+ );
+};
+
+export default CustomDomain;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DomainConfiguration/DnsConfiguration.jsx b/src/components/DomainSetupForm/DomainConfiguration/DnsConfiguration.jsx
new file mode 100644
index 0000000..0983266
--- /dev/null
+++ b/src/components/DomainSetupForm/DomainConfiguration/DnsConfiguration.jsx
@@ -0,0 +1,161 @@
+import React from 'react';
+import { copyToClipboard } from '../../../utils/domainUtils';
+
+/**
+ * Component for DNS configuration options
+ * @param {Object} props - Component props
+ * @param {string} props.domainType - Domain type ('domain' or 'subdomain')
+ * @param {string} props.dnsMethod - DNS method ('cname' or 'ns')
+ * @param {string} props.defaultSubdomain - Default SiliconPin subdomain
+ * @param {Object} props.dnsVerified - DNS verification state
+ * @param {Function} props.onDnsMethodChange - Handler for DNS method change
+ * @param {Function} props.checkDnsConfig - Function to check DNS configuration
+ * @param {Function} props.showToast - Function to show toast notifications
+ * @returns {JSX.Element} - Rendered component
+ */
+const DnsConfiguration = ({
+ domainType,
+ dnsMethod,
+ defaultSubdomain,
+ dnsVerified,
+ onDnsMethodChange,
+ checkDnsConfig,
+ showToast
+ }) => {
+ // Handle copy to clipboard with toast feedback
+ const handleCopyToClipboard = async (text) => {
+ const result = await copyToClipboard(text);
+ if (result.success) {
+ showToast('Copied to clipboard!');
+ } else {
+ showToast(`Failed to copy: ${result.error}`);
+ }
+ };
+
+ return (
+
+
Connect Your Domain
+
+ {/* CNAME Record Option */}
+
+
+
+
+
+
Point your domain to our SiliconPin subdomain
+
+
+
+ {defaultSubdomain}.subdomain.siliconpin.com
+
+
+
+
+
+
+
+
+
+
+ {/* Nameserver Option (only for full domains, not subdomains) */}
+ {domainType === 'domain' && (
+
+
+
+
+
+
Update your domain's nameservers to use ours
+
+
+
+
ns1.siliconpin.com
+
+
+
+
+
ns2.siliconpin.com
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
+
+export default DnsConfiguration;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/DomainConfiguration/index.jsx b/src/components/DomainSetupForm/DomainConfiguration/index.jsx
new file mode 100644
index 0000000..1e35d6f
--- /dev/null
+++ b/src/components/DomainSetupForm/DomainConfiguration/index.jsx
@@ -0,0 +1,91 @@
+import React from 'react';
+import CustomDomain from './CustomDomain';
+
+/**
+ * Main component for domain configuration
+ * @param {Object} props - Component props
+ * @param {Object} props.domainConfig - Domain configuration state
+ * @param {Object} props.validation - Domain validation state
+ * @param {Object} props.dnsVerified - DNS verification state
+ * @param {Object} props.handlers - Event handlers for domain config
+ * @param {Function} props.checkDnsConfig - Function to check DNS config
+ * @param {string} props.defaultSubdomain - Default SiliconPin subdomain
+ * @param {Function} props.showToast - Function to show toast notifications
+ * @returns {JSX.Element} - Rendered component
+ */
+const DomainConfiguration = ({
+ domainConfig,
+ validation,
+ dnsVerified,
+ handlers,
+ checkDnsConfig,
+ defaultSubdomain,
+ showToast
+ }) => {
+ const { useSubdomain, useCustomDomain } = domainConfig;
+ const {
+ handleUseSubdomainChange,
+ handleUseCustomDomainChange
+ } = handlers;
+
+ return (
+
+
Destination
+
+
+ {/* SiliconPin Subdomain */}
+
+
+
+
+
+
+ .subdomain.siliconpin.com
+
+
+
+
+ {/* Custom Domain */}
+
+
+
+
+
+ {useCustomDomain && (
+
+ )}
+
+
+
+
+ );
+};
+
+export default DomainConfiguration;
\ No newline at end of file
diff --git a/src/components/DomainSetupForm/index.jsx b/src/components/DomainSetupForm/index.jsx
new file mode 100644
index 0000000..ff10f68
--- /dev/null
+++ b/src/components/DomainSetupForm/index.jsx
@@ -0,0 +1,136 @@
+import React, { useCallback } from 'react';
+import DeploymentOptions from './DeploymentOptions';
+import DomainConfiguration from './DomainConfiguration';
+import { Toast } from '../shared/Toast';
+import { TemplatePreview } from '../shared/TemplatePreview';
+
+// Custom hooks
+import useDeploymentConfig from '../../hooks/useDeploymentConfig';
+import useDomainConfig from '../../hooks/useDomainConfig';
+import useDnsVerification from '../../hooks/useDnsVerification';
+import useToast from '../../hooks/useToast';
+import useFormValidation from '../../hooks/useFormValidation';
+
+/**
+ * Main DomainSetupForm component
+ * @param {Object} props - Component props
+ * @param {string} props.defaultSubdomain - Default SiliconPin subdomain
+ * @returns {JSX.Element} - Rendered component
+ */
+export const DomainSetupForm = ({ defaultSubdomain }) => {
+ // Initialize hooks for different concerns
+ const { toast, showToast } = useToast();
+
+ // Deployment configuration
+ const deploymentConfig = useDeploymentConfig();
+
+ // DNS verification (depends on domain config for reset logic)
+ const dnsVerificationHook = useDnsVerification(showToast);
+ const { dnsVerified, checkDnsConfig, resetAllDnsVerification } = dnsVerificationHook;
+
+ // Domain configuration (needs DNS reset function)
+ const domainConfig = useDomainConfig({}, resetAllDnsVerification);
+
+ // Pass the domain config to DNS verification hook for dependency tracking
+ // This is done after initialization to avoid circular dependencies
+ dnsVerificationHook.domainConfig = domainConfig.config;
+
+ // Form validation based on domain and DNS state
+ const { formValid } = useFormValidation(
+ domainConfig.config,
+ domainConfig.validation,
+ dnsVerified
+ );
+
+ // Form submission handler
+ const handleSubmit = useCallback((e) => {
+ e.preventDefault();
+
+ if (domainConfig.useCustomDomain && !formValid) {
+ showToast('Please complete domain validation and DNS verification before deploying.');
+ return;
+ }
+
+ // In a real app, this would submit the form data to the server
+ console.log({
+ deploymentType: deploymentConfig.type,
+ appType: deploymentConfig.appType,
+ sampleWebAppType: deploymentConfig.sampleWebAppType,
+ sourceType: deploymentConfig.sourceType,
+ repoUrl: deploymentConfig.repoUrl,
+ deploymentKey: deploymentConfig.deploymentKey,
+ useSubdomain: domainConfig.useSubdomain,
+ useCustomDomain: domainConfig.useCustomDomain,
+ customDomain: domainConfig.customDomain,
+ customSubdomain: domainConfig.customSubdomain,
+ domainType: domainConfig.domainType,
+ dnsMethod: domainConfig.dnsMethod
+ });
+
+ showToast('Form submitted successfully!');
+ }, [
+ formValid,
+ showToast,
+ deploymentConfig,
+ domainConfig
+ ]);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default DomainSetupForm;
\ No newline at end of file
diff --git a/src/components/atoms/SelectWithLabel/SelectWithLabel.tsx b/src/components/atoms/SelectWithLabel/SelectWithLabel.tsx
new file mode 100644
index 0000000..bb2173c
--- /dev/null
+++ b/src/components/atoms/SelectWithLabel/SelectWithLabel.tsx
@@ -0,0 +1,22 @@
+import React from "react";
+
+const SelectWithLabel = ({value, onChange}: any)=>{
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default SelectWithLabel;
\ No newline at end of file
diff --git a/src/components/atoms/SelectWithLabel/index.tsx b/src/components/atoms/SelectWithLabel/index.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/shared/TemplatePreview.jsx b/src/components/shared/TemplatePreview.jsx
new file mode 100644
index 0000000..2ae9190
--- /dev/null
+++ b/src/components/shared/TemplatePreview.jsx
@@ -0,0 +1,80 @@
+import React from 'react';
+
+export const TemplatePreview = ({ templateType }) => {
+ // Template information for different types
+ const templateInfo = {
+ developer: {
+ name: "Developer Portfolio",
+ description: "A modern, responsive portfolio site with sections for projects, skills, and contact information. Perfect for developers to showcase their work.",
+ features: ["Project showcase", "Skills section", "GitHub integration", "Contact form", "Blog ready"],
+ image: "https://images.unsplash.com/photo-1498050108023-c5249f4df085?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2072&q=80"
+ },
+ designer: {
+ name: "Designer Portfolio",
+ description: "A visually stunning portfolio for designers with image galleries, case studies, and animations to showcase creative work.",
+ features: ["Visual gallery", "Case studies", "Color scheme customization", "Smooth animations", "Design process showcase"],
+ image: "https://images.unsplash.com/photo-1561070791-2526d30994b5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2000&q=80"
+ },
+ photographer: {
+ name: "Photographer Portfolio",
+ description: "An elegant portfolio with fullscreen galleries, image zooming, and lightbox features designed for photographers to display their work.",
+ features: ["Fullscreen galleries", "Image zoom", "Lightbox", "Category filtering", "Client proofing"],
+ image: "https://images.unsplash.com/photo-1542038784456-1ea8e935640e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80"
+ },
+ documentation: {
+ name: "Documentation Site",
+ description: "A comprehensive documentation site with search, code snippets, and versioning support for technical documentation.",
+ features: ["Search functionality", "Code snippets with syntax highlighting", "Versioning", "Sidebar navigation", "Mobile-friendly"],
+ image: "https://images.unsplash.com/photo-1456406644174-8ddd4cd52a06?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2068&q=80"
+ },
+ business: {
+ name: "Single Page Business Site",
+ description: "A professional one-page website for businesses with sections for services, testimonials, team members, and contact information.",
+ features: ["Single page layout", "Services section", "Testimonials", "Team profiles", "Contact form with map"],
+ image: "https://images.unsplash.com/photo-1560179707-f14e90ef3623?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2073&q=80"
+ }
+ };
+
+ const template = templateInfo[templateType] || templateInfo.developer;
+
+ return (
+
+ {/* Template preview image */}
+
+

+
+
+
+ {/* Template details */}
+
+
{template.description}
+
+
+
Features:
+
+ {template.features.map((feature, index) => (
+ -
+ â
+ {feature}
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/shared/Toast.jsx b/src/components/shared/Toast.jsx
new file mode 100644
index 0000000..9a5a008
--- /dev/null
+++ b/src/components/shared/Toast.jsx
@@ -0,0 +1,20 @@
+import React from 'react';
+
+export const Toast = ({ visible, message, type = 'success' }) => {
+ // Color scheme based on type
+ const bgColor = type === 'error' ? 'bg-red-900' : 'bg-green-900';
+ const icon = type === 'error' ? 'â' : 'â';
+
+ return (
+
+
+ {icon}
+ {message}
+
+
+ );
+};
diff --git a/src/hooks/useDeploymentConfig.js b/src/hooks/useDeploymentConfig.js
new file mode 100644
index 0000000..cf20959
--- /dev/null
+++ b/src/hooks/useDeploymentConfig.js
@@ -0,0 +1,103 @@
+import { useState, useCallback } from 'react';
+
+/**
+ * Custom hook for managing deployment configuration state
+ * @param {Object} initialConfig - Initial deployment configuration
+ * @returns {Object} - Deployment configuration state and updater functions
+ */
+const useDeploymentConfig = (initialConfig = {}) => {
+ const [config, setConfig] = useState({
+ type: 'app',
+ appType: 'wordpress',
+ sampleWebAppType: 'developer',
+ sourceType: 'public',
+ repoUrl: '',
+ deploymentKey: '',
+ fileName: '',
+ ...initialConfig
+ });
+
+ // Extract values for easier access
+ const {
+ type,
+ appType,
+ sampleWebAppType,
+ sourceType,
+ repoUrl,
+ deploymentKey,
+ fileName
+ } = config;
+
+ // Update a single field
+ const updateField = useCallback((field, value) => {
+ setConfig(prev => ({
+ ...prev,
+ [field]: value
+ }));
+ }, []);
+
+ // Handler for deployment type change
+ const handleDeploymentTypeChange = useCallback((e) => {
+ updateField('type', e.target.value);
+ }, [updateField]);
+
+ // Handler for app type change
+ const handleAppTypeChange = useCallback((e) => {
+ updateField('appType', e.target.value);
+ }, [updateField]);
+
+ // Handler for sample web app type change
+ const handleSampleWebAppTypeChange = useCallback((e) => {
+ updateField('sampleWebAppType', e.target.value);
+ }, [updateField]);
+
+ // Handler for source type change
+ const handleSourceTypeChange = useCallback((e) => {
+ updateField('sourceType', e.target.value);
+ }, [updateField]);
+
+ // Handler for repo URL change
+ const handleRepoUrlChange = useCallback((e) => {
+ updateField('repoUrl', e.target.value);
+ }, [updateField]);
+
+ // Handler for deployment key change
+ const handleDeploymentKeyChange = useCallback((e) => {
+ updateField('deploymentKey', e.target.value);
+ }, [updateField]);
+
+ // Handler for file change
+ const handleFileChange = useCallback((e) => {
+ if (e.target.files.length > 0) {
+ updateField('fileName', e.target.files[0].name);
+ } else {
+ updateField('fileName', '');
+ }
+ }, [updateField]);
+
+ return {
+ // State values
+ config,
+ type,
+ appType,
+ sampleWebAppType,
+ sourceType,
+ repoUrl,
+ deploymentKey,
+ fileName,
+
+ // Update functions
+ updateField,
+
+ // Event handlers
+ handleDeploymentTypeChange,
+ handleAppTypeChange,
+ handleSampleWebAppTypeChange,
+ handleSourceTypeChange,
+ handleRepoUrlChange,
+ handleDeploymentKeyChange,
+ handleFileChange
+ };
+};
+
+export default useDeploymentConfig;
\ No newline at end of file
diff --git a/src/hooks/useDnsVerification.js b/src/hooks/useDnsVerification.js
new file mode 100644
index 0000000..8be0911
--- /dev/null
+++ b/src/hooks/useDnsVerification.js
@@ -0,0 +1,62 @@
+import { useState, useCallback } from 'react';
+
+/**
+ * Custom hook for managing DNS verification
+ * @param {Function} showToast - Function to display toast notifications
+ * @returns {Object} - DNS verification state and methods
+ */
+const useDnsVerification = (showToast) => {
+ // DNS verification state
+ const [dnsVerified, setDnsVerified] = useState({
+ cname: false,
+ ns: false,
+ a: false
+ });
+
+ // Check DNS configuration
+ const checkDnsConfig = useCallback((type) => {
+ showToast(`Checking ${type}... (This would verify DNS in a real app)`);
+
+ // Set type to 'checking' state
+ setDnsVerified(prev => ({
+ ...prev,
+ [type]: 'checking'
+ }));
+
+ // Simulate DNS check with a delay
+ setTimeout(() => {
+ setDnsVerified(prev => ({
+ ...prev,
+ [type]: true
+ }));
+
+ showToast(`${type} verified successfully!`);
+ }, 1500);
+ }, [showToast]);
+
+ // Reset DNS verification for a specific type
+ const resetDnsVerification = useCallback((type) => {
+ setDnsVerified(prev => ({
+ ...prev,
+ [type]: false
+ }));
+ }, []);
+
+ // Reset all DNS verification
+ const resetAllDnsVerification = useCallback(() => {
+ setDnsVerified({
+ cname: false,
+ ns: false,
+ a: false
+ });
+ }, []);
+
+ return {
+ dnsVerified,
+ checkDnsConfig,
+ resetDnsVerification,
+ resetAllDnsVerification
+ };
+};
+
+export default useDnsVerification;
\ No newline at end of file
diff --git a/src/hooks/useDomainConfig.js b/src/hooks/useDomainConfig.js
new file mode 100644
index 0000000..e3d458c
--- /dev/null
+++ b/src/hooks/useDomainConfig.js
@@ -0,0 +1,267 @@
+import { useState, useCallback, useEffect } from 'react';
+import { cleanDomainInput, isValidDomainFormat, validateDomainAvailability } from '../utils/domainUtils';
+
+/**
+ * Custom hook for managing domain configuration
+ * @param {Object} initialConfig - Initial domain configuration
+ * @param {Function} resetAllDnsVerification - Function to reset all DNS verification
+ * @returns {Object} - Domain configuration state and methods
+ */
+const useDomainConfig = (initialConfig = {}, resetAllDnsVerification) => {
+ // Domain configuration state
+ const [config, setConfig] = useState({
+ useSubdomain: true,
+ useCustomDomain: false,
+ customDomain: '',
+ customSubdomain: '',
+ domainType: 'domain',
+ dnsMethod: 'cname',
+ ...initialConfig
+ });
+
+ // Domain validation state
+ const [validation, setValidation] = useState({
+ isValidating: false,
+ isValidDomain: false,
+ validationMessage: '',
+ showDnsConfig: false
+ });
+
+ // Extract values for easier access
+ const {
+ useSubdomain,
+ useCustomDomain,
+ customDomain,
+ customSubdomain,
+ domainType,
+ dnsMethod
+ } = config;
+
+ const {
+ isValidating,
+ isValidDomain,
+ validationMessage,
+ showDnsConfig
+ } = validation;
+
+ // Update a single field in domain config
+ const updateField = useCallback((field, value) => {
+ setConfig(prev => ({
+ ...prev,
+ [field]: value
+ }));
+ }, []);
+
+ // Update validation state
+ const updateValidation = useCallback((updates) => {
+ setValidation(prev => ({
+ ...prev,
+ ...updates
+ }));
+ }, []);
+
+ // Handler for use subdomain checkbox
+ const handleUseSubdomainChange = useCallback((e) => {
+ const newValue = e.target.checked;
+
+ updateField('useSubdomain', newValue);
+
+ // CRITICAL RULE: If CNAME record is selected, SiliconPin subdomain must be enabled
+ // If user tries to uncheck SiliconPin subdomain while using CNAME, disable custom domain
+ if (useCustomDomain && dnsMethod === 'cname' && !newValue) {
+ updateField('useCustomDomain', false);
+
+ // Since we're disabling custom domain, reset validation
+ updateValidation({
+ showDnsConfig: false,
+ isValidDomain: false,
+ validationMessage: '',
+ isValidating: false
+ });
+
+ // Also reset DNS verification
+ if (resetAllDnsVerification) {
+ resetAllDnsVerification();
+ }
+ }
+ }, [useCustomDomain, dnsMethod, updateField, updateValidation, resetAllDnsVerification]);
+
+ // Handler for use custom domain checkbox
+ const handleUseCustomDomainChange = useCallback((e) => {
+ const newValue = e.target.checked;
+
+ updateField('useCustomDomain', newValue);
+
+ if (!newValue) {
+ // Reset validation when disabling custom domain
+ updateValidation({
+ showDnsConfig: false,
+ isValidDomain: false,
+ validationMessage: '',
+ isValidating: false
+ });
+
+ // Reset DNS verification
+ if (resetAllDnsVerification) {
+ resetAllDnsVerification();
+ }
+ } else {
+ // CRITICAL RULE: Force SiliconPin subdomain to be checked if custom domain is checked
+ updateField('useSubdomain', true);
+ }
+ }, [updateField, updateValidation, resetAllDnsVerification]);
+
+ // Handler for domain type change
+ const handleDomainTypeChange = useCallback((e) => {
+ updateField('domainType', e.target.value);
+
+ // Reset validation when changing domain type
+ updateValidation({
+ isValidDomain: false,
+ validationMessage: '',
+ showDnsConfig: false
+ });
+
+ // Reset DNS verification
+ if (resetAllDnsVerification) {
+ resetAllDnsVerification();
+ }
+ }, [updateField, updateValidation, resetAllDnsVerification]);
+
+ // Handler for DNS method change
+ const handleDnsMethodChange = useCallback((e) => {
+ const newValue = e.target.value;
+
+ updateField('dnsMethod', newValue);
+
+ // CRITICAL RULE: If changing to CNAME, ensure SiliconPin subdomain is enabled
+ if (newValue === 'cname') {
+ updateField('useSubdomain', true);
+ }
+ }, [updateField]);
+
+ // Handler for domain input change
+ const handleDomainChange = useCallback((e) => {
+ const cleanedValue = cleanDomainInput(e.target.value);
+ updateField('customDomain', cleanedValue);
+
+ // Reset domain validation when input changes
+ if (isValidDomain) {
+ updateValidation({
+ isValidDomain: false,
+ validationMessage: '',
+ showDnsConfig: false
+ });
+ }
+ }, [updateField, updateValidation, isValidDomain]);
+
+ // Handler for subdomain input change
+ const handleSubdomainChange = useCallback((e) => {
+ const cleanedValue = cleanDomainInput(e.target.value);
+ updateField('customSubdomain', cleanedValue);
+
+ // Reset domain validation when input changes
+ if (isValidDomain) {
+ updateValidation({
+ isValidDomain: false,
+ validationMessage: '',
+ showDnsConfig: false
+ });
+ }
+ }, [updateField, updateValidation, isValidDomain]);
+
+ // Validate domain
+ const validateDomain = useCallback(async () => {
+ const domain = domainType === 'domain' ? customDomain : customSubdomain;
+
+ if (!domain) {
+ updateValidation({
+ validationMessage: 'Please enter a domain name.',
+ isValidDomain: false,
+ showDnsConfig: false
+ });
+ return;
+ }
+
+ // Initial validation: check format with regex
+ if (!isValidDomainFormat(domain)) {
+ updateValidation({
+ validationMessage: 'Domain format is invalid. Please check your entry.',
+ isValidDomain: false,
+ showDnsConfig: false
+ });
+ return;
+ }
+
+ // Show validation in progress
+ updateValidation({
+ isValidating: true,
+ validationMessage: '',
+ showDnsConfig: false
+ });
+
+ // Validate with API
+ const result = await validateDomainAvailability(domain, domainType);
+
+ updateValidation({
+ isValidating: false,
+ isValidDomain: result.isValid,
+ validationMessage: result.message,
+ showDnsConfig: result.isValid
+ });
+
+ // Reset DNS verification when domain is validated
+ if (result.isValid && resetAllDnsVerification) {
+ resetAllDnsVerification();
+ }
+ }, [
+ domainType,
+ customDomain,
+ customSubdomain,
+ updateValidation,
+ resetAllDnsVerification
+ ]);
+
+ // Reset validation when disabling custom domain
+ useEffect(() => {
+ if (!useCustomDomain) {
+ updateValidation({
+ showDnsConfig: false,
+ isValidDomain: false,
+ validationMessage: '',
+ isValidating: false
+ });
+ }
+ }, [useCustomDomain, updateValidation]);
+
+ return {
+ // State values
+ config,
+ validation,
+ useSubdomain,
+ useCustomDomain,
+ customDomain,
+ customSubdomain,
+ domainType,
+ dnsMethod,
+ isValidating,
+ isValidDomain,
+ validationMessage,
+ showDnsConfig,
+
+ // Update functions
+ updateField,
+ updateValidation,
+
+ // Event handlers
+ handleUseSubdomainChange,
+ handleUseCustomDomainChange,
+ handleDomainTypeChange,
+ handleDnsMethodChange,
+ handleDomainChange,
+ handleSubdomainChange,
+ validateDomain
+ };
+};
+
+export default useDomainConfig;
\ No newline at end of file
diff --git a/src/hooks/useFormValidation.js b/src/hooks/useFormValidation.js
new file mode 100644
index 0000000..b56c39c
--- /dev/null
+++ b/src/hooks/useFormValidation.js
@@ -0,0 +1,57 @@
+import { useState, useCallback, useEffect } from 'react';
+
+/**
+ * Custom hook for form validation based on critical business rules
+ * @param {Object} domainConfig - Domain configuration state
+ * @param {Object} validation - Domain validation state
+ * @param {Object} dnsVerified - DNS verification state
+ * @returns {Object} - Form validation state and methods
+ */
+const useFormValidation = (domainConfig, validation, dnsVerified) => {
+ const [formValid, setFormValid] = useState(true);
+
+ const { useCustomDomain, dnsMethod } = domainConfig;
+ const { isValidDomain } = validation;
+
+ // Validate form
+ const validateForm = useCallback(() => {
+ let valid = true;
+
+ // For custom domain, require domain validation and DNS verification
+ if (useCustomDomain) {
+ // First requirement: domain must be validated successfully
+ if (!isValidDomain) {
+ valid = false;
+ }
+ // Second requirement: appropriate DNS verification must pass
+ else if (dnsMethod === 'cname' && dnsVerified.cname !== true) {
+ valid = false;
+ }
+ else if (dnsMethod === 'ns' && dnsVerified.ns !== true) {
+ valid = false;
+ }
+ }
+
+ setFormValid(valid);
+ return valid;
+ }, [useCustomDomain, isValidDomain, dnsMethod, dnsVerified.cname, dnsVerified.ns]);
+
+ // Validate form when relevant state changes
+ useEffect(() => {
+ validateForm();
+ }, [
+ useCustomDomain,
+ isValidDomain,
+ dnsMethod,
+ dnsVerified.cname,
+ dnsVerified.ns,
+ validateForm
+ ]);
+
+ return {
+ formValid,
+ validateForm
+ };
+};
+
+export default useFormValidation;
\ No newline at end of file
diff --git a/src/hooks/useToast.js b/src/hooks/useToast.js
new file mode 100644
index 0000000..b732f65
--- /dev/null
+++ b/src/hooks/useToast.js
@@ -0,0 +1,36 @@
+import { useState, useCallback } from 'react';
+
+/**
+ * Custom hook for managing toast notifications
+ * @param {number} duration - Duration in ms to show the toast (default: 3000)
+ * @returns {Object} - Toast state and methods
+ */
+const useToast = (duration = 3000) => {
+ const [toast, setToast] = useState({
+ visible: false,
+ message: ''
+ });
+
+ // Show a toast notification
+ const showToast = useCallback((message) => {
+ setToast({ visible: true, message });
+
+ // Auto-hide the toast after the specified duration
+ setTimeout(() => {
+ setToast({ visible: false, message: '' });
+ }, duration);
+ }, [duration]);
+
+ // Hide the toast notification
+ const hideToast = useCallback(() => {
+ setToast({ visible: false, message: '' });
+ }, []);
+
+ return {
+ toast,
+ showToast,
+ hideToast
+ };
+};
+
+export default useToast;
\ No newline at end of file
diff --git a/src/pages/get-started.astro b/src/pages/get-started.astro
index 28c58b7..6496176 100644
--- a/src/pages/get-started.astro
+++ b/src/pages/get-started.astro
@@ -1,6 +1,6 @@
---
import Layout from '../layouts/Layout.astro';
-import { DomainSetupForm } from '../components/DomainSetupForm';
+import { DomainSetupForm } from '../components/DomainSetupForm/index';
// Page-specific SEO metadata
const pageTitle = "Get Started | SiliconPin";
diff --git a/src/utils/domainUtils.js b/src/utils/domainUtils.js
new file mode 100644
index 0000000..846a694
--- /dev/null
+++ b/src/utils/domainUtils.js
@@ -0,0 +1,72 @@
+/**
+ * Clean domain input by removing http://, https://, www., and trailing slashes
+ * @param {string} input - The domain input string to clean
+ * @returns {string} - The cleaned domain string
+ */
+export const cleanDomainInput = (input) => {
+ return input
+ .replace(/^(https?:\/\/)?(www\.)?/i, '')
+ .replace(/\/+$/, '')
+ .trim();
+ };
+
+ /**
+ * Validate domain format using regex
+ * @param {string} domain - The domain to validate
+ * @returns {boolean} - Whether the domain format is valid
+ */
+ export const isValidDomainFormat = (domain) => {
+ if (!domain) return false;
+ return /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(domain);
+ };
+
+ /**
+ * Validate domain availability with API
+ * @param {string} domain - The domain to validate
+ * @param {string} type - The domain type ('domain' or 'subdomain')
+ * @returns {Promise} - Promise resolving to validation result
+ */
+ export const validateDomainAvailability = async (domain, type) => {
+ try {
+ const response = await fetch('http://localhost:2058/host-api/v1/validate-domain/', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ domain, type })
+ });
+
+ if (!response.ok) {
+ console.error(`Network error: ${response.status} ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ return {
+ isValid: data.status === "success",
+ message: data.status === "success"
+ ? 'Domain is valid and registered.'
+ : 'Domain appears to be unregistered or unavailable.'
+ };
+ } catch (error) {
+ console.error('Error validating domain:', error);
+ return {
+ isValid: false,
+ message: `Error checking domain: ${error.message}`
+ };
+ }
+ };
+
+ /**
+ * Copy text to clipboard
+ * @param {string} text - Text to copy
+ * @returns {Promise} - Promise resolving to success or error
+ */
+ export const copyToClipboard = async (text) => {
+ try {
+ await navigator.clipboard.writeText(text);
+ return { success: true };
+ } catch (err) {
+ console.error('Failed to copy:', err);
+ return { success: false, error: err.message };
+ }
+ };
\ No newline at end of file