2 Commits

Author SHA1 Message Date
Suvodip
dd8b979915 about page text justifyed 2025-03-21 19:20:52 +05:30
Suvodip
6907bf2852 s11 2025-03-21 18:08:06 +05:30
22 changed files with 130 additions and 1681 deletions

View File

@@ -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>
); );

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 };
}
};