Files
Wallos/index.php
Miguel Ribeiro 99c30e70c8 feat: add openai compatible host for ai recommendations
feat: move update banner to the dashboard
fix: handle some ai responses that come in a different format
chore: handle deprecation of curl_close
feat: enable ai recommendations at a schedule
2026-03-23 16:24:26 +00:00

393 lines
19 KiB
PHP

<?php
require_once 'includes/header.php';
require_once 'includes/getdbkeys.php';
function formatPrice($price, $currencyCode, $currencies)
{
$formattedPrice = CurrencyFormatter::format($price, $currencyCode);
if (strstr($formattedPrice, $currencyCode)) {
$symbol = $currencyCode;
foreach ($currencies as $currency) {
if ($currency['code'] === $currencyCode) {
if ($currency['symbol'] != "") {
$symbol = $currency['symbol'];
}
break;
}
}
$formattedPrice = str_replace($currencyCode, $symbol, $formattedPrice);
}
return $formattedPrice;
}
function formatDate($date, $lang = 'en')
{
$currentYear = date('Y');
$dateYear = date('Y', strtotime($date));
// Determine the date format based on whether the year matches the current year
$dateFormat = ($currentYear == $dateYear) ? 'MMM d' : 'MMM yyyy';
// Validate the locale and fallback to 'en' if unsupported
if (!in_array($lang, ResourceBundle::getLocales(''))) {
$lang = 'en'; // Fallback to English
}
// Create an IntlDateFormatter instance for the specified language
$formatter = new IntlDateFormatter(
$lang,
IntlDateFormatter::SHORT,
IntlDateFormatter::NONE,
null,
null,
$dateFormat
);
// Format the date
$formattedDate = $formatter->format(new DateTime($date));
return $formattedDate;
}
// Get the first name of the user
$stmt = $db->prepare("SELECT username, firstname FROM user WHERE id = :userId");
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
$first_name = $user['firstname'] ?? $user['username'] ?? '';
// Fetch the next 3 enabled subscriptions up for payment
$stmt = $db->prepare("SELECT id, logo, name, price, currency_id, next_payment, inactive FROM subscriptions WHERE user_id = :userId AND next_payment >= date('now') AND inactive = 0 ORDER BY next_payment ASC LIMIT 3");
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$upcomingSubscriptions = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$upcomingSubscriptions[] = $row;
}
// Fetch enabled subscriptions with manual renewal that are overdue
$stmt = $db->prepare("SELECT id, logo, name, price, currency_id, next_payment, inactive, auto_renew FROM subscriptions WHERE user_id = :userId AND next_payment < date('now') AND auto_renew = 0 AND inactive = 0 ORDER BY next_payment ASC");
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$overdueSubscriptions = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$overdueSubscriptions[] = $row;
}
$hasOverdueSubscriptions = !empty($overdueSubscriptions);
require_once 'includes/stats_calculations.php';
// Get AI Recommendations for user
$stmt = $db->prepare("SELECT * FROM ai_recommendations WHERE user_id = :userId");
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$aiRecommendations = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$aiRecommendations[] = $row;
}
?>
<section class="contain dashboard">
<?php
if ($isAdmin && $settings['update_notification']) {
if (!is_null($settings['latest_version'])) {
$latestVersion = $settings['latest_version'];
if (version_compare($version, $latestVersion) == -1) {
?>
<div class="update-banner">
<?= translate('new_version_available', $i18n) ?>:
<span><a href="https://github.com/ellite/Wallos/releases/tag/<?= htmlspecialchars($latestVersion) ?>"
target="_blank" rel="noreferer">
<?= htmlspecialchars($latestVersion) ?>
</a></span>
</div>
<?php
}
}
}
if ($demoMode) {
?>
<div class="demo-banner">
Running in <b>Demo Mode</b>, certain actions and settings are disabled.<br>
The database will be reset every 120 minutes.
</div>
<?php
}
?>
<h1><?= translate('hello', $i18n) ?> <?= htmlspecialchars($first_name) ?></h1>
<?php
// If there are overdue subscriptions, display them
if ($hasOverdueSubscriptions) {
?>
<div class="overdue-subscriptions">
<h2><?= translate('overdue_renewals', $i18n) ?></h2>
<div class="dashboard-subscriptions-container">
<div class="dashboard-subscriptions-list">
<?php
foreach ($overdueSubscriptions as $subscription) {
$subscriptionLogo = "images/uploads/logos/" . $subscription['logo'];
$subscriptionName = htmlspecialchars($subscription['name']);
$subscriptionPrice = $subscription['price'];
$subscriptionCurrency = $subscription['currency_id'];
$subscriptionNextPayment = $subscription['next_payment'];
$subscriptionDisplayNextPayment = date('F j', strtotime($subscriptionNextPayment));
$subscriptionDisplayPrice = formatPrice($subscriptionPrice, $currencies[$subscriptionCurrency]['code'], $currencies);
?>
<div class="subscription-item">
<?php
if (empty($subscription['logo'])) {
?>
<p class="subscription-item-title"><?= $subscriptionName ?></p>
<?php
} else {
?>
<img src="<?= $subscriptionLogo ?>" alt="<?= $subscriptionName ?> logo"
class="subscription-item-logo" title="<?= $subscriptionName ?>">
<?php
}
?>
<div class="subscription-item-info">
<p class="subscription-item-date"> <?= formatDate($subscriptionDisplayNextPayment, $lang) ?>
</p>
<p class="subscription-item-price"> <?= $subscriptionDisplayPrice ?></p>
</div>
</div>
<?php
}
?>
</div>
</div>
</div>
<?php
}
?>
<div class="upcoming-subscriptions">
<h2><?= translate('upcoming_payments', $i18n) ?></h2>
<div class="dashboard-subscriptions-container">
<div class="dashboard-subscriptions-list">
<?php
if (empty($upcomingSubscriptions)) {
?>
<p><?= translate('no_upcoming_payments', $i18n) ?></p>
<?php
} else {
foreach ($upcomingSubscriptions as $subscription) {
$subscriptionLogo = "images/uploads/logos/" . $subscription['logo'];
$subscriptionName = htmlspecialchars($subscription['name']);
$subscriptionPrice = $subscription['price'];
$subscriptionCurrency = $subscription['currency_id'];
$subscriptionNextPayment = $subscription['next_payment'];
$subscriptionDisplayNextPayment = date('F j', strtotime($subscriptionNextPayment));
$subscriptionDisplayPrice = formatPrice($subscriptionPrice, $currencies[$subscriptionCurrency]['code'], $currencies);
?>
<div class="subscription-item">
<?php
if (empty($subscription['logo'])) {
?>
<p class="subscription-item-title"><?= $subscriptionName ?></p>
<?php
} else {
?>
<img src="<?= $subscriptionLogo ?>" alt="<?= $subscriptionName ?> logo"
class="subscription-item-logo" title="<?= $subscriptionName ?>">
<?php
}
?>
<div class="subscription-item-info">
<p class="subscription-item-date"> <?= formatDate($subscriptionDisplayNextPayment, $lang) ?></p>
<p class="subscription-item-price"> <?= $subscriptionDisplayPrice ?></p>
</div>
</div>
<?php
}
}
?>
</div>
</div>
<?php if (!empty($aiRecommendations)) { ?>
<div class="ai-recommendations">
<h2><?= translate('ai_recommendations', $i18n) ?></h2>
<div class="ai-recommendations-container">
<ul class="ai-recommendations-list">
<?php
foreach ($aiRecommendations as $key => $recommendation) { ?>
<li class="ai-recommendation-item" data-id="<?= $recommendation['id'] ?>">
<div class="ai-recommendation-header">
<h3>
<span><?= ($key + 1) . ". " ?></span>
<?= htmlspecialchars($recommendation['title']) ?>
</h3>
<span class="item-arrow-down fa fa-caret-down"></span>
</div>
<p class="collapsible"><?= htmlspecialchars($recommendation['description']) ?></p>
<p class="ai-recommendation-savings">
<?= htmlspecialchars($recommendation['savings']) ?>
<span>
<a href="#" class="delete-ai-recommendation" title="<?= translate('delete', $i18n) ?>">
<i class="fa fa-trash"></i>
</a>
</span>
</p>
</li>
<?php } ?>
</ul>
</div>
</div>
<?php } ?>
<?php if (isset($amountDueThisMonth) || isset($budget) || isset($budgetUsed) || isset($budgetLeft) || isset($overBudgetAmount)) { ?>
<div class="budget-subscriptions">
<h2><?= translate('your_budget', $i18n) ?></h2>
<div class="dashboard-subscriptions-container">
<div class="dashboard-subscriptions-list">
<?php if (isset($amountDueThisMonth)) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate("amount_due", $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= CurrencyFormatter::format($amountDueThisMonth, $currencies[$userData['main_currency']]['code']) ?>
</p>
</div>
</div>
<?php } ?>
<?php if (isset($budget) && $budget > 0) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate("budget", $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= formatPrice($budget, $currencies[$userData['main_currency']]['code'], $currencies) ?>
</p>
</div>
</div>
<?php } ?>
<?php if (isset($budgetUsed)) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate("budget_used", $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= number_format($budgetUsed, 2) ?>%
</p>
</div>
</div>
<?php } ?>
<?php if (isset($budgetLeft)) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate("budget_remaining", $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= formatPrice($budgetLeft, $currencies[$userData['main_currency']]['code'], $currencies) ?>
</p>
</div>
</div>
<?php } ?>
<?php if (isset($overBudgetAmount) && $overBudgetAmount > 0) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate("over_budget", $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= formatPrice($overBudgetAmount, $currencies[$userData['main_currency']]['code'], $currencies) ?>
</p>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
<?php } ?>
</div>
<?php if (isset($activeSubscriptions) && $activeSubscriptions > 0) { ?>
<div class="current-subscriptions">
<h2><?= translate('your_subscriptions', $i18n) ?></h2>
<div class="dashboard-subscriptions-container">
<div class="dashboard-subscriptions-list">
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('active_subscriptions', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value"><?= $activeSubscriptions ?></p>
</div>
</div>
<?php if (isset($totalCostPerMonth)) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('monthly_cost', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= CurrencyFormatter::format($totalCostPerMonth, $currencies[$userData['main_currency']]['code']) ?>
</p>
</div>
</div>
<?php } ?>
<?php if (isset($totalCostPerYear)) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('yearly_cost', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= CurrencyFormatter::format($totalCostPerYear, $currencies[$userData['main_currency']]['code']) ?>
</p>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
<?php } ?>
<?php if (isset($inactiveSubscriptions) && $inactiveSubscriptions > 0) { ?>
<div class="savings-subscriptions">
<h2><?= translate('your_savings', $i18n) ?></h2>
<div class="dashboard-subscriptions-container">
<div class="dashboard-subscriptions-list">
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('inactive_subscriptions', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value"><?= $inactiveSubscriptions ?></p>
</div>
</div>
<?php if (isset($totalSavingsPerMonth) && $totalSavingsPerMonth > 0) { ?>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('monthly_savings', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= CurrencyFormatter::format($totalSavingsPerMonth, $currencies[$userData['main_currency']]['code']) ?>
</p>
</div>
</div>
<div class="subscription-item thin">
<p class="subscription-item-title"><?= translate('yearly_savings', $i18n) ?></p>
<div class="subscription-item-info">
<p class="subscription-item-value">
<?= CurrencyFormatter::format($totalSavingsPerMonth * 12, $currencies[$userData['main_currency']]['code']) ?>
</p>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
<?php } ?>
</section>
<script src="scripts/dashboard.js?<?= $version ?>"></script>
<?php
require_once 'includes/footer.php';
?>