feature: Add contract features to dashboard page

This commit is contained in:
Keith Solomon
2026-02-11 19:14:48 -06:00
parent c935ac0dda
commit 86c58e8930
7 changed files with 587 additions and 76 deletions

332
index.php
View File

@@ -40,6 +40,10 @@ $systemWaypoints = array();
$paginatedWaypoints = array();
$marketDetails = array();
$shipyardDetails = array();
$activeContracts = array();
$pendingContracts = array();
$deliveryReadyByTradeSymbol = array();
$contractNegotiationShipSymbol = '';
$selectedShipSymbol = '';
$selectedWaypointSymbol = '';
$waypointPageSize = 15;
@@ -103,6 +107,161 @@ try {
$ships = $shipsResponse['data'] ?? $shipsResponse;
$contracts = $contractsResponse['data'] ?? $contractsResponse;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_POST['contract_action'] ) ) {
$contractAction = (string) $_POST['contract_action'];
if ($contractAction === 'fulfill_contract' ) {
$contractId = trim( (string) ( $_POST['contract_id'] ?? '' ) );
if ($contractId !== '' ) {
$client->fulfillContract( $contractId );
$storage->clearAllCache();
$statusMessage = 'Contract ' . $contractId . ' fulfilled.';
}
} elseif ($contractAction === 'deliver_contract' ) {
$contractId = trim( (string) ( $_POST['contract_id'] ?? '' ) );
if ($contractId !== '' ) {
$contractToDeliver = null;
foreach ( (array) $contracts as $contract ) {
if ((string) ( $contract['id'] ?? '' ) === $contractId ) {
$contractToDeliver = $contract;
break;
}
}
if (! is_array( $contractToDeliver ) ) {
throw new SpacetradersApiException( 'Contract not found.' );
}
$deliveries = (array) ( $contractToDeliver['terms']['deliver'] ?? array() );
$deliveryAttempts = 0;
foreach ( $deliveries as $delivery ) {
$tradeSymbol = (string) ( $delivery['tradeSymbol'] ?? '' );
$destinationSymbol = (string) ( $delivery['destinationSymbol'] ?? '' );
$unitsRequired = (int) ( $delivery['unitsRequired'] ?? 0 );
$unitsFulfilled = (int) ( $delivery['unitsFulfilled'] ?? 0 );
$unitsRemaining = max( 0, $unitsRequired - $unitsFulfilled );
if ($tradeSymbol === '' || $destinationSymbol === '' || $unitsRemaining <= 0 ) {
continue;
}
foreach ( $ships as $ship ) {
if ($unitsRemaining <= 0 ) {
break;
}
$shipSymbol = (string) ( $ship['symbol'] ?? '' );
$shipStatus = (string) ( $ship['nav']['status'] ?? '' );
$shipWaypoint = (string) ( $ship['nav']['waypointSymbol'] ?? '' );
if ($shipSymbol === '' || $shipStatus === 'IN_TRANSIT' || $shipWaypoint !== $destinationSymbol ) {
continue;
}
$shipResponse = $client->getShip( $shipSymbol );
$shipData = $shipResponse['data'] ?? array();
$currentStatus = (string) ( $shipData['nav']['status'] ?? '' );
if ($currentStatus === 'IN_TRANSIT' ) {
continue;
}
if ($currentStatus !== 'DOCKED' ) {
$client->dockShip( $shipSymbol );
}
$availableUnits = 0;
$inventory = (array) ( $shipData['cargo']['inventory'] ?? array() );
foreach ( $inventory as $item ) {
if ((string) ( $item['symbol'] ?? '' ) === $tradeSymbol ) {
$availableUnits = (int) ( $item['units'] ?? 0 );
break;
}
}
if ($availableUnits <= 0 ) {
continue;
}
$deliverUnits = min( $availableUnits, $unitsRemaining );
if ($deliverUnits <= 0 ) {
continue;
}
$client->deliverContractCargo( $contractId, $shipSymbol, $tradeSymbol, $deliverUnits );
$unitsRemaining -= $deliverUnits;
$deliveryAttempts++;
}
}
if ($deliveryAttempts > 0 ) {
$storage->clearAllCache();
$statusMessage = 'Delivered contract cargo using ' . $deliveryAttempts . ' shipment(s) for contract ' . $contractId . '.';
} else {
$errorMessage = 'No eligible cargo/ships available to deliver for contract ' . $contractId . '.';
}
}
} elseif ($contractAction === 'negotiate_contract' ) {
$negotiateShipSymbol = trim( (string) ( $_POST['negotiate_ship_symbol'] ?? '' ) );
if ($negotiateShipSymbol === '' ) {
$negotiateShipSymbol = (string) ( $ships[0]['symbol'] ?? '' );
}
if ($negotiateShipSymbol === '' ) {
throw new SpacetradersApiException( 'No available ship found to negotiate a contract.' );
}
$client->negotiateContract( $negotiateShipSymbol );
$storage->clearAllCache();
$statusMessage = 'Negotiated a new contract using ' . $negotiateShipSymbol . '.';
}
if ($statusMessage !== '' || $errorMessage !== '' ) {
$shipsResponse = $client->listMyShips();
$contractsResponse = $client->listMyContracts();
$ships = $shipsResponse['data'] ?? $shipsResponse;
$contracts = $contractsResponse['data'] ?? $contractsResponse;
}
}
$activeContracts = array_values(
array_filter(
(array) $contracts,
static function ( array $contract ): bool {
return (bool) ( $contract['accepted'] ?? false ) && ! (bool) ( $contract['fulfilled'] ?? false );
}
)
);
$pendingContracts = array_values(
array_filter(
(array) $contracts,
static function ( array $contract ): bool {
return ! (bool) ( $contract['accepted'] ?? false ) && ! (bool) ( $contract['fulfilled'] ?? false );
}
)
);
foreach ( $ships as $ship ) {
$inventory = (array) ( $ship['cargo']['inventory'] ?? array() );
foreach ( $inventory as $item ) {
$tradeSymbol = (string) ( $item['symbol'] ?? '' );
$units = (int) ( $item['units'] ?? 0 );
if ($tradeSymbol === '' || $units <= 0 ) {
continue;
}
if (! isset( $deliveryReadyByTradeSymbol[ $tradeSymbol ] ) ) {
$deliveryReadyByTradeSymbol[ $tradeSymbol ] = 0;
}
$deliveryReadyByTradeSymbol[ $tradeSymbol ] += $units;
}
}
$contractNegotiationShipSymbol = (string) ( $ships[0]['symbol'] ?? '' );
$currentSystemSymbol = '';
if (isset( $ships[0]['nav']['systemSymbol'] ) && is_string( $ships[0]['nav']['systemSymbol'] ) ) {
@@ -468,7 +627,7 @@ try {
</td>
<td class="border border-gray-300 px-4 py-2"><?php echo htmlspecialchars( ucfirst( strtolower( $ship['registration']['role'] ) ) ); ?></td>
<td class="border border-gray-300 px-4 py-2"><?php echo htmlspecialchars( $ship['frame']['name'] ); ?></td>
<td class="border border-gray-300 px-4 py-2"><?php echo htmlspecialchars( ucfirst( strtolower( $shipStatus ) ) ); ?></td>
<td class="border border-gray-300 px-4 py-2"><?php echo htmlspecialchars( formatString( $shipStatus ) ); ?></td>
<td class="border border-gray-300 px-4 py-2">
<span
class="ship-nav-timer"
@@ -488,65 +647,126 @@ try {
</table>
<h2 class="text-2xl font-bold my-4">Contracts</h2>
<div class="flex flex-col lg:flex-row gap-4">
<?php
$i = 1;
foreach ( $contracts as $contract ) :
$type = ucfirst( strtolower( str_replace( '_', ' ', $contract['type'] ) ) ) ?? null;
$delivery = ucfirst( strtolower( str_replace( '_', ' ', $contract['terms']['deliver'][0]['tradeSymbol'] ) ) ) ?? null;
$accepted = $contract['accepted'] ? 'Accepted' : false;
$fulfilled = $contract['fulfilled'] ? 'Fulfilled' : false;
if ($fulfilled) {
continue;
}
if (! $accepted && ! $fulfilled) {
$status = 'Not Accepted';
} elseif ($accepted && ! $fulfilled) {
$status = 'Accepted';
} elseif ($accepted && $fulfilled) {
$status = 'Fulfilled';
} else {
$status = 'Unknown';
}
?>
<div class="mb-4 border border-gray-300 p-4 rounded">
<h3 class="text-xl font-bold mb-2 w-full underline decoration-gray-300">Contract <?php echo $i; ?>: <?php echo htmlspecialchars( $type ); ?> - <?php echo htmlspecialchars( $delivery ); ?></h3>
<p class="mb-0 font-bold">
Delivery Details: <span class="font-normal"><?php echo number_format( $contract['terms']['deliver'][0]['unitsRequired'] ); ?> units delivered to <?php echo htmlspecialchars( $contract['terms']['deliver'][0]['destinationSymbol'] ); ?></span>
</p>
<p class="mb-2 font-bold">
Payment: <span class="font-normal"><?php echo number_format( $contract['terms']['payment']['onAccepted'] ); ?> on Accept, <?php echo number_format( $contract['terms']['payment']['onFulfilled'] ); ?> on Fulfill</span>
</p>
<p class="mb-0 font-bold">Deadline To Accept: <span class="font-normal"><?php echo htmlspecialchars( date( 'Y-m-d H:i:s', strtotime( $contract['deadlineToAccept'] ) ) ); ?></span></p>
<p class="mb-2 font-bold">Deadline: <span class="font-normal"><?php echo htmlspecialchars( date( 'Y-m-d H:i:s', strtotime( $contract['terms']['deadline'] ) ) ); ?></span></p>
<p class="mb-0 font-bold">
Status: <span class="font-normal"><?php echo htmlspecialchars( $status ); ?></span>
<?php if ($status === 'Not Accepted' ) : ?>
<span class="ml-2">
<a
href="index.php?accept_contract=<?php echo urlencode( $contract['id'] ); ?>"
class="font-normal text-blue-400 hover:underline"
>
Accept?
</a>
</span>
<?php endif; ?>
<div class="mb-6 border border-gray-600 rounded p-4">
<h3 class="text-xl font-bold mb-3">Active Contracts</h3>
<?php if (empty( $activeContracts ) ) : ?>
<p class="text-gray-300 mb-3">No active contracts.</p>
<?php if ($contractNegotiationShipSymbol !== '' ) : ?>
<form method="post">
<input type="hidden" name="contract_action" value="negotiate_contract">
<input type="hidden" name="negotiate_ship_symbol" value="<?php echo htmlspecialchars( $contractNegotiationShipSymbol ); ?>">
<button type="submit" class="px-3 py-2 bg-emerald-700 rounded hover:bg-emerald-600">
Negotiate New Contract
</button>
</form>
<p class="text-sm text-gray-400 mt-2">
Uses ship: <?php echo htmlspecialchars( $contractNegotiationShipSymbol ); ?>
</p>
<?php endif; ?>
<?php else : ?>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<?php foreach ( $activeContracts as $contract ) : ?>
<?php
$contractType = (string) ( $contract['type'] ?? '' );
$deliveries = (array) ( $contract['terms']['deliver'] ?? array() );
?>
<div class="border border-gray-500 rounded p-3">
<p><span class="font-bold">Contract:</span> <?php echo htmlspecialchars( (string) ( $contract['id'] ?? '' ) ); ?></p>
<p><span class="font-bold">Type:</span> <?php echo htmlspecialchars( ucfirst( strtolower( str_replace( '_', ' ', $contractType ) ) ) ); ?></p>
<p>
<span class="font-bold">Deadline:</span>
<?php echo htmlspecialchars( (string) date( 'Y-m-d H:i:s', strtotime( (string) ( $contract['terms']['deadline'] ?? '' ) ) ) ); ?>
</p>
<p>
<span class="font-bold">Payment:</span>
<?php echo number_format( (int) ( $contract['terms']['payment']['onAccepted'] ?? 0 ) ); ?>
+
<?php echo number_format( (int) ( $contract['terms']['payment']['onFulfilled'] ?? 0 ) ); ?>
</p>
<form method="post" class="mt-3">
<input type="hidden" name="contract_action" value="deliver_contract">
<input type="hidden" name="contract_id" value="<?php echo htmlspecialchars( (string) ( $contract['id'] ?? '' ) ); ?>">
<button type="submit" class="px-3 py-1 bg-violet-700 rounded hover:bg-violet-600">
Deliver Contract Goods
</button>
</form>
<form method="post" class="mt-2">
<input type="hidden" name="contract_action" value="fulfill_contract">
<input type="hidden" name="contract_id" value="<?php echo htmlspecialchars( (string) ( $contract['id'] ?? '' ) ); ?>">
<button type="submit" class="px-3 py-1 bg-emerald-700 rounded hover:bg-emerald-600">
Fulfill Contract
</button>
</form>
<?php if (! empty( $deliveries ) ) : ?>
<p class="mt-2 font-bold">Deliveries:</p>
<ul class="list-disc list-inside text-sm text-gray-300">
<?php foreach ( $deliveries as $delivery ) : ?>
<?php
$tradeSymbol = (string) ( $delivery['tradeSymbol'] ?? '' );
$unitsRequired = (int) ( $delivery['unitsRequired'] ?? 0 );
$unitsFulfilled = (int) ( $delivery['unitsFulfilled'] ?? 0 );
$remainingUnits = max( 0, $unitsRequired - $unitsFulfilled );
$readyUnits = (int) ( $deliveryReadyByTradeSymbol[ $tradeSymbol ] ?? 0 );
$deliverableNow = min( $readyUnits, $remainingUnits );
?>
<li>
<?php echo htmlspecialchars( $tradeSymbol ); ?>:
<?php echo number_format( $unitsFulfilled ); ?>/<?php echo number_format( $unitsRequired ); ?>
to <?php echo htmlspecialchars( (string) ( $delivery['destinationSymbol'] ?? '' ) ); ?>
| Ready: <?php echo number_format( $deliverableNow ); ?>
<?php if ($readyUnits > $deliverableNow ) : ?>
(<?php echo number_format( $readyUnits ); ?> in cargo)
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php
$i++;
endforeach;
?>
<?php endif; ?>
</div>
<?php if (! empty( $pendingContracts ) ) : ?>
<div class="mb-6 border border-gray-600 rounded p-4">
<h3 class="text-xl font-bold mb-3">Pending Contracts</h3>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<?php foreach ( $pendingContracts as $contract ) : ?>
<div class="border border-gray-500 rounded p-3">
<?php $deliveries = (array) ( $contract['terms']['deliver'] ?? array() ); ?>
<p><span class="font-bold">Contract:</span> <?php echo htmlspecialchars( (string) ( $contract['id'] ?? '' ) ); ?></p>
<p><span class="font-bold">Type:</span> <?php echo htmlspecialchars( ucfirst( strtolower( str_replace( '_', ' ', (string) ( $contract['type'] ?? '' ) ) ) ) ); ?></p>
<p><span class="font-bold">Deadline To Accept:</span> <?php echo htmlspecialchars( date( 'Y-m-d H:i:s', strtotime( (string) ( $contract['deadlineToAccept'] ?? '' ) ) ) ); ?></p>
<?php if (! empty( $deliveries ) ) : ?>
<p class="mt-2 font-bold">Requirements:</p>
<ul class="list-disc list-inside text-sm text-gray-300">
<?php foreach ( $deliveries as $delivery ) : ?>
<?php
$tradeSymbol = (string) ( $delivery['tradeSymbol'] ?? '' );
$unitsRequired = (int) ( $delivery['unitsRequired'] ?? 0 );
$unitsFulfilled = (int) ( $delivery['unitsFulfilled'] ?? 0 );
$destinationSymbol = (string) ( $delivery['destinationSymbol'] ?? '' );
?>
<li>
<?php echo htmlspecialchars( $tradeSymbol ); ?>:
<?php echo number_format( $unitsFulfilled ); ?>/<?php echo number_format( $unitsRequired ); ?>
to <?php echo htmlspecialchars( $destinationSymbol ); ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<a
href="index.php?accept_contract=<?php echo urlencode( (string) ( $contract['id'] ?? '' ) ); ?>"
class="mt-2 inline-block px-3 py-1 bg-blue-700 rounded hover:bg-blue-600"
>
Accept Contract
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<script>
(function() {
const tabs = document.querySelectorAll('.system-tab');