Update buy and packages pages to load real data from API
- wallet/buy.tsx: createResource fetches /api/packages?role= instead of hardcoded bundles - packages.tsx: replaced stub with real package list from API; shows type badge, price, tracecoin amount Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3209d13011
commit
bcb940f3f1
2 changed files with 201 additions and 67 deletions
|
|
@ -1,15 +1,110 @@
|
|||
import { createResource, Show, For } from 'solid-js';
|
||||
import { A } from '@solidjs/router';
|
||||
import { authState } from '~/lib/auth';
|
||||
|
||||
const API = '/api/gateway';
|
||||
|
||||
type Package = {
|
||||
id: string;
|
||||
name: string;
|
||||
role_key: string;
|
||||
package_type: string;
|
||||
tracecoins_amount: number;
|
||||
price_inr: number;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const PACKAGE_TYPE_LABELS: Record<string, string> = {
|
||||
TRACECOIN_BUNDLE: 'Tracecoin Bundle',
|
||||
JOB_POSTING: 'Job Posting',
|
||||
CONTACT_VIEWS: 'Contact Views',
|
||||
};
|
||||
|
||||
export default function DashboardPackagesPage() {
|
||||
const rc = () => authState().runtime_config;
|
||||
const role = () => rc()?.role ?? '';
|
||||
|
||||
const [packages] = createResource(role, async (roleKey) => {
|
||||
if (!roleKey) return [];
|
||||
try {
|
||||
const params = new URLSearchParams({ role: roleKey });
|
||||
const res = await fetch(`${API}/api/packages?${params}`);
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
return (Array.isArray(data) ? data : (data.packages ?? [])) as Package[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<section class="dashboard-card">
|
||||
<h1>Packages</h1>
|
||||
<p class="dashboard-muted">
|
||||
Package purchase flow is being finalized in the Rust payments module.
|
||||
</p>
|
||||
<p>
|
||||
You can review your current wallet in <A href="/dashboard/wallet">Wallet</A>.
|
||||
</p>
|
||||
</section>
|
||||
<div style={{ 'max-width': '900px' }}>
|
||||
<div style={{ 'margin-bottom': '28px' }}>
|
||||
<h1 style={{ margin: 0, 'font-size': '22px', 'font-weight': '800' }}>Packages</h1>
|
||||
<p style={{ margin: '6px 0 0', color: '#64748b', 'font-size': '14px' }}>
|
||||
Available packages for your account type.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Show when={packages.loading}>
|
||||
<p style={{ color: '#94a3b8' }}>Loading packages…</p>
|
||||
</Show>
|
||||
|
||||
<Show when={!packages.loading && (packages()?.length ?? 0) === 0}>
|
||||
<section class="dashboard-card">
|
||||
<p class="dashboard-muted">No packages available for your role at this time.</p>
|
||||
<p>
|
||||
You can manage your Tracecoins in <A href="/dashboard/wallet">Wallet</A>.
|
||||
</p>
|
||||
</section>
|
||||
</Show>
|
||||
|
||||
<Show when={!packages.loading && (packages()?.length ?? 0) > 0}>
|
||||
<div style={{ display: 'grid', 'grid-template-columns': 'repeat(auto-fill, minmax(260px, 1fr))', gap: '16px' }}>
|
||||
<For each={packages()}>
|
||||
{(pkg) => (
|
||||
<div class="table-card" style={{ padding: '24px', display: 'flex', 'flex-direction': 'column', gap: '12px' }}>
|
||||
<div>
|
||||
<span style={{
|
||||
'font-size': '11px',
|
||||
'font-weight': '700',
|
||||
'text-transform': 'uppercase',
|
||||
color: '#fd6116',
|
||||
'letter-spacing': '0.06em',
|
||||
}}>
|
||||
{PACKAGE_TYPE_LABELS[pkg.package_type] ?? pkg.package_type}
|
||||
</span>
|
||||
<h3 style={{ margin: '4px 0 0', 'font-size': '16px', 'font-weight': '800' }}>{pkg.name}</h3>
|
||||
</div>
|
||||
|
||||
<Show when={pkg.description}>
|
||||
<p style={{ margin: 0, color: '#64748b', 'font-size': '13px' }}>{pkg.description}</p>
|
||||
</Show>
|
||||
|
||||
<div style={{ display: 'flex', 'align-items': 'baseline', gap: '8px' }}>
|
||||
<span style={{ 'font-size': '28px', 'font-weight': '800' }}>
|
||||
₹{(pkg.price_inr / 100).toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Show when={pkg.tracecoins_amount > 0}>
|
||||
<p style={{ margin: 0, 'font-size': '13px', color: '#64748b' }}>
|
||||
🪙 {pkg.tracecoins_amount} Tracecoins included
|
||||
</p>
|
||||
</Show>
|
||||
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
style={{ 'margin-top': 'auto' }}
|
||||
onClick={() => alert('Payment gateway integration coming soon.')}
|
||||
>
|
||||
Buy Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,34 @@
|
|||
import { For } from 'solid-js';
|
||||
import { createResource, Show, For } from 'solid-js';
|
||||
import { authState } from '~/lib/auth';
|
||||
|
||||
const API = '/api/gateway';
|
||||
|
||||
type Package = {
|
||||
id: string;
|
||||
name: string;
|
||||
role_key: string;
|
||||
package_type: string;
|
||||
tracecoins_amount: number;
|
||||
price_inr: number;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export default function BuyTracecoins() {
|
||||
const rc = () => authState().runtime_config;
|
||||
const role = () => rc()?.role ?? '';
|
||||
|
||||
const BUNDLES = [
|
||||
{ id: 'basic', name: 'Basic Pack', coins: 100, price: 500, popular: false, description: 'Perfect for beginners' },
|
||||
{ id: 'pro', name: 'Professional Pack', coins: 300, price: 1200, popular: true, description: 'Best value for active pros' },
|
||||
{ id: 'elite', name: 'Elite Pack', coins: 1000, price: 3500, popular: false, description: 'Power user savings' },
|
||||
];
|
||||
const [packages] = createResource(role, async (roleKey) => {
|
||||
if (!roleKey) return [];
|
||||
try {
|
||||
const params = new URLSearchParams({ role: roleKey });
|
||||
const res = await fetch(`${API}/api/packages?${params}`);
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
return (Array.isArray(data) ? data : (data.packages ?? [])) as Package[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ 'max-width': '1000px' }}>
|
||||
|
|
@ -19,59 +39,78 @@ export default function BuyTracecoins() {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', 'grid-template-columns': 'repeat(auto-fit, minmax(300px, 1fr))', gap: '20px' }}>
|
||||
<For each={BUNDLES}>
|
||||
{(bundle) => (
|
||||
<div
|
||||
class="table-card"
|
||||
style={{
|
||||
padding: '32px',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
'flex-direction': 'column',
|
||||
'align-items': 'center',
|
||||
border: bundle.popular ? '2px solid #fd6116' : '1px solid rgba(16, 11, 47, 0.08)'
|
||||
}}
|
||||
>
|
||||
{bundle.popular && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '-12px',
|
||||
background: '#fd6116',
|
||||
color: '#fff',
|
||||
padding: '2px 12px',
|
||||
'border-radius': '999px',
|
||||
'font-size': '11px',
|
||||
'font-weight': '800',
|
||||
'text-transform': 'uppercase'
|
||||
}}>
|
||||
Most Popular
|
||||
<Show when={packages.loading}>
|
||||
<p style={{ color: '#94a3b8' }}>Loading packages…</p>
|
||||
</Show>
|
||||
|
||||
<Show when={!packages.loading && (packages()?.length ?? 0) === 0}>
|
||||
<div class="table-card" style={{ padding: '40px', 'text-align': 'center', color: '#94a3b8' }}>
|
||||
No packages available for your role at this time.
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={!packages.loading && (packages()?.length ?? 0) > 0}>
|
||||
<div style={{ display: 'grid', 'grid-template-columns': 'repeat(auto-fit, minmax(280px, 1fr))', gap: '20px' }}>
|
||||
<For each={packages()}>
|
||||
{(pkg, i) => {
|
||||
const isPopular = () => i() === 1;
|
||||
return (
|
||||
<div
|
||||
class="table-card"
|
||||
style={{
|
||||
padding: '32px',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
'flex-direction': 'column',
|
||||
'align-items': 'center',
|
||||
border: isPopular() ? '2px solid #fd6116' : '1px solid rgba(16, 11, 47, 0.08)',
|
||||
}}
|
||||
>
|
||||
<Show when={isPopular()}>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '-12px',
|
||||
background: '#fd6116',
|
||||
color: '#fff',
|
||||
padding: '2px 12px',
|
||||
'border-radius': '999px',
|
||||
'font-size': '11px',
|
||||
'font-weight': '800',
|
||||
'text-transform': 'uppercase',
|
||||
}}>
|
||||
Most Popular
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<h3 style={{ margin: '0 0 8px', 'font-size': '18px', 'font-weight': '800' }}>{pkg.name}</h3>
|
||||
<Show when={pkg.description}>
|
||||
<p style={{ margin: '0 0 24px', color: '#64748b', 'font-size': '13px', 'text-align': 'center' }}>
|
||||
{pkg.description}
|
||||
</p>
|
||||
</Show>
|
||||
|
||||
<div style={{ 'font-size': '48px', 'font-weight': '800', color: '#111827', 'margin-bottom': '4px' }}>
|
||||
🪙 {pkg.tracecoins_amount}
|
||||
</div>
|
||||
<div style={{ 'font-size': '14px', color: '#64748b', 'margin-bottom': '24px' }}>Tracecoins</div>
|
||||
|
||||
<div style={{ 'font-size': '24px', 'font-weight': '700', 'margin-bottom': '24px' }}>
|
||||
₹{(pkg.price_inr / 100).toLocaleString('en-IN')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
style={{ width: '100%', 'padding-top': '14px', 'padding-bottom': '14px' }}
|
||||
onClick={() => alert('Payment gateway integration coming soon.')}
|
||||
>
|
||||
Purchase Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h3 style={{ margin: '0 0 8px', 'font-size': '18px', 'font-weight': '800' }}>{bundle.name}</h3>
|
||||
<p style={{ margin: '0 0 24px', color: '#64748b', 'font-size': '13px', 'text-align': 'center' }}>{bundle.description}</p>
|
||||
|
||||
<div style={{ 'font-size': '48px', 'font-weight': '800', color: '#111827', 'margin-bottom': '4px' }}>
|
||||
🪙 {bundle.coins}
|
||||
</div>
|
||||
<div style={{ 'font-size': '14px', color: '#64748b', 'margin-bottom': '24px' }}>Tracecoins</div>
|
||||
|
||||
<div style={{ 'font-size': '24px', 'font-weight': '700', 'margin-bottom': '24px' }}>
|
||||
₹{(bundle.price).toLocaleString('en-IN')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
style={{ width: '100%', 'padding-top': '14px', 'padding-bottom': '14px' }}
|
||||
onClick={() => alert('Payment gateway integration coming soon.')}
|
||||
>
|
||||
Purchase Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="table-card" style={{ 'margin-top': '40px', padding: '24px', background: '#f8fafc' }}>
|
||||
<h4 style={{ margin: '0 0 12px', 'font-size': '15px', 'font-weight': '700' }}>Safe & Secure Payments</h4>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue