112 lines
5 KiB
JavaScript
112 lines
5 KiB
JavaScript
// Production - The Idle Game: B2B Procurement Contracts Board
|
|
|
|
import { getProduct } from '../data/products.js';
|
|
import { getClient } from '../data/contracts.js';
|
|
import { fmtCurrency, fmtNum, fmtTime } from '../core/formatters.js';
|
|
|
|
export function renderContractsBoard(container, state, onAction) {
|
|
const contracts = state.activeContracts || [];
|
|
|
|
let html = `
|
|
<div class="contracts-header-pane">
|
|
<div class="contracts-header-info">
|
|
<h2 class="view-title">
|
|
<iconify-icon icon="lucide:scroll-text" class="title-icon"></iconify-icon>
|
|
B2B Corporate Procurement Tenders
|
|
</h2>
|
|
<p class="view-subtitle">
|
|
Supply high-priority commodity shipments to corporate clients for massive cash injections and corporate prestige.
|
|
</p>
|
|
</div>
|
|
<div class="contracts-meta-badge">
|
|
<span>Completed Tenders:</span>
|
|
<strong>${state.stats.contractsCompleted || 0}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="contracts-grid">
|
|
`;
|
|
|
|
if (contracts.length === 0) {
|
|
html += `
|
|
<div class="empty-contracts-card">
|
|
<iconify-icon icon="lucide:clock" class="empty-icon"></iconify-icon>
|
|
<h3>Awaiting New Tenders</h3>
|
|
<p>Corporate procurement agents are preparing new commodity RFPs...</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
for (const c of contracts) {
|
|
const client = getClient(c.clientId);
|
|
const product = getProduct(c.productId);
|
|
const currentStock = state.products[c.productId]?.stock || 0;
|
|
const canFulfill = currentStock >= c.targetAmount;
|
|
const progressPct = Math.min(100, Math.floor((currentStock / c.targetAmount) * 100));
|
|
|
|
html += `
|
|
<div class="contract-card ${canFulfill ? 'ready-to-fulfill' : ''}">
|
|
<div class="contract-client-row">
|
|
<div class="client-brand">
|
|
<div class="client-avatar">
|
|
<iconify-icon icon="${client.icon}"></iconify-icon>
|
|
</div>
|
|
<div>
|
|
<h4 class="client-name">${client.name}</h4>
|
|
<span class="client-badge">${client.badge}</span>
|
|
</div>
|
|
</div>
|
|
<div class="contract-timer font-mono ${c.remainingSeconds < 60 ? 'timer-urgent' : ''}">
|
|
<iconify-icon icon="lucide:timer"></iconify-icon>
|
|
<span>${fmtTime(c.remainingSeconds)}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="contract-body">
|
|
<h3 class="contract-title">${c.title}</h3>
|
|
<div class="contract-req-row">
|
|
<div class="contract-req-item">
|
|
<span class="req-label">Required Commodity:</span>
|
|
<div class="req-val-wrap">
|
|
<iconify-icon icon="${product?.icon || 'lucide:box'}" style="color: ${product?.color || '#2563eb'};"></iconify-icon>
|
|
<strong>${product?.name}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="contract-req-progress font-mono">
|
|
${fmtNum(currentStock, 0)} / <strong>${fmtNum(c.targetAmount, 0)}</strong> ${product?.unit || 'units'}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="contract-progress-track">
|
|
<div class="contract-progress-fill" style="width: ${progressPct}%;"></div>
|
|
</div>
|
|
|
|
<div class="contract-payout-box">
|
|
<div class="payout-col">
|
|
<span class="payout-label">Contract Payout:</span>
|
|
<span class="payout-val font-mono">${fmtCurrency(c.rewardCash)}</span>
|
|
</div>
|
|
<div class="payout-col">
|
|
<span class="payout-label">Corporate Rep:</span>
|
|
<span class="payout-val font-mono">+${c.rewardRep} Rep</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="contract-footer">
|
|
<button class="btn-primary-corp btn-fulfill-contract" data-uid="${c.uid}" ${canFulfill ? '' : 'disabled'}>
|
|
<iconify-icon icon="lucide:check-check"></iconify-icon>
|
|
<span>${canFulfill ? 'Deliver Shipment & Collect Payout' : `Fulfill Tender (Need ${fmtNum(c.targetAmount - currentStock, 0)} more)`}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
html += `</div>`;
|
|
container.innerHTML = html;
|
|
|
|
container.querySelectorAll('.btn-fulfill-contract').forEach(btn => {
|
|
btn.onclick = () => onAction({ type: 'FULFILL_CONTRACT', uid: btn.dataset.uid });
|
|
});
|
|
}
|