diff --git a/package-lock.json b/package-lock.json
index 296aba7..6ae6710 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"lucide-react": "^0.484.0",
"pocketbase": "^0.25.2",
"postcss": "^8.5.3",
+ "react-qr-code": "^2.0.15",
"react-router-dom": "^7.4.1",
"react-to-print": "^3.0.5",
"tailwind-merge": "^3.0.2",
@@ -4524,6 +4525,18 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -6009,6 +6022,17 @@
"node": ">=6"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/property-information": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz",
@@ -6019,6 +6043,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/qr.js": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz",
+ "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==",
+ "license": "MIT"
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -6068,6 +6098,25 @@
"react": "^19.0.0"
}
},
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-qr-code": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.15.tgz",
+ "integrity": "sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==",
+ "license": "MIT",
+ "dependencies": {
+ "prop-types": "^15.8.1",
+ "qr.js": "0.0.0"
+ },
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
diff --git a/package.json b/package.json
index 6c1da60..5a5fe36 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"lucide-react": "^0.484.0",
"pocketbase": "^0.25.2",
"postcss": "^8.5.3",
+ "react-qr-code": "^2.0.15",
"react-router-dom": "^7.4.1",
"react-to-print": "^3.0.5",
"tailwind-merge": "^3.0.2",
diff --git a/src/components/DomainSetupForm.jsx b/src/components/DomainSetupForm.jsx
index 4f448f1..adbd070 100644
--- a/src/components/DomainSetupForm.jsx
+++ b/src/components/DomainSetupForm.jsx
@@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Toast } from './Toast';
import { TemplatePreview } from './TemplatePreview';
-
+import { Button } from './ui/button';
+import { Input } from "./ui/input";
+import { Label, Select } from '@radix-ui/react-select';
export const DomainSetupForm = ({ defaultSubdomain }) => {
// Deployment type and app selections
const [deploymentType, setDeploymentType] = useState('app');
@@ -27,12 +29,73 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
// DNS configuration
const [dnsMethod, setDnsMethod] = useState('cname');
const [showDnsConfig, setShowDnsConfig] = useState(false);
- const [dnsVerified, setDnsVerified] = useState({
- cname: false,
- ns: false,
- a: false,
- ip: false
- });
+ const [dnsVerified, setDnsVerified] = useState({ cname: false, ns: false, a: false, ip: false});
+ const [txnId, setTxnId] = useState('');
+ const [userEmail, setUserEmail] = useState('');
+
+ const [panelType, setPanelType] = useState('');
+ const API_URL = 'http://192.168.1.197:2058/v1/users/index.php';
+ const SERVICES_API_URL = 'http://192.168.1.197:2058/v1/services/index.php';
+ // const BILLING_API_URL = 'http://192.168.1.197:2058/v1/users/index.php';
+
+ const [selectedTenure, setSelectedTenure] = useState('');
+ const [selectedPrice, setSelectedPrice] = useState(0);
+
+ const handleCheckboxChange = (tenure, price) => {
+ if (selectedTenure === tenure) {
+ setSelectedTenure('');
+ setSelectedPrice(0);
+ } else {
+ setSelectedTenure(tenure);
+ setSelectedPrice(price);
+ }
+ // console.log(selectedTenure, ' ', selectedPrice);
+ };
+ const handlePanelBuyNow = () => {
+ // Disable button during processing
+ const buyButton = document.getElementById('buy-button'); // Add ID to your button
+ if (buyButton) buyButton.disabled = true;
+
+ showToast('Loading...');
+ const formData = new FormData();
+ formData.append('service', panelType);
+ formData.append('tenure', selectedTenure);
+ formData.append('amount', 1); //selectedPrice
+
+ fetch(`${API_URL}?query=initiate_payment`, {
+ method: 'POST',
+ body: formData,
+ credentials: 'include'
+ })
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ if(data.success === true){
+ setTxnId(data.txn_id);
+ setUserEmail(data.user_email);
+ window.location.href = `/make-payment?query=get-initiated_payment&orderId=${data.order_id}`
+ // redirectToPayU(data);
+ showToast('Redirecting to payment page...');
+ } else {
+ throw new Error(data.message || 'Payment initialization failed');
+ }
+ })
+ .catch(error => {
+ showToast(error.message || 'Payment failed. Please try again.');
+ console.error('An error occurred:', error);
+ })
+ .finally(() => {
+ if (buyButton) buyButton.disabled = false;
+ });
+ };
+
+
+
+
// Form validation
const [formValid, setFormValid] = useState(true);
@@ -159,52 +222,71 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
};
// Validate domain
- const validateDomain = () => {
+ const validateDomain = async () => {
const domain = domainType === 'domain' ? customDomain : customSubdomain;
-
- if (!domain) {
- setValidationMessage('Please enter a domain name.');
- setIsValidDomain(false);
- return;
- }
-
- // Initial validation: check format with regex
- const validFormat = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(domain);
- if (!validFormat) {
- setValidationMessage('Domain format is invalid. Please check your entry.');
- setIsValidDomain(false);
- return;
- }
-
+
+ // Reset validation state
setIsValidating(true);
+ setIsValidDomain(false);
setValidationMessage('');
setShowDnsConfig(false);
-
- // Simulate an API call to validate the domain
- setTimeout(() => {
- // Simulate a real domain check - in a real app this would be an API call
- // call /host-api/v1/domains/validate/?domain=domain.com
- const checkResult = true; // Assume domain is valid for demo
-
+
+ // Check if domain is empty
+ if (!domain) {
+ setValidationMessage('Please enter a domain name.');
setIsValidating(false);
- setIsValidDomain(checkResult);
-
- if (checkResult) {
+ setIsValidDomain(false);
+ return;
+ }
+
+ // Validate domain format
+ const domainFormatRegex = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i;
+ if (!domainFormatRegex.test(domain)) {
+ setValidationMessage('Domain format is invalid. Please check your entry.');
+ setIsValidating(false);
+ setIsValidDomain(false);
+ return;
+ }
+
+ try {
+ // Make API call to validate domain
+ const response = await fetch(`${SERVICES_API_URL}?query=validate-domain`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ domain })
+ });
+
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ }
+
+ const data = await response.json();
+
+ if (data.valid) {
setValidationMessage('Domain is valid and registered.');
+ setIsValidDomain(true);
setShowDnsConfig(true);
} else {
- setValidationMessage('Domain appears to be unregistered or unavailable.');
+ setValidationMessage(data.message || 'Domain appears to be unregistered or unavailable.');
+ setIsValidDomain(false);
}
-
+ } catch (error) {
+ console.error('Domain validation error:', error);
+ setValidationMessage('Error validating domain. Please try again.');
+ setIsValidDomain(false);
+ } finally {
+ setIsValidating(false);
validateForm();
- }, 500);
+ }
};
// Check DNS configuration
const checkSubDomainCname = () => {
const domainToCheck = customDomain || customSubdomain;
- fetch('http://localhost:2058/host-api/v1/check-c-name/', {
+ fetch(`${SERVICES_API_URL}?query=check-c-name`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
@@ -312,6 +394,7 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
⚙️ From Source
📄 Static Site Upload
🌐 Sample Web App
+ 🗄️ PHP MYSQL with a admin panel
@@ -461,316 +544,321 @@ export const DomainSetupForm = ({ defaultSubdomain }) => {
)}
+ {
+ deploymentType === 'php-mysql-with-admin-panel' && (
+
+ Select Panel
+ setPanelType(e.target.value)}
+ 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]"
+ >
+ -Select-
+ 🧰 Hestia Panel
+ 🖥️ Webmin
+ 📊 cPanel
+
+
+ )
+
+ }
+ {
+ deploymentType === 'php-mysql-with-admin-panel' && panelType === 'Hestia-Panel' && (
+ <>
+
+ {["5 Domains", "free Let's Encrypt SSL", "1 MariaDB database", "phpMyAdmin"].map((feature, index) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+ handleCheckboxChange('monthly', 200)} className="hidden" />
+ ₹200
+ Monthly
+
- {/* Domain Configuration */}
-
-
Destination
-
-
- {/* SiliconPin Subdomain */}
-
+
+ {selectedTenure && (
+
You selected {selectedTenure} plan at ₹{selectedPrice}
+ )}
+
+ >
+ )
+ }
+ {
+ deploymentType === 'php-mysql-with-admin-panel' && panelType === 'cPanel' ? (
+
+
+ cPanel is a proprietary software. Here at Siliconpin, we encourage using freedom-oriented software.
+ If you need a cPanel, you can visit
+ https://cicdhosting.com
+
+
- {/* Custom Domain */}
-
-
-
-
Use Custom Domain
+ ) : deploymentType === 'php-mysql-with-admin-panel' && (
+
Proceed to Pay
+ )
+ }
+ {
+ deploymentType !== 'php-mysql-with-admin-panel' && (
+ <>
+ {/* Domain Configuration */}
+
+
Destination
- {useCustomDomain && (
-
- {/* Domain Type Selection */}
-
-
-
- Root Domain
-
-
-
-
- Subdomain
-
+
+ {/* SiliconPin Subdomain */}
+
+
+
+
Use SiliconPin Subdomain
+
+
+ .subdomain.siliconpin.com
+
+
- {/* Domain Input */}
- {domainType === 'domain' ? (
-
-
-
- Enter domain without http://, www, or trailing slashes (example.com). You can configure www or other subdomains later.
-
-
- ) : (
-
-
-
- Enter the full subdomain without http:// or trailing slashes. www and protocol prefixes will be automatically removed.
-
-
- )}
+ {/* Custom Domain */}
+
+
+
+
Use Custom Domain
- {/* Domain Validation */}
-
- Validate Domain
-
+ {useCustomDomain && (
+
+ {/* Domain Type Selection */}
+
+
+
+ Root Domain
+
- {/* Validation Status */}
- {useCustomDomain && isValidating && (
-
-
-
-
Verifying domain registration and availability...
+
+
+ Subdomain
+
-
- )}
- {useCustomDomain && !isValidating && validationMessage && (
-
-
- {validationMessage}
-
-
- )}
-
- {/* DNS Configuration Options */}
- {showDnsConfig && (
-
-
Connect Your Domain
-
- {/* CNAME Record Option */}
-
-
+ {/* Domain Input */}
+ {domainType === 'domain' ? (
+
-
-
Use CNAME Record
-
Point your domain to our SiliconPin subdomain
+
+ Enter domain without http://, www, or trailing slashes (example.com). You can configure www or other subdomains later.
+
+
+ ) : (
+
+
+
+ Enter the full subdomain without http:// or trailing slashes. www and protocol prefixes will be automatically removed.
+
+
+ )}
-
-
- {defaultSubdomain}.subdomain.siliconpin.com
-
-
copyToClipboard(`${defaultSubdomain}.subdomain.siliconpin.com`)}
- className="ml-2 text-[#6d9e37] hover:text-white"
- aria-label="Copy CNAME value"
- >
- 📋
-
-
-
- (checkSubDomainCname())}
- className={`px-3 py-1 text-white text-sm rounded
- ${dnsVerified.cname
- ? 'bg-green-700 hover:bg-green-600'
- : 'bg-neutral-600 hover:bg-neutral-500'}`}
- >
- {dnsVerified.cname ? '✓ CNAME Verified' : 'Check CNAME'}
-
-
+ {/* Domain Validation */}
+
+ Validate Domain
+
+
+ {/* Validation Status */}
+ {useCustomDomain && isValidating && (
+
+
+
+
Verifying domain registration and availability...
-
-
+
+ )}
- {/* Nameserver Option (only for full domains, not subdomains) */}
- {domainType === 'domain' && (
- <>
-
-
-
+ {useCustomDomain && !isValidating && validationMessage && (
+
+
+ {validationMessage}
+
+
+ )}
+
+ {/* DNS Configuration Options */}
+ {showDnsConfig && (
+
+
Connect Your Domain
+
+ {/* CNAME Record Option */}
+
+
+
-
Use Our Nameservers
-
Update your domain's nameservers to use ours
+
Use CNAME Record
+
Point your domain to our SiliconPin subdomain
-
-
-
ns1.siliconpin.com
-
copyToClipboard('ns1.siliconpin.com')}
- className="ml-2 text-[#6d9e37] hover:text-white"
- aria-label="Copy nameserver value"
- >
- 📋
-
-
-
-
-
ns2.siliconpin.com
-
copyToClipboard('ns2.siliconpin.com')}
- className="ml-2 text-[#6d9e37] hover:text-white"
- aria-label="Copy nameserver value"
- >
- 📋
-
+
+
+ {defaultSubdomain}.subdomain.siliconpin.com
+
copyToClipboard(`${defaultSubdomain}.subdomain.siliconpin.com`)}
+ className="ml-2 text-[#6d9e37] hover:text-white"
+ aria-label="Copy CNAME value"
+ >
+ 📋
+
-
checkDnsConfig('ns')}
+ onClick={() => (checkSubDomainCname())}
className={`px-3 py-1 text-white text-sm rounded
- ${dnsVerified.ns
+ ${dnsVerified.cname
? 'bg-green-700 hover:bg-green-600'
: 'bg-neutral-600 hover:bg-neutral-500'}`}
>
- {dnsVerified.ns ? '✓ Nameservers Verified' : 'Check Nameservers'}
+ {dnsVerified.cname ? '✓ CNAME Verified' : 'Check CNAME'}
-
-
-
-
-
Use Our IP Address
-
Update your domain's nameservers to use ours
+ {/* Nameserver Option (only for full domains, not subdomains) */}
+ {domainType === 'domain' && (
+ <>
+
+
+
+
+
Use Our Nameservers
+
Update your domain's nameservers to use ours
-
-
-
xxx.xxx.x.xx
-
copyToClipboard('xxx.xxx.x.xx')}
- className="ml-2 text-[#6d9e37] hover:text-white"
- aria-label="Copy nameserver value"
- >
- 📋
-
+
+
+
ns1.siliconpin.com
+
copyToClipboard('ns1.siliconpin.com')} className="ml-2 text-[#6d9e37] hover:text-white" aria-label="Copy nameserver value">
+ 📋
+
+
+
+
+
ns2.siliconpin.com
+
copyToClipboard('ns2.siliconpin.com')} className="ml-2 text-[#6d9e37] hover:text-white" aria-label="Copy nameserver value">
+ 📋
+
+
+
+
+
+ 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'}
+
+
-
-
- 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
-
-
+
-
-
- >
+
+
+
+
+
+
Use Our IP Address
+
Update your domain's nameservers to use ours
+
+
+
xxx.xxx.x.xx
+
copyToClipboard('xxx.xxx.x.xx')} className="ml-2 text-[#6d9e37] hover:text-white" aria-label="Copy nameserver value">
+ 📋
+
+
+
+
+ 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
+
+
+
+
+
+ >
+ )}
+
)}
)}
- )}
+
-
-
- {/* Form Submit Button */}
-
- Start the Deployment
-
+ {/* Form Submit Button */}
+
+ Start the Deployment
+
+ >
+ )
+ }
);
};
+// upi://pay?pa=merchant@bank&pn=Merchant%20Inc&am=100.00&cu=INR&tn=Payment%20for%20goods
+
+
diff --git a/src/components/Login.tsx b/src/components/Login.tsx
index 0426fb6..b38f04b 100644
--- a/src/components/Login.tsx
+++ b/src/components/Login.tsx
@@ -28,8 +28,8 @@ interface AuthResponse {
const LoginPage = () => {
- const [email, setEmail] = useState('suvodip@siliconpin.com');
- const [password, setPassword] = useState('Simple2pass');
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
const [passwordVisible, setPasswordVisible] = useState(false);
const [status, setStatus] = useState
({ message: '', isError: false });
const [isLoading, setIsLoading] = useState(false);
@@ -115,10 +115,10 @@ const LoginPage = () => {
setIsLoading(false);
}
};
-
+
const syncSessionWithBackend = async (authData: AuthResponse, avatarUrl: string) => {
try {
- const response = await fetch('http://localhost:2058/host-api/v1/users/session/', {
+ const response = await fetch('http://192.168.1.197:2058/v1/users/?query=login', {
method: 'POST',
credentials: 'include', // Important for cookies
headers: { 'Content-Type': 'application/json' },
diff --git a/src/components/MakePayment.jsx b/src/components/MakePayment.jsx
new file mode 100644
index 0000000..47be175
--- /dev/null
+++ b/src/components/MakePayment.jsx
@@ -0,0 +1,180 @@
+import React, {useState, useEffect} from "react";
+import { Button } from "./ui/button";
+import QRCode from "react-qr-code";
+import { Dialog, DialogContent, DialogHeader, DialogTitle} from "./ui/dialog";
+import { Toast } from './Toast';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "./ui/card";
+
+const API_URL = 'http://192.168.1.197:2058/v1/users/index.php';
+
+export default function MakePayment(){
+ const [initialOrderData, setInitialOrderData] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [showQRModal, setShowQRModal] = useState(false);
+ const [upiPaymentLink, setUpiPaymentLink] = useState("");
+
+ useEffect(() => {
+ const urlParams = new URLSearchParams(window.location.search);
+ const orderId = urlParams.get('orderId');
+
+ if (!orderId) {
+ setError('Order ID is missing from URL');
+ setIsLoading(false);
+ return;
+ }
+
+ const getInitialOrderData = () => {
+ setIsLoading(true);
+ const formData = new FormData();
+ formData.append('order_id', orderId);
+
+ fetch(`${API_URL}?query=get-initiated_payment`, {
+ method: 'POST',
+ body: formData,
+ credentials: 'include'
+ })
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ if (!data.success) {
+ throw new Error(data.message || 'Failed to initialize payment');
+ }
+ setInitialOrderData(data);
+ setError(null);
+
+ // Generate UPI payment link when data is loaded
+ if (data.payment_data?.amount) {
+ const amount = data.payment_data.amount;
+ const upiLink = generateUPILink(amount, data.txn_id);
+ setUpiPaymentLink(upiLink);
+ }
+ })
+ .catch(error => {
+ setError(error.message || 'Payment failed. Please try again.');
+ console.error('An error occurred:', error);
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+ };
+
+ getInitialOrderData();
+ }, []);
+
+ function generateUPILink(amount, transactionId) {
+ // Replace these with your actual merchant details
+ const merchantUPI = "siliconpin@ybl";
+ const merchantName = "SiliconPin";
+ const currency = "INR";
+
+ // Encode parameters for URL
+ const encodedMerchantName = encodeURIComponent(merchantName);
+ const transactionNote = encodeURIComponent(`Payment for order #${transactionId}`);
+
+ // Construct UPI payment link
+ return `upi://pay?pa=${merchantUPI}&pn=${encodedMerchantName}&am=${amount}&cu=${currency}&tn=${transactionNote}`;
+ }
+// waiting payment update
+// Payment update not recieved if
+ function redirectToPayU() {
+ if (!initialOrderData?.payment_data || !initialOrderData.payment_url) {
+ console.error('Payment data not loaded yet');
+ alert('Payment information is not ready. Please wait.');
+ return;
+ }
+
+ // Create a form dynamically
+ const form = document.createElement('form');
+ form.method = 'POST';
+ form.action = initialOrderData.payment_url;
+ form.style.display = 'none';
+
+ Object.entries(initialOrderData.payment_data).forEach(([key, value]) => {
+ const input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = key;
+ input.value = value;
+ form.appendChild(input);
+ });
+
+ document.body.appendChild(form);
+ form.submit();
+ }
+
+ function handleQRPaymentClick() {
+ if (!upiPaymentLink) {
+ alert('Payment information is not ready. Please wait.');
+ return;
+ }
+ setShowQRModal(true);
+ }
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (error) {
+ return ;
+ }
+
+ return (
+
+
+ Complete Your Payment
+
+ {initialOrderData && (
+
+
+ Order ID:
+ {initialOrderData.txn_id}
+
+
+ Amount:
+ ₹{initialOrderData.payment_data?.amount}
+
+
+ )}
+
+
+ Pay Using:
+ UPI QR
+ OR
+ Payment Gateway
+ Applicabel 2% Transaction Charge if using Payment Gateway
+
+
+
+ {/* QR Code Modal */}
+
+
+
+ Scan QR Code to Pay
+
+
+
+
+
+
+ Scan this QR code with any UPI app to complete your payment
+
+
+ {upiPaymentLink}
+
+
navigator.clipboard.writeText(upiPaymentLink)} className="text-sm" >Copy UPI Link
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/PaymentForm.jsx b/src/components/PaymentForm.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/TestCool.jsx b/src/components/TestCool.jsx
new file mode 100644
index 0000000..23447c9
--- /dev/null
+++ b/src/components/TestCool.jsx
@@ -0,0 +1,171 @@
+import React, { useState } from "react";
+
+export default function DeployWordPress() {
+ const [loading, setLoading] = useState(false);
+ const [message, setMessage] = useState("");
+ const [debugInfo, setDebugInfo] = useState(null);
+
+ // Configuration - Replace with your actual values
+ const COOLIFY_API_URL = "http://192.168.1.197:8000/api/v1";
+ const TOKEN = "zXSR33z74eK26abbKyL9bz4d3PYouTSK8FSjOltv719c52d8";
+ const PROJECT_UUID = "wc40gg048gkwg0go80ggog44";
+ const SERVER_UUID = "sgswssowscc84o8sc8wockgc";
+ const ENVIRONMENT_NAME = "production"; // Changed from 'dev' to 'production' as default
+
+ const createWordPress = async () => {
+ setLoading(true);
+ setMessage("");
+ setDebugInfo(null);
+
+ try {
+ // 1. Create MySQL Service - Updated to Coolify's expected format
+ const mysqlPayload = {
+ name: "wordpress-db",
+ type: "mysql",
+ projectUuid: PROJECT_UUID,
+ serverUuid: SERVER_UUID,
+ version: "8.0",
+ destination: { // Coolify often requires this structure
+ serverUuid: SERVER_UUID,
+ environment: ENVIRONMENT_NAME
+ },
+ configuration: {
+ type: "mysql",
+ settings: { // Changed from environmentVariables to settings
+ MYSQL_ROOT_PASSWORD: "example",
+ MYSQL_DATABASE: "wordpress",
+ MYSQL_USER: "wordpress",
+ MYSQL_PASSWORD: "example",
+ MYSQL_ALLOW_EMPTY_PASSWORD: "no"
+ }
+ }
+ };
+
+ const mysqlRes = await fetch(`${COOLIFY_API_URL}/services`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${TOKEN}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(mysqlPayload),
+ });
+
+ const mysqlData = await mysqlRes.json();
+ setDebugInfo({ mysql: { request: mysqlPayload, response: mysqlData } });
+
+ if (!mysqlRes.ok) {
+ throw new Error(
+ mysqlData.message ||
+ mysqlData.error?.message ||
+ JSON.stringify(mysqlData.errors) ||
+ "MySQL validation failed"
+ );
+ }
+
+ // 2. Create WordPress Service
+ const wpPayload = {
+ name: "wordpress",
+ type: "wordpress",
+ projectUuid: PROJECT_UUID,
+ serverUuid: SERVER_UUID,
+ version: "latest",
+ destination: {
+ serverUuid: SERVER_UUID,
+ environment: ENVIRONMENT_NAME
+ },
+ configuration: {
+ type: "wordpress",
+ settings: {
+ WORDPRESS_DB_HOST: "wordpress-db",
+ WORDPRESS_DB_USER: "wordpress",
+ WORDPRESS_DB_PASSWORD: "example",
+ WORDPRESS_DB_NAME: "wordpress",
+ WORDPRESS_TABLE_PREFIX: "wp_"
+ }
+ }
+ };
+
+ const wpRes = await fetch(`${COOLIFY_API_URL}/services`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${TOKEN}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(wpPayload),
+ });
+
+ const wpData = await wpRes.json();
+ setDebugInfo(prev => ({ ...prev, wordpress: { request: wpPayload, response: wpData } }));
+
+ if (!wpRes.ok) {
+ throw new Error(
+ wpData.message ||
+ wpData.error?.message ||
+ JSON.stringify(wpData.errors) ||
+ "WordPress validation failed"
+ );
+ }
+
+ setMessage("🎉 WordPress + MySQL deployed successfully!");
+ } catch (err) {
+ setMessage(`❌ Deployment failed: ${err.message}`);
+ console.error("Deployment error:", err, debugInfo);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
Deploy WordPress
+
+
+ {loading ? (
+
+
+
+
+
+ Deploying...
+
+ ) : "Deploy WordPress"}
+
+
+ {message && (
+
+ {message}
+
+ )}
+
+ {debugInfo && (
+
+
+ Debug Details
+
+
{JSON.stringify(debugInfo, null, 2)}
+
+
+
+
Troubleshooting:
+
+ Verify PROJECT_UUID and SERVER_UUID are correct
+ Check if environment {ENVIRONMENT_NAME} exists
+ Ensure your Coolify version supports this API format
+
+
+
+ )}
+
+ );
+}
+
+
+// [{"uuid":"sgswssowscc84o8sc8wockgc","description":"This is the server where Coolify is running on. Don't delete this!","name":"localhost","ip":"host.docker.internal","is_coolify_host":true,"is_reachable":true,"is_usable":true,"port":22,"proxy":{"redirect_enabled":true},"settings":{"id":1,"concurrent_builds":2,"delete_unused_networks":false,"delete_unused_volumes":false,"docker_cleanup_frequency":"0 0 * * *","docker_cleanup_threshold":80,"dynamic_timeout":3600,"force_disabled":false,"force_docker_cleanup":true,"generate_exact_labels":false,"is_build_server":false,"is_cloudflare_tunnel":false,"is_jump_server":false,"is_logdrain_axiom_enabled":false,"is_logdrain_custom_enabled":false,"is_logdrain_highlight_enabled":false,"is_logdrain_newrelic_enabled":false,"is_metrics_enabled":false,"is_reachable":true,"is_sentinel_debug_enabled":false,"is_sentinel_enabled":false,"is_swarm_manager":false,"is_swarm_worker":false,"is_usable":true,"logdrain_axiom_api_key":null,"logdrain_axiom_dataset_name":null,"logdrain_custom_config":null,"logdrain_custom_config_parser":null,"logdrain_highlight_project_id":null,"logdrain_newrelic_base_uri":null,"logdrain_newrelic_license_key":null,"sentinel_custom_url":"http:\/\/host.docker.internal:8000","sentinel_metrics_history_days":7,"sentinel_metrics_refresh_rate_seconds":10,"sentinel_push_interval_seconds":60,"sentinel_token":"eyJpdiI6IllwMlBsOUtXODdUR0ZIbWtZenJRWFE9PSIsInZhbHVlIjoiZFUxcE9zSXFXdkVrN0tDUGdSbHpGVTE3cHVEeFlBM1hFWk56S05NVWVmVmxHV0tBQ2kra25uRnVzRzNHaFpBSGVTaGZLMGpqdS9nMU85MXhmS3VYMVE9PSIsIm1hYyI6ImY3ODQyNGI2OTVlMmRiMzFmNzJjZjRlYjNkNDIxOGVmZTAxOWMyNjY0ZTYxODE5MDIwY2FhMGUwYjU4ODMyN2MiLCJ0YWciOiIifQ==","server_disk_usage_check_frequency":"0 23 * * *","server_disk_usage_notification_threshold":80,"server_id":0,"server_timezone":"UTC","wildcard_domain":null,"created_at":"2025-04-03T17:12:45.000000Z","updated_at":"2025-04-03T17:16:25.000000Z"},"user":"root"}]
\ No newline at end of file
diff --git a/src/components/Ticket.tsx b/src/components/Ticket.tsx
index eb8d666..a05b03b 100644
--- a/src/components/Ticket.tsx
+++ b/src/components/Ticket.tsx
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react";
import { Button } from "./ui/button";
-import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./ui/card";
import Table from "./ui/table";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog";
import { Input } from "./ui/input";
@@ -28,7 +27,7 @@ interface Message {
user_type: string;
}
-const API_URL = 'http://localhost:2058/host-api/app/v1/ticket/index.php';
+const API_URL = 'http://192.168.1.197:2058/v1/ticket/index.php';
function Ticketing() {
const [tickets, setTickets] = useState([]);
diff --git a/src/components/Topic.tsx b/src/components/Topic.tsx
new file mode 100644
index 0000000..72364b0
--- /dev/null
+++ b/src/components/Topic.tsx
@@ -0,0 +1,26 @@
+import React, {useEffect, useState} from "react";
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./ui/card";
+
+export default function TopicCreation(){
+ const API_URL = 'http://192.168.1.197:2058/v1/users/index.php';
+ // useEffect(() => {
+ // fetch(`${API_URL}?query=get-topic`)
+ // .then(response => response.json())
+ // .then{(data: any) => {
+ // console.log(data);
+ // }}
+ // })
+ return(
+ <>
+
+
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo, excepturi ipsum et animi debitis doloremque quam aliquid quaerat. Totam officiis iste laudantium amet corrupti, doloribus sunt minima dolor odit. Ipsum.
+
+
+
+
+ >
+ )
+}
diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx
index ec1d211..1bed53b 100644
--- a/src/components/UserProfile.tsx
+++ b/src/components/UserProfile.tsx
@@ -8,6 +8,7 @@ import { Separator } from "./ui/separator";
import { Textarea } from "./ui/textarea";
import React, { useState, useEffect } from 'react';
import UpdateAvatar from './UpdateAvatar';
+import {localizeTime} from "../lib/localizeTime";
interface SessionData {
[key: string]: any;
@@ -24,13 +25,12 @@ export default function ProfilePage() {
const [userData, setUserData] = useState(null);
const [invoiceList, setInvoiceList] = useState([]);
const [error, setError] = useState(null);
-
+ const USER_API_URL = 'http://192.168.1.197:2058/v1/users/index.php';
+ const INVOICE_API_URL = 'http://192.168.1.197:2058/v1/invoice/';
useEffect(() => {
const fetchSessionData = async () => {
try {
- const response = await fetch(
- 'http://localhost:2058/host-api/v1/users/get-profile-data/',
- {
+ const response = await fetch(`${USER_API_URL}?query=get-user`, {
credentials: 'include', // Crucial for cookies
headers: { 'Accept': 'application/json' }
}
@@ -49,7 +49,7 @@ export default function ProfilePage() {
};
const getInvoiceListData = async () => {
try {
- const response = await fetch('http://localhost:2058/host-api/v1/invoice/invoice-info/', {
+ const response = await fetch(`${USER_API_URL}?query=invoice-info`, {
method: 'GET',
credentials: 'include', // Crucial for cookies
headers: { 'Accept': 'application/json' }
@@ -105,7 +105,6 @@ export default function ProfilePage() {
JP
-
@@ -128,17 +127,22 @@ export default function ProfilePage() {
- Billing Information
+
+
View your billing history.
- Invoice ID
- Date
+ Invoice
+ Invoice Date
Description
Amount
+ Status
Action
@@ -146,16 +150,19 @@ export default function ProfilePage() {
{
invoiceList.map((invoice) => (
- {invoice.invoice_id}
- {invoice.date}
- {invoice.description}
- {invoice.amount}
- Print
+ {invoice.invoice_number}
+ {invoice.invoice_date}
+ {invoice.notes ? invoice.notes : ''}
+ {invoice.total_amount}
+ {invoice.status}
+
+ View
+
))
}
-
+
diff --git a/src/pages/make-payment.astro b/src/pages/make-payment.astro
new file mode 100644
index 0000000..d71591d
--- /dev/null
+++ b/src/pages/make-payment.astro
@@ -0,0 +1,9 @@
+---
+import Layout from "../layouts/Layout.astro";
+import MakePaymentPage from "../components/MakePayment";
+---
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/payment.astro b/src/pages/payment.astro
new file mode 100644
index 0000000..2900b47
--- /dev/null
+++ b/src/pages/payment.astro
@@ -0,0 +1,9 @@
+---
+import Layout from "../layouts/Layout.astro";
+---
+
+
+
Payment Page
+ We are working on this!
+
+
\ No newline at end of file
diff --git a/src/pages/test-cool.astro b/src/pages/test-cool.astro
new file mode 100644
index 0000000..422a075
--- /dev/null
+++ b/src/pages/test-cool.astro
@@ -0,0 +1,7 @@
+---
+import Layout from "../layouts/Layout.astro";
+import TestCools from "../components/TestCool";
+---
+
+
+
\ No newline at end of file
diff --git a/src/pages/topic.astro b/src/pages/topic.astro
new file mode 100644
index 0000000..b041d43
--- /dev/null
+++ b/src/pages/topic.astro
@@ -0,0 +1,7 @@
+---
+import Layout from "../layouts/Layout.astro";
+import TopicList from "../components/Topic";
+---
+
+
+
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index b24627f..b157276 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1919,7 +1919,7 @@ jiti@^1.21.6, jiti@>=1.21.0:
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz"
integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
-js-tokens@^4.0.0:
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
@@ -1983,6 +1983,13 @@ longest-streak@^3.0.0:
resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz"
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
+loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
lru-cache@^10.2.0, lru-cache@^10.4.3:
version "10.4.3"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz"
@@ -2563,7 +2570,7 @@ npm-run-path@^5.1.0:
dependencies:
path-key "^4.0.0"
-object-assign@^4.0.1:
+object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@@ -2798,6 +2805,15 @@ prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
+prop-types@^15.8.1:
+ version "15.8.1"
+ resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
property-information@^6.0.0:
version "6.5.0"
resolved "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz"
@@ -2808,6 +2824,11 @@ property-information@^7.0.0:
resolved "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz"
integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==
+qr.js@0.0.0:
+ version "0.0.0"
+ resolved "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz"
+ integrity sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
@@ -2825,6 +2846,19 @@ radix3@^1.1.2:
dependencies:
scheduler "^0.25.0"
+react-is@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-qr-code@^2.0.15:
+ version "2.0.15"
+ resolved "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.15.tgz"
+ integrity sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==
+ dependencies:
+ prop-types "^15.8.1"
+ qr.js "0.0.0"
+
react-refresh@^0.14.2:
version "0.14.2"
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz"
@@ -2879,7 +2913,7 @@ react-to-print@^3.0.5:
resolved "https://registry.npmjs.org/react-to-print/-/react-to-print-3.0.5.tgz"
integrity sha512-Z15MwMOzYCHWi26CZeFNwflAg7Nr8uWD6FTj+EkfIOjYyjr0MXGbI0c7rF4Fgrbj3XG9hFndb1ourxpPz2RAiA==
-"react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ~19", "react@^17.0.2 || ^18.0.0 || ^19.0.0", react@^19.0.0, react@>=16.8.0, react@>=18:
+react@*, "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ~19", "react@^17.0.2 || ^18.0.0 || ^19.0.0", react@^19.0.0, react@>=16.8.0, react@>=18:
version "19.0.0"
resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz"
integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==