Compare commits
2 Commits
get-starte
...
footerImpr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd8b979915 | ||
|
|
6907bf2852 |
@@ -30,7 +30,8 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
const [dnsVerified, setDnsVerified] = useState({
|
const [dnsVerified, setDnsVerified] = useState({
|
||||||
cname: false,
|
cname: false,
|
||||||
ns: false,
|
ns: false,
|
||||||
a: false
|
a: false,
|
||||||
|
ip: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Form validation
|
// Form validation
|
||||||
@@ -61,7 +62,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateForm();
|
validateForm();
|
||||||
}, [useCustomDomain, dnsVerified.cname, dnsVerified.ns, domainType, dnsMethod]);
|
}, [useCustomDomain, dnsVerified.cname, dnsVerified.ns, dnsVerified.ns, domainType, dnsMethod]);
|
||||||
|
|
||||||
// Show toast notification
|
// Show toast notification
|
||||||
const showToast = (message) => {
|
const showToast = (message) => {
|
||||||
@@ -110,7 +111,8 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
setDnsVerified({
|
setDnsVerified({
|
||||||
cname: false,
|
cname: false,
|
||||||
ns: false,
|
ns: false,
|
||||||
a: false
|
a: false,
|
||||||
|
ip: false
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Force SiliconPin subdomain to be checked if custom domain is checked
|
// Force SiliconPin subdomain to be checked if custom domain is checked
|
||||||
@@ -187,45 +189,23 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
setValidationMessage('');
|
setValidationMessage('');
|
||||||
setShowDnsConfig(false);
|
setShowDnsConfig(false);
|
||||||
|
|
||||||
fetch('/validate-domain', {
|
// Simulate an API call to validate the domain
|
||||||
method: 'POST',
|
setTimeout(() => {
|
||||||
headers: {
|
// Simulate a real domain check - in a real app this would be an API call
|
||||||
'Content-Type': 'application/json',
|
const checkResult = true; // Assume domain is valid for demo
|
||||||
},
|
|
||||||
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);
|
|
||||||
|
|
||||||
setIsValidating(false);
|
setIsValidating(false);
|
||||||
setIsValidDomain(checkResult);
|
setIsValidDomain(checkResult);
|
||||||
|
|
||||||
if (checkResult) {
|
if (checkResult) {
|
||||||
setValidationMessage('Domain is valid and registered.');
|
setValidationMessage('Domain is valid and registered.');
|
||||||
setShowDnsConfig(true);
|
setShowDnsConfig(true);
|
||||||
} else {
|
} else {
|
||||||
setValidationMessage('Domain appears to be unregistered or unavailable.');
|
setValidationMessage('Domain appears to be unregistered or unavailable.');
|
||||||
}
|
}
|
||||||
|
|
||||||
validateForm();
|
validateForm();
|
||||||
})
|
}, 1500);
|
||||||
.catch(error => {
|
|
||||||
console.error('Error validating domain:', error);
|
|
||||||
setIsValidating(false);
|
|
||||||
setIsValidDomain(false);
|
|
||||||
setValidationMessage('Error checking domain. Please try again.');
|
|
||||||
validateForm();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check DNS configuration
|
// Check DNS configuration
|
||||||
@@ -268,6 +248,10 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
setFormValid(false);
|
setFormValid(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (dnsMethod === 'ip' && !dnsVerified.ip) {
|
||||||
|
setFormValid(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormValid(true);
|
setFormValid(true);
|
||||||
@@ -283,20 +267,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In a real app, this would submit the form data to the server
|
// In a real app, this would submit the form data to the server
|
||||||
console.log({
|
console.log([{ deploymentType, appType, sampleWebAppType, sourceType, repoUrl, deploymentKey, useSubdomain, useCustomDomain, customDomain, customSubdomain, domainType, dnsMethod}]);
|
||||||
deploymentType,
|
|
||||||
appType,
|
|
||||||
sampleWebAppType,
|
|
||||||
sourceType,
|
|
||||||
repoUrl,
|
|
||||||
deploymentKey,
|
|
||||||
useSubdomain,
|
|
||||||
useCustomDomain,
|
|
||||||
customDomain,
|
|
||||||
customSubdomain,
|
|
||||||
domainType,
|
|
||||||
dnsMethod
|
|
||||||
});
|
|
||||||
|
|
||||||
showToast('Form submitted successfully!');
|
showToast('Form submitted successfully!');
|
||||||
};
|
};
|
||||||
@@ -572,9 +543,10 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
|
|
||||||
{/* Domain Validation */}
|
{/* Domain Validation */}
|
||||||
<button
|
<button
|
||||||
|
disabled={!customDomain}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={validateDomain}
|
onClick={validateDomain}
|
||||||
className="px-4 py-2 bg-neutral-600 text-white font-medium rounded-md hover:bg-neutral-500 transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-500"
|
className={`px-4 py-2 ${!customDomain ? 'bg-neutral-600 cursor-not-allowed' : 'bg-[#6d9e37] focus:ring-[#6d9e37] transition-colors'} text-white font-medium rounded-md transition-colors focus:outline-none`}
|
||||||
>
|
>
|
||||||
Validate Domain
|
Validate Domain
|
||||||
</button>
|
</button>
|
||||||
@@ -604,7 +576,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
|
|
||||||
{/* CNAME Record Option */}
|
{/* CNAME Record Option */}
|
||||||
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
||||||
<div className="flex items-start">
|
<label for="dns-cname" className="flex items-start cursor-pointer">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
id="dns-cname"
|
id="dns-cname"
|
||||||
@@ -644,67 +616,114 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Nameserver Option (only for full domains, not subdomains) */}
|
{/* Nameserver Option (only for full domains, not subdomains) */}
|
||||||
{domainType === 'domain' && (
|
{domainType === 'domain' && (
|
||||||
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
<>
|
||||||
<div className="flex items-start">
|
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
||||||
<input
|
<label for="dns-ns" className="flex items-start cursor-pointer">
|
||||||
type="radio"
|
<input
|
||||||
id="dns-ns"
|
type="radio"
|
||||||
name="dns-method"
|
id="dns-ns"
|
||||||
value="ns"
|
name="dns-method"
|
||||||
checked={dnsMethod === 'ns'}
|
value="ns"
|
||||||
onChange={handleDnsMethodChange}
|
checked={dnsMethod === 'ns'}
|
||||||
className="mt-1 mr-2"
|
onChange={handleDnsMethodChange}
|
||||||
/>
|
className="mt-1 mr-2"
|
||||||
<div className="flex-1">
|
/>
|
||||||
<label htmlFor="dns-ns" className="block text-white font-medium">Use Our Nameservers</label>
|
<div className="flex-1">
|
||||||
<p className="text-sm text-neutral-300">Update your domain's nameservers to use ours</p>
|
<label htmlFor="dns-ns" className="block text-white font-medium">Use Our Nameservers</label>
|
||||||
|
<p className="text-sm text-neutral-300">Update your domain's nameservers to use ours</p>
|
||||||
|
|
||||||
<div className="mt-3 space-y-2">
|
<div className="mt-3 space-y-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns1.siliconpin.com</div>
|
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns1.siliconpin.com</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard('ns1.siliconpin.com')}
|
onClick={() => copyToClipboard('ns1.siliconpin.com')}
|
||||||
className="ml-2 text-[#6d9e37] hover:text-white"
|
className="ml-2 text-[#6d9e37] hover:text-white"
|
||||||
aria-label="Copy nameserver value"
|
aria-label="Copy nameserver value"
|
||||||
>
|
>
|
||||||
<span className="text-lg">📋</span>
|
<span className="text-lg">📋</span>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns2.siliconpin.com</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => copyToClipboard('ns2.siliconpin.com')}
|
||||||
|
className="ml-2 text-[#6d9e37] hover:text-white"
|
||||||
|
aria-label="Copy nameserver value"
|
||||||
|
>
|
||||||
|
<span className="text-lg">📋</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="mt-2 text-right">
|
||||||
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns2.siliconpin.com</div>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard('ns2.siliconpin.com')}
|
onClick={() => checkDnsConfig('ns')}
|
||||||
className="ml-2 text-[#6d9e37] hover:text-white"
|
className={`px-3 py-1 text-white text-sm rounded
|
||||||
aria-label="Copy nameserver value"
|
${dnsVerified.ns
|
||||||
|
? 'bg-green-700 hover:bg-green-600'
|
||||||
|
: 'bg-neutral-600 hover:bg-neutral-500'}`}
|
||||||
>
|
>
|
||||||
<span className="text-lg">📋</span>
|
{dnsVerified.ns ? '✓ Nameservers Verified' : 'Check Nameservers'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</label>
|
||||||
<div className="mt-2 text-right">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => checkDnsConfig('ns')}
|
|
||||||
className={`px-3 py-1 text-white text-sm rounded
|
|
||||||
${dnsVerified.ns
|
|
||||||
? 'bg-green-700 hover:bg-green-600'
|
|
||||||
: 'bg-neutral-600 hover:bg-neutral-500'}`}
|
|
||||||
>
|
|
||||||
{dnsVerified.ns ? '✓ Nameservers Verified' : 'Check Nameservers'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
||||||
|
<label for="dns-ip" className="flex items-start cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="dns-ip"
|
||||||
|
name="dns-method"
|
||||||
|
value="ip"
|
||||||
|
checked={dnsMethod === 'ip'}
|
||||||
|
onChange={handleDnsMethodChange}
|
||||||
|
className="mt-1 mr-2"
|
||||||
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<label htmlFor="dns-ip" className="block text-white font-medium">Use Our IP Address</label>
|
||||||
|
<p className="text-sm text-neutral-300">Update your domain's nameservers to use ours</p>
|
||||||
|
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">xxx.xxx.x.xx</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => copyToClipboard('xxx.xxx.x.xx')}
|
||||||
|
className="ml-2 text-[#6d9e37] hover:text-white"
|
||||||
|
aria-label="Copy nameserver value"
|
||||||
|
>
|
||||||
|
<span className="text-lg">📋</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-right">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => checkDnsConfig('ip')}
|
||||||
|
className={`px-3 py-1 text-white text-sm rounded
|
||||||
|
${dnsMethod === 'ip'
|
||||||
|
? 'bg-green-700 hover:bg-green-600'
|
||||||
|
: 'bg-neutral-600 hover:bg-neutral-500'}`}
|
||||||
|
>
|
||||||
|
{/* {dnsVerified.ip ? '✓ IP Address Verified' : 'Check IP Address'} */}
|
||||||
|
Proceed to Pay
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -714,7 +733,6 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Form Submit Button */}
|
{/* Form Submit Button */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -728,7 +746,6 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
|
|||||||
Start the Deployment
|
Start the Deployment
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<Toast visible={toast.visible} message={toast.message} />
|
<Toast visible={toast.visible} message={toast.message} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="app-type" className="block text-white font-medium">Select Application</label>
|
|
||||||
<select
|
|
||||||
id="app-type"
|
|
||||||
value={appType}
|
|
||||||
onChange={onAppTypeChange}
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<option value="wordpress">🔌 WordPress</option>
|
|
||||||
<option value="prestashop">🛒 PrestaShop</option>
|
|
||||||
<option value="laravel">🚀 Laravel</option>
|
|
||||||
<option value="cakephp">🍰 CakePHP</option>
|
|
||||||
<option value="symfony">🎯 Symfony</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppDeployment;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="source-type" className="block text-white font-medium">Source Type</label>
|
|
||||||
<select
|
|
||||||
id="source-type"
|
|
||||||
value={sourceType}
|
|
||||||
onChange={onSourceTypeChange}
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<option value="public">🌐 Public Repository</option>
|
|
||||||
<option value="private">🔒 Private Repository</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pt-2">
|
|
||||||
<label htmlFor="repo-url" className="block text-white font-medium mb-2">Repository URL</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="repo-url"
|
|
||||||
value={repoUrl}
|
|
||||||
onChange={onRepoUrlChange}
|
|
||||||
placeholder="https://github.com/username/repository"
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{sourceType === 'private' && (
|
|
||||||
<div className="pt-2">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<label htmlFor="deployment-key" className="block text-white font-medium">Deployment Key</label>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => showToast('Deployment keys are used for secure access to private repositories')}
|
|
||||||
className="text-neutral-400 hover:text-white focus:outline-none"
|
|
||||||
aria-label="Deployment Key Information"
|
|
||||||
>
|
|
||||||
<span className="text-lg">❓</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea
|
|
||||||
id="deployment-key"
|
|
||||||
value={deploymentKey}
|
|
||||||
onChange={onDeploymentKeyChange}
|
|
||||||
placeholder="Paste your SSH private key here"
|
|
||||||
rows="6"
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37] font-mono text-sm"
|
|
||||||
/>
|
|
||||||
<p className="mt-1 text-sm text-neutral-400">Your private key is used only for deploying and is never stored on our servers.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SourceDeployment;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="p-4 bg-neutral-700/50 rounded-md text-neutral-300 text-center">
|
|
||||||
Upload the zip file containing your static website
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pt-2">
|
|
||||||
<label htmlFor="file-upload" className="block text-white font-medium mb-2">Upload File (ZIP/TAR)</label>
|
|
||||||
<div className="flex items-center justify-center w-full">
|
|
||||||
<label
|
|
||||||
htmlFor="file-upload"
|
|
||||||
className="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed rounded-lg cursor-pointer border-neutral-600 hover:border-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
|
||||||
<span className="text-3xl mb-3 text-neutral-400">📁</span>
|
|
||||||
<p className="mb-2 text-sm text-neutral-400">
|
|
||||||
<span className="font-semibold">Click to upload</span> or drag and drop
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-neutral-500">ZIP or TAR files only (max. 100MB)</p>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
id="file-upload"
|
|
||||||
ref={fileInputRef}
|
|
||||||
type="file"
|
|
||||||
onChange={onFileChange}
|
|
||||||
className="hidden"
|
|
||||||
accept=".zip,.tar"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{fileName && (
|
|
||||||
<div className="mt-2 text-sm text-neutral-400">
|
|
||||||
Selected file: {fileName}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StaticDeployment;
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="sample-web-app-type" className="block text-white font-medium">Select Template Type</label>
|
|
||||||
<select
|
|
||||||
id="sample-web-app-type"
|
|
||||||
value={templateType}
|
|
||||||
onChange={onTemplateTypeChange}
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<option value="developer">👨💻 Developer Portfolio</option>
|
|
||||||
<option value="designer">🎨 Designer Portfolio</option>
|
|
||||||
<option value="photographer">📸 Photographer Portfolio</option>
|
|
||||||
<option value="documentation">📚 Documentation Site</option>
|
|
||||||
<option value="business">🏢 Single Page Business Site</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
{/* Information box */}
|
|
||||||
<div className="mt-4 p-4 bg-neutral-700/30 rounded-md border border-neutral-600">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<div className="mr-3 text-2xl">ℹ️</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<p className="text-sm text-neutral-300">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Template Preview */}
|
|
||||||
<TemplatePreview templateType={templateType} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TemplateDeployment;
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Deployment Type Selection */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="deployment-type" className="block text-white font-medium">Deployment Type</label>
|
|
||||||
<select
|
|
||||||
id="deployment-type"
|
|
||||||
value={type}
|
|
||||||
onChange={handleDeploymentTypeChange}
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<option value="app">💻 Deploy an App</option>
|
|
||||||
<option value="source">⚙️ From Source</option>
|
|
||||||
<option value="static">📄 Static Site Upload</option>
|
|
||||||
<option value="sample-web-app">🌐 Sample Web App</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Render different components based on deployment type */}
|
|
||||||
{type === 'app' && (
|
|
||||||
<AppDeployment
|
|
||||||
appType={appType}
|
|
||||||
onAppTypeChange={handleAppTypeChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{type === 'sample-web-app' && (
|
|
||||||
<TemplateDeployment
|
|
||||||
templateType={sampleWebAppType}
|
|
||||||
onTemplateTypeChange={handleSampleWebAppTypeChange}
|
|
||||||
TemplatePreview={TemplatePreview}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{type === 'source' && (
|
|
||||||
<SourceDeployment
|
|
||||||
sourceType={sourceType}
|
|
||||||
repoUrl={repoUrl}
|
|
||||||
deploymentKey={deploymentKey}
|
|
||||||
onSourceTypeChange={handleSourceTypeChange}
|
|
||||||
onRepoUrlChange={handleRepoUrlChange}
|
|
||||||
onDeploymentKeyChange={handleDeploymentKeyChange}
|
|
||||||
showToast={showToast}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{type === 'static' && (
|
|
||||||
<StaticDeployment
|
|
||||||
fileName={fileName}
|
|
||||||
onFileChange={handleFileChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeploymentOptions;
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="mt-3 space-y-4">
|
|
||||||
{/* Domain Type Selection */}
|
|
||||||
<div className="flex flex-col space-y-3 sm:flex-row sm:space-y-0 sm:space-x-4">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="domain-type-domain"
|
|
||||||
name="domain-type"
|
|
||||||
value="domain"
|
|
||||||
checked={domainType === 'domain'}
|
|
||||||
onChange={handleDomainTypeChange}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
<label htmlFor="domain-type-domain" className="text-white">Root Domain</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="domain-type-subdomain"
|
|
||||||
name="domain-type"
|
|
||||||
value="subdomain"
|
|
||||||
checked={domainType === 'subdomain'}
|
|
||||||
onChange={handleDomainTypeChange}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
<label htmlFor="domain-type-subdomain" className="text-white">Subdomain</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Domain Input */}
|
|
||||||
{domainType === 'domain' ? (
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="custom-domain"
|
|
||||||
value={customDomain}
|
|
||||||
onChange={handleDomainChange}
|
|
||||||
placeholder="yourdomain.com"
|
|
||||||
aria-label="Enter root domain"
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
/>
|
|
||||||
<p className="mt-1 text-xs text-neutral-400">
|
|
||||||
Enter domain without http://, www, or trailing slashes (example.com). You can configure www or other subdomains later.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="custom-subdomain"
|
|
||||||
value={customSubdomain}
|
|
||||||
onChange={handleSubdomainChange}
|
|
||||||
placeholder="blog.yourdomain.com"
|
|
||||||
aria-label="Enter subdomain"
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
/>
|
|
||||||
<p className="mt-1 text-xs text-neutral-400">
|
|
||||||
Enter the full subdomain without http:// or trailing slashes. www and protocol prefixes will be automatically removed.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Domain Validation */}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={validateDomain}
|
|
||||||
disabled={isValidating}
|
|
||||||
className="px-4 py-2 bg-neutral-600 text-white font-medium rounded-md hover:bg-neutral-500 transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
||||||
>
|
|
||||||
{isValidating ? 'Validating...' : 'Validate Domain'}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Validation Status */}
|
|
||||||
{isValidating && (
|
|
||||||
<div className="p-3 bg-neutral-700/50 rounded-md">
|
|
||||||
<div className="flex items-center justify-center space-x-2">
|
|
||||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white"></div>
|
|
||||||
<p className="text-white">Verifying domain registration and availability...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isValidating && validationMessage && (
|
|
||||||
<div className={`p-3 rounded-md ${isValidDomain ? 'bg-green-900/20 border border-green-700/50' : 'bg-red-900/20 border border-red-700/50'}`}>
|
|
||||||
<p className={isValidDomain ? 'text-green-400' : 'text-red-400'}>
|
|
||||||
{validationMessage}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* DNS Configuration Options */}
|
|
||||||
{showDnsConfig && (
|
|
||||||
<DnsConfiguration
|
|
||||||
domainType={domainType}
|
|
||||||
dnsMethod={dnsMethod}
|
|
||||||
defaultSubdomain={defaultSubdomain}
|
|
||||||
dnsVerified={dnsVerified}
|
|
||||||
onDnsMethodChange={handleDnsMethodChange}
|
|
||||||
checkDnsConfig={checkDnsConfig}
|
|
||||||
showToast={showToast}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomDomain;
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="mt-4 space-y-4 p-4 rounded-md bg-neutral-800/50 border border-neutral-700">
|
|
||||||
<h4 className="text-white font-medium">Connect Your Domain</h4>
|
|
||||||
|
|
||||||
{/* CNAME Record Option */}
|
|
||||||
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="dns-cname"
|
|
||||||
name="dns-method"
|
|
||||||
value="cname"
|
|
||||||
checked={dnsMethod === 'cname'}
|
|
||||||
onChange={onDnsMethodChange}
|
|
||||||
className="mt-1 mr-2"
|
|
||||||
/>
|
|
||||||
<div className="flex-1">
|
|
||||||
<label htmlFor="dns-cname" className="block text-white font-medium">Use CNAME Record</label>
|
|
||||||
<p className="text-sm text-neutral-300">Point your domain to our SiliconPin subdomain</p>
|
|
||||||
|
|
||||||
<div className="mt-3 flex items-center">
|
|
||||||
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">
|
|
||||||
{defaultSubdomain}.subdomain.siliconpin.com
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleCopyToClipboard(`${defaultSubdomain}.subdomain.siliconpin.com`)}
|
|
||||||
className="ml-2 text-[#6d9e37] hover:text-white"
|
|
||||||
aria-label="Copy CNAME value"
|
|
||||||
>
|
|
||||||
<span className="text-lg">📋</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 text-right">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => checkDnsConfig('cname')}
|
|
||||||
disabled={dnsVerified.cname === 'checking'}
|
|
||||||
className={`px-3 py-1 text-white text-sm rounded
|
|
||||||
${dnsVerified.cname === true
|
|
||||||
? 'bg-green-700 hover:bg-green-600'
|
|
||||||
: dnsVerified.cname === 'checking'
|
|
||||||
? 'bg-neutral-500 cursor-not-allowed'
|
|
||||||
: 'bg-neutral-600 hover:bg-neutral-500'}`}
|
|
||||||
>
|
|
||||||
{dnsVerified.cname === true
|
|
||||||
? '✓ CNAME Verified'
|
|
||||||
: dnsVerified.cname === 'checking'
|
|
||||||
? 'Checking...'
|
|
||||||
: 'Check CNAME'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Nameserver Option (only for full domains, not subdomains) */}
|
|
||||||
{domainType === 'domain' && (
|
|
||||||
<div className="p-4 bg-neutral-700/30 rounded-md border border-neutral-600 space-y-3">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="dns-ns"
|
|
||||||
name="dns-method"
|
|
||||||
value="ns"
|
|
||||||
checked={dnsMethod === 'ns'}
|
|
||||||
onChange={onDnsMethodChange}
|
|
||||||
className="mt-1 mr-2"
|
|
||||||
/>
|
|
||||||
<div className="flex-1">
|
|
||||||
<label htmlFor="dns-ns" className="block text-white font-medium">Use Our Nameservers</label>
|
|
||||||
<p className="text-sm text-neutral-300">Update your domain's nameservers to use ours</p>
|
|
||||||
|
|
||||||
<div className="mt-3 space-y-2">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns1.siliconpin.com</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleCopyToClipboard('ns1.siliconpin.com')}
|
|
||||||
className="ml-2 text-[#6d9e37] hover:text-white"
|
|
||||||
aria-label="Copy nameserver value"
|
|
||||||
>
|
|
||||||
<span className="text-lg">📋</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="bg-neutral-800 p-2 rounded font-mono text-sm text-neutral-300">ns2.siliconpin.com</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleCopyToClipboard('ns2.siliconpin.com')}
|
|
||||||
className="ml-2 text-[#6d9e37] hover:text-white"
|
|
||||||
aria-label="Copy nameserver value"
|
|
||||||
>
|
|
||||||
<span className="text-lg">📋</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-2 text-right">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => checkDnsConfig('ns')}
|
|
||||||
disabled={dnsVerified.ns === 'checking'}
|
|
||||||
className={`px-3 py-1 text-white text-sm rounded
|
|
||||||
${dnsVerified.ns === true
|
|
||||||
? 'bg-green-700 hover:bg-green-600'
|
|
||||||
: dnsVerified.ns === 'checking'
|
|
||||||
? 'bg-neutral-500 cursor-not-allowed'
|
|
||||||
: 'bg-neutral-600 hover:bg-neutral-500'}`}
|
|
||||||
>
|
|
||||||
{dnsVerified.ns === true
|
|
||||||
? '✓ Nameservers Verified'
|
|
||||||
: dnsVerified.ns === 'checking'
|
|
||||||
? 'Checking...'
|
|
||||||
: 'Check Nameservers'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DnsConfiguration;
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="pt-4 border-t border-neutral-700">
|
|
||||||
<h3 className="text-lg font-medium text-white mb-4">Destination</h3>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* SiliconPin Subdomain */}
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="use-subdomain"
|
|
||||||
checked={useSubdomain}
|
|
||||||
onChange={handleUseSubdomainChange}
|
|
||||||
className="mt-1"
|
|
||||||
/>
|
|
||||||
<div className="flex-1">
|
|
||||||
<label htmlFor="use-subdomain" className="block text-white font-medium">Use SiliconPin Subdomain</label>
|
|
||||||
<div className="mt-2 flex">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="subdomain"
|
|
||||||
value={defaultSubdomain}
|
|
||||||
className="rounded-l-md py-2 px-3 bg-neutral-600 border-y border-l border-neutral-600 text-neutral-300 focus:outline-none w-1/3 font-mono"
|
|
||||||
readOnly
|
|
||||||
aria-label="Default subdomain"
|
|
||||||
/>
|
|
||||||
<span className="rounded-r-md py-2 px-3 bg-neutral-800 border border-neutral-700 text-neutral-400 w-2/3">.subdomain.siliconpin.com</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Custom Domain */}
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="use-custom-domain"
|
|
||||||
checked={useCustomDomain}
|
|
||||||
onChange={handleUseCustomDomainChange}
|
|
||||||
className="mt-1"
|
|
||||||
/>
|
|
||||||
<div className="flex-1">
|
|
||||||
<label htmlFor="use-custom-domain" className="block text-white font-medium">Use Custom Domain</label>
|
|
||||||
|
|
||||||
{useCustomDomain && (
|
|
||||||
<CustomDomain
|
|
||||||
domainConfig={domainConfig}
|
|
||||||
validation={validation}
|
|
||||||
dnsVerified={dnsVerified}
|
|
||||||
handlers={handlers}
|
|
||||||
checkDnsConfig={checkDnsConfig}
|
|
||||||
defaultSubdomain={defaultSubdomain}
|
|
||||||
showToast={showToast}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DomainConfiguration;
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
|
||||||
{/* Deployment Options Section */}
|
|
||||||
<DeploymentOptions
|
|
||||||
deploymentConfig={deploymentConfig}
|
|
||||||
handlers={{
|
|
||||||
handleDeploymentTypeChange: deploymentConfig.handleDeploymentTypeChange,
|
|
||||||
handleAppTypeChange: deploymentConfig.handleAppTypeChange,
|
|
||||||
handleSampleWebAppTypeChange: deploymentConfig.handleSampleWebAppTypeChange,
|
|
||||||
handleSourceTypeChange: deploymentConfig.handleSourceTypeChange,
|
|
||||||
handleRepoUrlChange: deploymentConfig.handleRepoUrlChange,
|
|
||||||
handleDeploymentKeyChange: deploymentConfig.handleDeploymentKeyChange,
|
|
||||||
handleFileChange: deploymentConfig.handleFileChange
|
|
||||||
}}
|
|
||||||
showToast={showToast}
|
|
||||||
TemplatePreview={TemplatePreview}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Domain Configuration Section */}
|
|
||||||
<DomainConfiguration
|
|
||||||
domainConfig={domainConfig.config}
|
|
||||||
validation={domainConfig.validation}
|
|
||||||
dnsVerified={dnsVerified}
|
|
||||||
handlers={{
|
|
||||||
handleUseSubdomainChange: domainConfig.handleUseSubdomainChange,
|
|
||||||
handleUseCustomDomainChange: domainConfig.handleUseCustomDomainChange,
|
|
||||||
handleDomainTypeChange: domainConfig.handleDomainTypeChange,
|
|
||||||
handleDnsMethodChange: domainConfig.handleDnsMethodChange,
|
|
||||||
handleDomainChange: domainConfig.handleDomainChange,
|
|
||||||
handleSubdomainChange: domainConfig.handleSubdomainChange,
|
|
||||||
validateDomain: domainConfig.validateDomain
|
|
||||||
}}
|
|
||||||
checkDnsConfig={checkDnsConfig}
|
|
||||||
defaultSubdomain={defaultSubdomain}
|
|
||||||
showToast={showToast}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Form Submit Button */}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={domainConfig.useCustomDomain && !formValid}
|
|
||||||
className={`w-full mt-6 px-6 py-3 text-white font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-800
|
|
||||||
${domainConfig.useCustomDomain && !formValid
|
|
||||||
? 'bg-neutral-600 cursor-not-allowed'
|
|
||||||
: 'bg-[#6d9e37] hover:bg-[#598035] focus:ring-[#6d9e37] transition-colors'
|
|
||||||
}`}
|
|
||||||
aria-label="Start deployment"
|
|
||||||
>
|
|
||||||
Start the Deployment
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Toast visible={toast.visible} message={toast.message} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DomainSetupForm;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
const SelectWithLabel = ({value, onChange}: any)=>{
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<label htmlFor="deployment-type" className="block text-white font-medium">Deployment Type</label>
|
|
||||||
<select
|
|
||||||
id="deployment-type"
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className="w-full rounded-md py-2 px-3 bg-neutral-700 border border-neutral-600 text-white focus:outline-none focus:ring-2 focus:ring-[#6d9e37]"
|
|
||||||
>
|
|
||||||
<option value="app">💻 Deploy an App</option>
|
|
||||||
<option value="source">⚙️ From Source</option>
|
|
||||||
<option value="static">📄 Static Site Upload</option>
|
|
||||||
<option value="sample-web-app">🌐 Sample Web App</option>
|
|
||||||
</select>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SelectWithLabel;
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div className="mt-6 bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
|
|
||||||
{/* Template preview image */}
|
|
||||||
<div className="relative h-48 overflow-hidden">
|
|
||||||
<img
|
|
||||||
src={template.image}
|
|
||||||
alt={`${template.name} Preview`}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent flex items-end">
|
|
||||||
<div className="p-4">
|
|
||||||
<h3 className="text-xl font-semibold text-white">{template.name}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Template details */}
|
|
||||||
<div className="p-5 space-y-4">
|
|
||||||
<p className="text-neutral-300">{template.description}</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4 className="text-white font-medium mb-2">Features:</h4>
|
|
||||||
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
|
||||||
{template.features.map((feature, index) => (
|
|
||||||
<li key={index} className="text-neutral-400 flex items-center">
|
|
||||||
<span className="mr-2 text-[#6d9e37]">✓</span>
|
|
||||||
{feature}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pt-2">
|
|
||||||
<button className="px-4 py-2 bg-[#6d9e37] text-white rounded-md hover:bg-[#598035] transition-colors w-full">
|
|
||||||
Select This Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div
|
|
||||||
className={`fixed bottom-5 right-5 p-3 ${bgColor} text-white rounded-md shadow-lg transform transition-all duration-300 z-50 ${
|
|
||||||
visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10 pointer-events-none'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-lg">{icon}</span>
|
|
||||||
<span>{message}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -24,17 +24,17 @@ const pageImage = "https://images.unsplash.com/photo-1551731409-43eb3e517a1a?q=8
|
|||||||
<div class="max-w-4xl mx-auto space-y-12">
|
<div class="max-w-4xl mx-auto space-y-12">
|
||||||
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
||||||
<h2 class="text-2xl font-bold text-white mb-4">Our Story</h2>
|
<h2 class="text-2xl font-bold text-white mb-4">Our Story</h2>
|
||||||
<p class="text-neutral-300 mb-4">
|
<p class="text-neutral-300 mb-4 text-justify">
|
||||||
SiliconPin was founded in 2021 with a clear mission: to provide businesses of all sizes with reliable, high-performance hosting solutions that enable growth and innovation. What started as a small team of passionate developers and system administrators has grown into a trusted hosting provider serving clients across various industries.
|
SiliconPin was founded in 2021 with a clear mission: to provide businesses of all sizes with reliable, high-performance hosting solutions that enable growth and innovation. What started as a small team of passionate developers and system administrators has grown into a trusted hosting provider serving clients across various industries.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
Based in Habra, West Bengal, India, our team combines technical expertise with a deep understanding of business needs to deliver hosting solutions that are not just technically sound but also aligned with our clients' business objectives.
|
Based in Habra, West Bengal, India, our team combines technical expertise with a deep understanding of business needs to deliver hosting solutions that are not just technically sound but also aligned with our clients' business objectives.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
||||||
<h2 class="text-2xl font-bold text-white mb-4">Our Mission</h2>
|
<h2 class="text-2xl font-bold text-white mb-4">Our Mission</h2>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
At SiliconPin, our mission is to empower businesses through technology by providing reliable, secure, and scalable hosting solutions. We believe that technology should enable businesses to focus on what they do best, without worrying about infrastructure management or technical complexities.
|
At SiliconPin, our mission is to empower businesses through technology by providing reliable, secure, and scalable hosting solutions. We believe that technology should enable businesses to focus on what they do best, without worrying about infrastructure management or technical complexities.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
@@ -44,25 +44,25 @@ const pageImage = "https://images.unsplash.com/photo-1551731409-43eb3e517a1a?q=8
|
|||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Reliability</h3>
|
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Reliability</h3>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
We understand that downtime means lost business. That's why we prioritize reliability in everything we do, from our infrastructure to our customer service.
|
We understand that downtime means lost business. That's why we prioritize reliability in everything we do, from our infrastructure to our customer service.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Security</h3>
|
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Security</h3>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
In an increasingly digital world, security is paramount. We implement robust security measures to protect our clients' data and applications.
|
In an increasingly digital world, security is paramount. We implement robust security measures to protect our clients' data and applications.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Innovation</h3>
|
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Innovation</h3>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
We continuously explore new technologies and methodologies to improve our services and provide our clients with cutting-edge solutions.
|
We continuously explore new technologies and methodologies to improve our services and provide our clients with cutting-edge solutions.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Customer Focus</h3>
|
<h3 class="text-xl font-medium text-[#6d9e37] mb-2">Customer Focus</h3>
|
||||||
<p class="text-neutral-300">
|
<p class="text-neutral-300 text-justify">
|
||||||
Our clients' success is our success. We work closely with our clients to understand their needs and provide tailored solutions that help them achieve their goals.
|
Our clients' success is our success. We work closely with our clients to understand their needs and provide tailored solutions that help them achieve their goals.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +71,7 @@ const pageImage = "https://images.unsplash.com/photo-1551731409-43eb3e517a1a?q=8
|
|||||||
|
|
||||||
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
<section class="bg-neutral-800 rounded-lg p-6 sm:p-8 border border-neutral-700">
|
||||||
<h2 class="text-2xl font-bold text-white mb-4">Why Choose SiliconPin?</h2>
|
<h2 class="text-2xl font-bold text-white mb-4">Why Choose SiliconPin?</h2>
|
||||||
<ul class="list-disc list-inside space-y-3 text-neutral-300">
|
<ul class="list-disc list-inside space-y-3 text-neutral-300 text-justify">
|
||||||
<li>24/7 expert technical support</li>
|
<li>24/7 expert technical support</li>
|
||||||
<li>99.9% uptime guarantee</li>
|
<li>99.9% uptime guarantee</li>
|
||||||
<li>Scalable infrastructure to grow with your business</li>
|
<li>Scalable infrastructure to grow with your business</li>
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const contactSchema = {
|
|||||||
<span class="text-neutral-300 font-medium">Sunday:</span>
|
<span class="text-neutral-300 font-medium">Sunday:</span>
|
||||||
<span class="text-white">Closed</span>
|
<span class="text-white">Closed</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-3 border-t border-neutral-700">
|
<div class="pt-3 border-t border-neutral-700 flex items-center justify-between">
|
||||||
<span class="text-neutral-300">Technical Support:</span>
|
<span class="text-neutral-300">Technical Support:</span>
|
||||||
<span class="text-[#6d9e37] font-semibold block mt-1">24/7</span>
|
<span class="text-[#6d9e37] font-semibold block mt-1">24/7</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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('/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 };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user