suvodip ghosh 2025-07-12 13:22:40 +00:00
parent 9f2f235c09
commit e8f62c18a6
13 changed files with 569 additions and 130 deletions

View File

@ -0,0 +1,360 @@
import React, {useState, useEffect} from "react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import QRCode from "react-qr-code";
import { Dialog, DialogContent, DialogHeader, DialogTitle} from "./ui/dialog";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
import { FileX, Copy, CheckCircle2, AlertCircle, X } from "lucide-react";
import Loader from "./ui/loader";
const PUBLIC_USER_API_URL = 'https://siliconpin.com/v1/balance/';
// const PUBLIC_USER_API_URL = import.meta.env.PUBLIC_USER_API_URL;
export default function AddBalance() {
const [orderData, setOrderData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [showQRModal, setShowQRModal] = useState(false);
const [showHelpMessage, setShowHelpMessage] = useState(false);
const [upiPaymentLink, setUpiPaymentLink] = useState("");
const [copied, setCopied] = useState(false);
const [orderId, setOrderId] = useState('');
const [transactionId, setTransactionId] = useState('');
const [paymentResponse, setPaymentResponse] = useState({
message: '',
visible: false,
isSuccess: false
});
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;
}
setOrderId(orderId);
fetchOrderData(orderId);
}, []);
const fetchOrderData = async (orderId) => {
setIsLoading(true);
try {
const formData = new FormData();
formData.append('order_id', orderId);
const response = await fetch(`${PUBLIC_USER_API_URL}?query=get-initiated-add-balance`, {
method: 'POST',
headers: {
'Content-Type' : 'application/json'
},
body: JSON.stringify({order_id:orderId}),
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (!data.success) {
throw new Error(data.message || 'Failed to fetch payment details');
}
setOrderData(data);
setError(null);
// Generate UPI payment link
if (data.payment_data?.amount) {
const upiLink = generateUPILink(data.payment_data.amount, data.txn_id);
setUpiPaymentLink(upiLink);
}
} catch (error) {
setError(error.message || 'Failed to load payment details. Please try again.');
console.error('Error:', error);
} finally {
setIsLoading(false);
}
};
function generateUPILink(amount, transactionId) {
const merchantUPI = "7001601485@okbizaxis";
const merchantName = "SiliconPin";
const currency = "INR";
const transactionNote = encodeURIComponent(`Balance top-up #${transactionId}`);
return `upi://pay?pa=${merchantUPI}&pn=${encodeURIComponent(merchantName)}&am=${amount}&cu=${currency}&tn=${transactionNote}`;
}
const redirectToPayU = () => {
if (!orderData?.payment_data || !orderData.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 = orderData.payment_url;
form.style.display = 'none';
Object.entries(orderData.payment_data).forEach(([key, value]) => {
const input = document.createElement('input');
input.type = 'text';
input.name = key;
input.value = value;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
};
const handlePayPalPayment = () => {
const rawInrAmount = orderData.payment_data?.amount || 0;
const exchangeRate = 80;
const convertedUSD = rawInrAmount / exchangeRate;
const usdAmount = Math.max(convertedUSD, 1).toFixed(2);
const paypalUrl = `https://www.paypal.com/paypalme/dwdconsultancy/${usdAmount}`;
window.open(paypalUrl, '_blank');
};
const handleQRPaymentClick = () => {
if(orderData.currency !== 'INR'){
return
}
if (!upiPaymentLink) {
alert('Payment information is not ready. Please wait.');
return;
}
setShowQRModal(true);
};
const handleCopy = () => {
navigator.clipboard.writeText(orderId)
.then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 1000);
})
.catch(err => {
console.error('Failed to copy text: ', err);
});
};
const handleSavePayment = async () => {
try {
const response = await fetch(`${PUBLIC_USER_API_URL}?query=save-balance-payment`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
orderId: orderId,
transactionId: transactionId
})
});
const data = await response.json();
if (data.success) {
setPaymentResponse({
message: `Payment of ₹${orderData?.payment_data?.amount} recorded. Your balance will be updated shortly.`,
visible: true,
isSuccess: true
});
} else {
throw new Error(data.message || 'Payment verification failed');
}
} catch (error) {
console.error('Error:', error);
setPaymentResponse({
message: 'Failed to verify payment. Please contact support.',
visible: true,
isSuccess: false
});
}
};
useEffect(() => {
let timer;
if (showQRModal) {
setShowHelpMessage(false);
timer = setTimeout(() => {
setShowHelpMessage(true);
}, 2000);
}
return () => clearTimeout(timer);
}, [showQRModal]);
if (isLoading) {
return <Loader />;
}
if (!orderData) {
return (
<div className="flex flex-col items-center justify-center flex-grow py-10 text-center px-4 text-gray-600 mt-20">
<FileX className="mb-4 text-gray-500" size={100} />
<p className="text-lg">No payment request found. <a className="text-[#6d9e37] font-medium hover:underline" href="/profile">Click here</a> to go to your profile.</p>
</div>
);
}
if (error) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
<strong>Error:</strong> {error}
</div>
</div>
);
}
const formattedBalance = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: orderData.currency, // fallback if undefined
maximumFractionDigits: 2,
}).format(orderData.payment_data?.amount || 0);
return (
<div className="flex flex-col items-center justify-center min-h-screen p-4">
<Card className="max-w-md w-full bg-white p-6 rounded-lg shadow-md">
<CardHeader>
<CardTitle>Add Balance to Your Account</CardTitle>
</CardHeader>
<CardContent>
{orderData && (
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
<div className="flex justify-between mb-2">
<span className="font-bold">Order ID:</span>
<span>{orderData.txn_id}</span>
</div>
<div className="flex justify-between">
<span className="font-bold">Amount:</span>
<span>{formattedBalance}</span>
</div>
</div>
)}
<div className="flex flex-col gap-3">
<span className="font-medium">Pay Using:</span>
<hr className="border-b border-b-[#6d9e37] mb-2" />
<Button disabled={orderData.currency !== 'INR'} onClick={handleQRPaymentClick}>UPI QR Code</Button>
{
orderData.currency !== 'INR' && (
<p className="text-center text-xs text-gray-500">UPI QR Code Payment support only for INR currency</p>
)
}
<span className="text-center text-gray-400 text-xs">Banking Service by DWD Consultancy Services (0% Processing Fee)</span>
<div className="flex items-center my-2">
<div className="flex-grow border-t border-gray-500"></div>
<span className="mx-2 text-gray-500 font-semibold">OR</span>
<div className="flex-grow border-t border-gray-500"></div>
</div>
<Button
disabled={orderData.currency !== 'INR'}
className="text-[#414042] font-bold border-[2px] border-[#00ad7d] hover:bg-[#00ad7d]/80"
onClick={redirectToPayU}
variant="outline"
>
Pay with&nbsp;
<img className="w-[50px]" src="/assets/payu-logo.svg" alt="PayU" />
</Button>
{
orderData.currency !== 'INR' && (
<p className="text-center text-xs text-gray-500">Pay with PayU support only for INR currency</p>
)
}
<Button
variant="outline"
className="mt-2 text-[#414042] font-bold border-[2px] border-b-[#003087] border-r-[#003087] border-t-[#009CDE] border-l-[#009CDE] hover:bg-gradient-to-r hover:from-[#009CDE] hover:to-[#003087]"
onClick={handlePayPalPayment}
>
Pay with&nbsp;
<img className="w-[50px]" src="/assets/paypal-logo-white.svg" alt="PayPal" />
</Button>
</div>
</CardContent>
</Card>
{/* QR Code Modal */}
<Dialog open={showQRModal} onOpenChange={(open) => {setShowQRModal(open); if (!open) {setShowHelpMessage(false);}}}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Scan QR Code to Add Balance</DialogTitle>
</DialogHeader>
<div className="flex flex-col items-center space-y-4">
<div className="p-4 bg-white rounded-lg border border-gray-200">
<QRCode value={upiPaymentLink} size={256} level="H" />
</div>
<p className="text-sm text-gray-500 text-center">
Scan this QR code with any UPI app to complete your payment
</p>
<Button variant="outline" onClick={handleCopy}>
{copied ? 'Copied!' : orderId}
{!copied && <Copy size={15} className="ml-2" />}
</Button>
{showHelpMessage && (
<div className="w-full mt-4 p-4 bg-yellow-50 rounded-lg border border-yellow-200">
<p className="text-sm text-yellow-800 mb-3">
If you haven't received payment confirmation, please share your transaction ID with us.
</p>
<Input
type="text"
value={transactionId}
onChange={(e) => setTransactionId(e.target.value)}
placeholder="Enter UPI transaction ID"
className="w-full mb-2"
/>
<Button onClick={handleSavePayment} className="w-full">
Verify Payment
</Button>
</div>
)}
</div>
</DialogContent>
</Dialog>
{/* Payment Response Toast */}
{paymentResponse.visible && (
<div className="fixed bottom-4 left-0 right-0 flex justify-center">
<div className={`p-4 rounded-lg shadow-lg max-w-md w-full mx-4 ${
paymentResponse.isSuccess
? 'bg-green-50 border border-green-200 text-green-800'
: 'bg-red-50 border border-red-200 text-red-800'
}`}>
<div className="flex justify-between items-center">
<div className="flex items-center">
{paymentResponse.isSuccess ? (
<CheckCircle2 className="w-5 h-5 mr-2 text-green-500" />
) : (
<AlertCircle className="w-5 h-5 mr-2 text-red-500" />
)}
<span>{paymentResponse.message}</span>
</div>
<button
onClick={() => setPaymentResponse(prev => ({...prev, visible: false}))}
className="p-1 rounded-full hover:bg-opacity-20 hover:bg-current"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
</div>
)}
</div>
);
}

View File

@ -8,6 +8,7 @@ import { Button } from "./ui/button";
import { useIsLoggedIn } from '../lib/isLoggedIn';
import { token, user_name, pb_id } from '../lib/CookieValues';
import CommentSystem from './CommentSystem/CommentSystem';
import '../styles/markdown.css'
const COMMENTS_API_URL = 'https://host-api-sxashuasysagibx.siliconpin.com/v1/comments/';
export default function TopicDetail(props) {
@ -123,7 +124,7 @@ export default function TopicDetail(props) {
return (
<div className="container mx-auto px-4 py-12">
<article className="max-w-4xl mx-auto">
<img src={props.topic.img ? props.topic.img : '/assets/images/thumb-place.jpg'} alt={props.topic.title} className="w-full object-cover rounded-lg mb-8 shadow-md" />
<img src={props.topic.img ? props.topic.img : '/assets/images/thumb-place.jpg'} alt={props.topic.title} className="w-full object-cover rounded-lg mb-8 shadow-md" />
<h1 className="text-4xl font-bold text-[#6d9e37] mb-6">{props.topic.title}</h1>
{/* Enhanced Social Share Buttons */}
@ -209,7 +210,7 @@ export default function TopicDetail(props) {
</div>
</div>
<div id="markdown-content" className="table-scroll-wrapper font-light mb-8 text-justify prose max-w-none" dangerouslySetInnerHTML={{ __html: marked.parse(props.topic.content || '') }} ></div>
<div id="markdown-content" className="table-scroll-wrapper font-light mb-8 text-justify prose max-w-none leading-8" dangerouslySetInnerHTML={{ __html: marked.parse(props.topic.content || '') }} ></div>
{
allGalleryImages && (
<ImageSlider images={allGalleryImages} />

View File

@ -42,7 +42,7 @@ export default function TopicItems(props) {
</div>
{
props.topics.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto">
{
props.topics.map((topic) => (
<a href={`/topic/${topic.slug}`} key={topic.id} className="hover:scale-[1.02] transition-transform duration-200 ">
@ -52,7 +52,7 @@ export default function TopicItems(props) {
</div>
<CardContent className="flex-1 p-6">
<CardTitle className="mb-2 line-clamp-1">{topic.title}</CardTitle>
<CardDescription className="line-clamp-4 mb-4 text-justify" dangerouslySetInnerHTML={{ __html: marked.parse(topic.content || '') }}></CardDescription>
<CardDescription className="line-clamp-6 mb-4 text-justify" dangerouslySetInnerHTML={{ __html: marked.parse(topic.content || '') }}></CardDescription>
<div className="flex justify-between items-center">
<p className="text-xs text-gray-500"><strong>Author: </strong>{topic.user_name}</p>
<p className="text-xs text-gray-500">

View File

@ -15,7 +15,7 @@ export default function TopicCreation() {
const [pagination, setPagination] = useState({
current_page: 1,
last_page: 1,
per_page: 10,
per_page: 12,
total: 0
});

View File

@ -15,6 +15,7 @@ import { PDFDownloadLink } from '@react-pdf/renderer';
import InvoicePDF from "../lib/InvoicePDF";
import { useIsLoggedIn } from '../lib/isLoggedIn';
import PasswordUpdateCard from './PasswordUpdateCard';
import Index from "../pages/topic/index.astro";
interface SessionData {
[key: string]: any;
}
@ -42,7 +43,7 @@ type PaymentData = {
};
export default function ProfilePage() {
const { isLoggedIn, loading, sessionData } = useIsLoggedIn();
const { isLoggedIn, loading, sessionData, balance } = useIsLoggedIn();
const typedSessionData = sessionData as SessionData | null;
const [userData, setUserData] = useState<UserData | null>(null);
const [invoiceList, setInvoiceList] = useState<any[]>([]);
@ -52,10 +53,27 @@ export default function ProfilePage() {
const [toast, setToast] = useState<ToastState>({ visible: false, message: '' });
const [txnId, setTxnId] = useState<string>('');
const [userEmail, setUserEmail] = useState<string>('');
const [addBalanceModal, setAddBalanceModal] = useState(false);
const [balanceData, setBalanceData] = useState([
{
currency: "INR",
balanceList: ["100", "500", "1000", "1500"]
},
{
currency: "USD",
balanceList: ["10", "50", "100", "150"]
}
]);
const [selectedAmount, setSelectedAmount] = useState('');
const [customAmount, setCustomAmount] = useState('');
const [selectedCurrency, setSelectedCurrency] = useState("INR");
const [balanceApiloading, setBalanceApiloading] = useState(false);
const [formError, setFormError] = useState('');
const PUBLIC_USER_API_URL = import.meta.env.PUBLIC_USER_API_URL;
const PUBLIC_INVOICE_API_URL = import.meta.env.PUBLIC_INVOICE_API_URL;
const currentBalanceList = balanceData.find(item => item.currency === selectedCurrency)?.balanceList || [];
useEffect(() => {
const fetchSessionData = async () => {
try {
@ -106,6 +124,42 @@ export default function ProfilePage() {
getInvoiceListData();
}, []);
const handleNext = async () => {
const amountToSend = selectedAmount === "others" ? customAmount : selectedAmount;
if (!amountToSend || isNaN(Number(amountToSend))) {
setFormError("Please enter a valid amount.");
return;
}
const formData = new FormData();
formData.append('amount', amountToSend);
formData.append('currency', selectedCurrency); // ✅ Include currency
try {
setBalanceApiloading(true);
const response = await fetch(`https://siliconpin.com/v1/balance/?query=initiate-add-balance`, {
method: "POST",
credentials: 'include',
body: formData,
});
const data = await response.json();
if (data.success === true) {
window.location.href = `/add-balance?orderId=${data.transaction_id}`;
} else {
alert("Something went wrong. Please try again.");
}
} catch (error) {
console.error("API Error:", error);
alert("An error occurred while processing your request.");
} finally {
setBalanceApiloading(false);
}
};
const showToast = (message: string) => {
@ -164,6 +218,11 @@ export default function ProfilePage() {
if (!userData) {
return <Loader />;
}
const formattedBalance = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
maximumFractionDigits: 2,
}).format(balance);
return (
<div className="space-y-6 container mx-auto">
<Separator />
@ -171,7 +230,17 @@ export default function ProfilePage() {
<div className="flex-1 lg:max-w-3xl">
<Card>
<CardHeader>
<CardTitle>Personal Information</CardTitle>
<div className="flex justify-between">
<CardTitle>Personal Information</CardTitle>
<div className="group relative overflow-hidden w-fit cursor-pointer" onClick={() => {setAddBalanceModal(true)}}>
<div className="flex items-center space-x-2 border-y border-l border-[#6d9e37] rounded-full relative z-10 transition-all duration-300">
<p className="text-lg font-bold text-center px-2 group-hover:text-white">Balance: {formattedBalance}</p>
<Button title="Add Balance" className="text-lg rounded-full group-hover:bg-[#262626] z-10" variant="outline" size="sm">&#43;</Button>
</div>
<div className="absolute inset-0 bg-[#6d9e37] translate-x-full group-hover:translate-x-0 transition-transform duration-300 ease-in-out z-0 rounded-full"></div>
</div>
</div>
<CardDescription>
Update your personal information and avatar.
</CardDescription>
@ -367,6 +436,76 @@ export default function ProfilePage() {
</DialogFooter>
</DialogContent>
</Dialog>
{/* Add balance modal */}
<Dialog open={addBalanceModal} onOpenChange={setAddBalanceModal}>
<DialogContent className="max-w-md sm:rounded-2xl p-6">
<DialogHeader>
<DialogTitle className="">Add Balance</DialogTitle>
<DialogDescription className="">Choose an amount or enter to add to your balance</DialogDescription>
{formError && <p className="text-red-600 text-sm mt-2">{formError}</p>}
</DialogHeader>
<div className="flex gap-3 flex-wrap mb-4">
{["INR", "USD"].map((currency) => (
<Button
key={currency}
variant={selectedCurrency === currency ? "default" : "outline"}
onClick={() => {
setSelectedCurrency(currency);
setSelectedAmount('');
setCustomAmount('');
setFormError('');
}}
>
{currency}
</Button>
))}
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{balanceData
.find((item) => item.currency === selectedCurrency)
?.balanceList.map((amount) => (
<Button
key={amount}
variant={customAmount === amount ? "default" : "outline"}
onClick={() => {
setCustomAmount(amount);
setSelectedAmount(amount);
}}
>
{selectedCurrency === "INR" ? `${amount}` : `$${amount}`}
</Button>
))}
</div>
<div
className={`flex items-center gap-2 border-b ${
customAmount ? "border-[#6d9e37]" : "border-gray-700"
} p-2 rounded-md text-[#6d9e37] font-bold outline-none mt-4 w-full`}
>
<span className="text-sm whitespace-nowrap">Youve selected</span>
<span className="text-lg">
{selectedCurrency === "INR" ? "₹" : "$"}
</span>
<input
type="text"
placeholder="Amount"
className="w-20 bg-transparent outline-none text-[#6d9e37] text-lg font-bold text-center"
value={customAmount}
onChange={(e) => {
setCustomAmount(e.target.value);
setSelectedAmount(e.target.value);
}}
/>
<span className="text-sm whitespace-nowrap">to top up your balance.</span>
</div>
<DialogFooter className="mt-6 flex justify-between w-full">
<Button variant="outline" onClick={() => setAddBalanceModal(false)} className="w-full"> Cancel </Button>
<Button disabled={!customAmount || balanceApiloading} onClick={handleNext} className="w-full">{balanceApiloading ? "Processing..." : "Next"}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}

View File

@ -1,11 +1,13 @@
import { useState, useEffect } from 'react';
const PUBLIC_USER_API_URL = import.meta.env.PUBLIC_USER_API_URL;
export const useIsLoggedIn = () => {
const [isLoggedIn, setIsLoggedIn] = useState(null); // null means "unknown"
const [isLoggedIn, setIsLoggedIn] = useState(null);
const [sessionData, setSessionData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [balance, setBalance] = useState(0);
useEffect(() => {
const checkLoginStatus = async () => {
@ -13,14 +15,30 @@ export const useIsLoggedIn = () => {
const response = await fetch(`${PUBLIC_USER_API_URL}?query=isLoggedIn`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error(`Network response was not ok (status: ${response.status})`);
}
const data = await response.json();
setIsLoggedIn(!!data?.isLoggedIn);
setSessionData(data?.session_data || null);
if (!data.success) {
throw new Error('API request was not successful');
}
setIsLoggedIn(!!data.isLoggedIn);
setSessionData(data.session_data || null);
const userData = data.userDBData?.[0]; // Get first item in array
setBalance(Number(data?.userDBData?.balance || 0));
setLoading(false);
} catch (err) {
setError(err);
setIsLoggedIn(false);
setSessionData(null);
setBalance(0);
setLoading(false);
}
};
@ -28,14 +46,14 @@ export const useIsLoggedIn = () => {
checkLoginStatus();
}, []);
return { isLoggedIn, sessionData, loading, error };
return { isLoggedIn, sessionData, loading, error, balance };
};
const IsLoggedIn = ({ children, fallback = null }) => {
const { isLoggedIn, loading, error } = useIsLoggedIn();
const { isLoggedIn, loading, error, balance } = useIsLoggedIn();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error checking login status</div>;
if (error) return <div>Error checking login status: {error.message}</div>;
return isLoggedIn ? children : fallback;
};

View File

@ -0,0 +1,7 @@
---
import Layout from "../layouts/Layout.astro";
import AddBalancePage from "../components/AddBalance";
---
<Layout title="">
<AddBalancePage client:load />
</Layout>

View File

@ -4,5 +4,5 @@ import PaymentSucces from "../components/PaymentSuccessfully";
---
<Layout title="">
<PaymentSucces client:load />
<PaymentSucces client:load />
</Layout>

View File

@ -1,68 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Upload Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.upload-container {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
}
.file-input-wrapper {
margin: 20px 0;
}
.file-label {
display: inline-block;
padding: 10px 15px;
background-color: #f0f0f0;
border-radius: 4px;
cursor: pointer;
border: 1px solid #ccc;
}
.upload-btn {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.note {
margin-top: 20px;
font-size: 0.9em;
color: #666;
}
</style>
</head>
<body>
<div>
<h1>Image Upload Test</h1>
<div class="upload-container">
<!-- Pure HTML form with direct submission -->
<form
action="/v1/check/"
method="POST"
enctype="multipart/form-data"
>
<input type="submit" value="Upload Image" class="upload-btn">
</form>
</div>
</div>
</body>
</html>

View File

@ -1,7 +0,0 @@
---
import Layout from "../layouts/Layout.astro"
import TestFileUpload from "../components/TestFileUpload"
---
<Layout title="">
<TestFileUpload client:load />
</Layout>

View File

@ -1,9 +0,0 @@
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="">
<div class="text-center text-2xl font-bold my-4">Test to Include PHP under Astro file</div>
<div set:html="<?php echo 'Hello! I am from PHP'; ?>"></div>
</Layout>

View File

@ -50,34 +50,4 @@ export async function getStaticPaths() {
<Layout title={`${topic.title ? topic.title + '- Silicon Topic' : 'Silicon Topic'}`} ogImage={topic.img? topic.img : '/assets/images/thumb-place.jpg'} canonicalURL={`/topic/${topic.slug}`} >
<TopicDetail client:load topic={topic} />
</Layout>
<style>
#markdown-content > table, th, tbody, td{
border: 1px solid #e5e7eb;
}
#markdown-content > table > thead > tr > th{
text-align: center;
padding: 5px;
}
#markdown-content > table > thead > tr > th:first-child,
#markdown-content > table > tbody > tr > td:first-child {
text-align: left;
}
#markdown-content > table > thead > tr > th:not(:first-child),
#markdown-content > table > tbody > tr > td:not(:first-child) {
text-align: center;
}
.table-scroll-wrapper > table {
overflow-x: auto;
width: 100%;
table-layout: auto;
}
.table-scroll-wrapper > table {
min-width: 600px; /* adjust as needed */
width: 100%;
border-collapse: collapse;
}
</style>
</Layout>

28
src/styles/markdown.css Normal file
View File

@ -0,0 +1,28 @@
/* markdown.css */
/* Wrapper div scroll korbe */
.table-scroll-wrapper {
@apply overflow-x-auto;
}
/* Markdown-er table scrollable + bordered hobe */
.table-scroll-wrapper table {
@apply w-max min-w-full border border-gray-300 block;
}
/* Table cell border, padding */
.table-scroll-wrapper th,
.table-scroll-wrapper td {
@apply border border-gray-300 px-4 py-2 text-left;
}
/* markdown.css */
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #d1d5db; /* Tailwind's gray-300 */
padding: 0.5rem 1rem;
}