diff --git a/README.md b/README.md index bf51ff148..d55352435 100644 --- a/README.md +++ b/README.md @@ -190,5 +190,5 @@ I strongly believe in the importance of open source software and the collaborati ## Links - The author: [henrique.pt](https://henrique.pt) -- Wallos Landinpage: [wallosapp.com](https://wallosapp.com) +- Wallos Landingpage: [wallosapp.com](https://wallosapp.com) - Join the conversation: [Discord Server](https://discord.gg/anex9GUrPW) \ No newline at end of file diff --git a/about.php b/about.php index 8ba26f31f..8929afa92 100644 --- a/about.php +++ b/about.php @@ -78,6 +78,15 @@

+

+ Icons by icons8: + + https://icons8.com/ + + + + +

diff --git a/api/subscriptions/get_monthly_cost.php b/api/subscriptions/get_monthly_cost.php new file mode 100644 index 000000000..e0e7f4bc5 --- /dev/null +++ b/api/subscriptions/get_monthly_cost.php @@ -0,0 +1,151 @@ + false, + "title" => "Missing parameters" + ]; + echo json_encode($response); + exit; + } + + $month = $_REQUEST['month']; + $year = $_REQUEST['year']; + $apiKey = $_REQUEST['api_key']; + + $sql = "SELECT * FROM user WHERE api_key = :apiKey"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':apiKey', $apiKey); + $result = $stmt->execute(); + $user = $result->fetchArray(SQLITE3_ASSOC); + + $sql = "SELECT * FROM last_exchange_update"; + $result = $db->query($sql); + $lastExchangeUpdate = $result->fetchArray(SQLITE3_ASSOC); + + $userId = $user['id']; + $userCurrencyId = $user['main_currency']; + $needsCurrencyConversion = false; + $canConvertCurrency = empty($lastExchangeUpdate['date']) ? false : true; + + $sql = "SELECT * FROM currencies WHERE id = :currencyId"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':currencyId', $userCurrencyId); + $result = $stmt->execute(); + $currency = $result->fetchArray(SQLITE3_ASSOC); + $currency_code = $currency['code']; + $currency_symbol = $currency['symbol']; + + + $title = date('F Y', strtotime($year . '-' . $month . '-01')); + $monthlyCost = 0; + $notes = []; + + $sql = "SELECT * FROM subscriptions WHERE user_id = :userId AND inactive = 0"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':userId', $userId); + $result = $stmt->execute(); + $subscriptions = []; + while ($subscription = $result->fetchArray(SQLITE3_ASSOC)) { + $subscriptions[] = $subscription; + if ($subscription['currency_id'] !== $userCurrencyId) { + $needsCurrencyConversion = true; + } + } + + if ($needsCurrencyConversion) { + if (!$canConvertCurrency) { + $notes[] = "You are using multiple currencies, but the exchange rates have not been updated yet. Please check your Fixer API Key."; + } else { + $sql = "SELECT * FROM currencies WHERE user_id = :userId"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':userId', $userId); + $result = $stmt->execute(); + $currencies = []; + while ($currency = $result->fetchArray(SQLITE3_ASSOC)) { + $currencies[$currency['id']] = $currency['rate']; + } + } + } + + // Calculate the monthly cost based on the next_payment_date, payment cycle, and payment frequency + foreach ($subscriptions as $subscription) { + $nextPaymentDate = strtotime($subscription['next_payment']); + $cycle = $subscription['cycle']; // Integer from 1 to 4 + $frequency = $subscription['frequency']; + + // Determine the strtotime increment string based on cycle + switch ($cycle) { + case 1: // Days + $incrementString = "+{$frequency} days"; + break; + case 2: // Weeks + $incrementString = "+{$frequency} weeks"; + break; + case 3: // Months + $incrementString = "+{$frequency} months"; + break; + case 4: // Years + $incrementString = "+{$frequency} years"; + break; + default: + $incrementString = "+{$frequency} months"; // Default case, if needed + } + + // Calculate the start of the month + $startOfMonth = strtotime($year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01'); + + // Find the first payment date of the month by moving backwards + $startDate = $nextPaymentDate; + while ($startDate > $startOfMonth) { + $startDate = strtotime("-" . $incrementString, $startDate); + } + + // Calculate the monthly cost + for ($date = $startDate; $date <= strtotime("+1 month", $startOfMonth); $date = strtotime($incrementString, $date)) { + if (date('Y-m', $date) == $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT)) { + $price = $subscription['price']; + if ($userCurrencyId !== $subscription['currency_id']) { + $price *= $currencies[$userCurrencyId] / $currencies[$subscription['currency_id']]; + } + $monthlyCost += $price; + } + } + } + + $formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY); + $localizedMonthlyCost = $formatter->formatCurrency($monthlyCost, $currency_code); + + echo json_encode([ + 'success' => true, + 'title' => $title, + 'monthly_cost' => number_format($monthlyCost, 2), + 'localized_monthly_cost' => $localizedMonthlyCost, + 'currency_code' => $currency_code, + 'currency_symbol' => $currency_symbol, + 'notes' => $notes + ], JSON_UNESCAPED_UNICODE); + +} +?> \ No newline at end of file diff --git a/endpoints/user/regenerateapikey.php b/endpoints/user/regenerateapikey.php new file mode 100644 index 000000000..3034d12d3 --- /dev/null +++ b/endpoints/user/regenerateapikey.php @@ -0,0 +1,40 @@ + false, + "message" => translate('session_expired', $i18n) + ])); +} + +if ($_SERVER["REQUEST_METHOD"] === "POST") { + $postData = file_get_contents("php://input"); + $data = json_decode($postData, true); + + $apiKey = bin2hex(random_bytes(32)); + + $sql = "UPDATE user SET api_key = :apiKey WHERE id = :userId"; + $stmt = $db->prepare($sql); + $stmt->bindValue(':apiKey', $apiKey, SQLITE3_TEXT); + $stmt->bindValue(':userId', $userId, SQLITE3_TEXT); + $result = $stmt->execute(); + + if ($result) { + $response = [ + "success" => true, + "message" => translate('user_details_saved', $i18n), + "apiKey" => $apiKey + ]; + echo json_encode($response); + } else { + $response = [ + "success" => false, + "message" => translate('error_updating_user_data', $i18n) + ]; + echo json_encode($response); + } + +} + +?> \ No newline at end of file diff --git a/images/siteicons/svg/mobile-menu/calendar.php b/images/siteicons/svg/mobile-menu/calendar.php new file mode 100644 index 000000000..cc9b50d5b --- /dev/null +++ b/images/siteicons/svg/mobile-menu/calendar.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/siteicons/svg/mobile-menu/home.php b/images/siteicons/svg/mobile-menu/home.php new file mode 100644 index 000000000..af054b52f --- /dev/null +++ b/images/siteicons/svg/mobile-menu/home.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/siteicons/svg/mobile-menu/profile.php b/images/siteicons/svg/mobile-menu/profile.php new file mode 100644 index 000000000..831ce536d --- /dev/null +++ b/images/siteicons/svg/mobile-menu/profile.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/siteicons/svg/mobile-menu/settings.php b/images/siteicons/svg/mobile-menu/settings.php new file mode 100644 index 000000000..8aa56fd95 --- /dev/null +++ b/images/siteicons/svg/mobile-menu/settings.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/siteicons/svg/mobile-menu/statistics.php b/images/siteicons/svg/mobile-menu/statistics.php new file mode 100644 index 000000000..94306005a --- /dev/null +++ b/images/siteicons/svg/mobile-menu/statistics.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/includes/header.php b/includes/header.php index d2186556a..9adfdbc2a 100644 --- a/includes/header.php +++ b/includes/header.php @@ -165,6 +165,8 @@ function hex2rgb($hex) -
-
-

-
-
-
-

-
- > - > -
-
-
-
- -

-
- -
-
-

- - -

-
- -
-
- diff --git a/styles/dark-theme.css b/styles/dark-theme.css index 6a44c1b5e..e8b1e451d 100644 --- a/styles/dark-theme.css +++ b/styles/dark-theme.css @@ -255,18 +255,13 @@ input { background-color: #222; border-top: #111; box-shadow: 0 -2px 5px rgba(120, 120, 120, 0.1); - ; } .mobile-nav>a { - padding: 0px 10px; + color: #909090; } - .mobile-nav>a>i { - color: #777; - } - - .mobile-nav>a.active>i { + .mobile-nav>a.active { color: #f0f0F0; } } \ No newline at end of file diff --git a/styles/styles.css b/styles/styles.css index 2b4edae44..cd0cb7ea1 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -846,13 +846,18 @@ header #avatar { .account-fixer .buttons, .account-categories .buttons, .account-notifications .buttons, -.admin-form .buttons { +.admin-form .buttons, +.account-2fa .buttons { display: flex; justify-content: flex-end; align-items: center; gap: 20px; } +.account-2fa .buttons { + justify-content: flex-start; +} + .admin-form hr { margin: 20px 0px; color: var(--main-color); @@ -1677,11 +1682,15 @@ button.dark-theme-button i { @media (max-width: 768px) { .toast { - bottom: 70px; + bottom: 0px; right: 0px; left: 0px; width: 100%; } + + .mobile-navivagtion .toast { + bottom: 70px; + } } .toast.active { @@ -2583,37 +2592,34 @@ input[type="radio"]:checked+label::after { flex-direction: row; justify-content: space-around; z-index: 2; - padding: 10px 0px 20px 0px; - font-size: 28px; + padding: 7px 0px; box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); box-sizing: border-box; - align-items: baseline; + align-items: center; } - - .mobile-nav>a>i { + + .mobile-nav>a { + flex-grow: 1; + flex-shrink: 0; + text-align: center; + padding: 5px 0px 10px 0px; color: #AAA; - } - - .mobile-nav>a.active>i { - color: #202020; - } - - .mobile-nav>a.secondary, - .mobile-nav>button { - background-color: var(--main-color); - border: none; - border-radius: 35px; - padding: 3px 8px; + font-size: 10px; + text-decoration: none; + + display: flex; + flex-direction: column; + align-items: center; } - .mobile-nav>button { - border: none; - font-size: 28px; + .mobile-nav>a>svg { + width: 30px; + height: 30px; + max-width: 85%; } - - .mobile-nav>a.secondary>i, - .mobile-nav>button>i { - color: #fff; + + .mobile-nav>a.active{ + color: #202020; } .mobile-navivagtion .mobileNavigationHideOnMobile {