diff --git a/endpoints/subscriptions/get.php b/endpoints/subscriptions/get.php index 9208b69..3ddddd6 100644 --- a/endpoints/subscriptions/get.php +++ b/endpoints/subscriptions/get.php @@ -24,10 +24,30 @@ if ($sort == "price" || $sort == "id") { $order = "DESC"; } - if (in_array($sort, $allowedSortCriteria)) { - $sql = "SELECT * FROM subscriptions ORDER BY $sort $order, inactive ASC"; + if (!in_array($sort, $allowedSortCriteria)) { + $sort = "next_payment"; } } + + $sql = "SELECT * FROM subscriptions WHERE 1=1"; + + if (isset($_GET['category']) && $_GET['category'] != "") { + $category = $_GET['category']; + $sql .= " AND category_id = $category"; + } + + if (isset($_GET['payment']) && $_GET['payment'] != "") { + $payment = $_GET['payment']; + $sql .= " AND payment_method_id = $payment"; + } + + if (isset($_GET['member']) && $_GET['member'] != "") { + $member = $_GET['member']; + $sql .= " AND payer_user_id = $member"; + } + + $sql .= " ORDER BY $sort $order, inactive ASC"; + $result = $db->query($sql); if ($result) { @@ -76,16 +96,16 @@ if (count($subscriptions) == 0) { ?> -
- <?= translate('empty_page', $i18n) ?> -

- -

- -
+
+

+ +

+ + <?= translate('empty_page', $i18n) ?> +
"Keine Abonnements hinzugefügt", "add_first_subscription" => "Erstes Abonnement hinzufügen", 'new_subscription' => "Neues Abonnement", + 'search' => "Suche", 'sort' => "Sortieren", 'name' => "Bezeichnung", 'last_added' => "Zuletzt hinzugefügt", @@ -47,6 +48,8 @@ $i18n = [ "years" => "Jahre", "external_url" => "Externe URL besuchen", "empty_page" => "Leere Seite", + "clear_filters" => "Filter zurücksetzen", + "no_matching_subscriptions" => "Keine passenden Abonnements gefunden", // Subscription form "add_subscription" => "Abonnement hinzufügen", "edit_subscription" => "Abonnement editieren", diff --git a/includes/i18n/el.php b/includes/i18n/el.php index 0a32c7c..a94740c 100644 --- a/includes/i18n/el.php +++ b/includes/i18n/el.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Δεν υπάρχουν καταχωρημένες συνδρομές", "add_first_subscription" => "Προσθήκη πρώτης συνδρομής", 'new_subscription' => "Νέα συνδρομή", + 'search' => "Αναζήτηση", 'sort' => "Ταξινόμηση", 'name' => "Όνομα", 'last_added' => "Τελευταία προσθήκη", @@ -47,6 +48,8 @@ $i18n = [ "years" => "χρόνια", "external_url" => "Επίσκεψη εξωτερικού συνδέσμου", "empty_page" => "Κενή σελίδα", + "clear_filters" => "Καθαρισμός φίλτρων", + "no_matching_subscriptions" => "Δεν υπάρχουν συνδρομές που ταιριάζουν με τα φίλτρα σου", // Subscription form "add_subscription" => "Προσθήκη συνδρομής", "edit_subscription" => "Επεξεργασία συνδρομής", diff --git a/includes/i18n/en.php b/includes/i18n/en.php index 9ae7b9a..c6e2412 100644 --- a/includes/i18n/en.php +++ b/includes/i18n/en.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "You don't have any subscriptions yet", "add_first_subscription" => "Add first subscription", 'new_subscription' => "New Subscription", + 'search' => "Search", 'sort' => "Sort", 'name' => "Name", 'last_added' => "Last Added", @@ -47,6 +48,8 @@ $i18n = [ "years" => "years", "external_url" => "Visit External URL", "empty_page" => "Empty Page", + "clear_filters" => "Clear Filters", + "no_matching_subscriptions" => "No matching subscriptions", // Subscription form "add_subscription" => "Add subscription", "edit_subscription" => "Edit subscription", diff --git a/includes/i18n/es.php b/includes/i18n/es.php index ca7e063..c18119d 100644 --- a/includes/i18n/es.php +++ b/includes/i18n/es.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Aún no tienes ninguna suscripción", "add_first_subscription" => "Añadir primera suscripción", 'new_subscription' => "Nueva Suscripción", + 'search' => "Buscar", 'sort' => "Ordenar", 'name' => "Nombre", 'last_added' => "Última Añadida", @@ -47,6 +48,8 @@ $i18n = [ "years" => "años", "external_url" => "Visitar URL Externa", "empty_page" => "Página Vacía", + "clear_filters" => "Limpiar Filtros", + "no_matching_subscriptions" => "No hay suscripciones que coincidan con los filtros", // Subscription form "add_subscription" => "Añadir suscripción", "edit_subscription" => "Editar suscripción", diff --git a/includes/i18n/fr.php b/includes/i18n/fr.php index 7725569..9c9f059 100644 --- a/includes/i18n/fr.php +++ b/includes/i18n/fr.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Vous n'avez pas encore d'abonnement", "add_first_subscription" => "Ajoutez le premier abonnement", 'new_subscription' => "Nouvel abonnement", + 'search' => "Rechercher", 'sort' => "Trier", 'name' => "Nom", 'last_added' => "Dernier ajouté", @@ -47,6 +48,8 @@ $i18n = [ "years" => "années", "external_url" => "Visiter l'URL externe", "empty_page" => "Page vide", + "clear_filters" => "Effacer les filtres", + "no_matching_subscriptions" => "Aucun abonnement ne correspond à vos critères de recherche", // Formulaire d'abonnement "add_subscription" => "Ajouter un abonnement", "edit_subscription" => "Modifier l'abonnement", diff --git a/includes/i18n/jp.php b/includes/i18n/jp.php index 7ea61ec..06c834c 100644 --- a/includes/i18n/jp.php +++ b/includes/i18n/jp.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "まだ定期購入がありません", "add_first_subscription" => "最初の定期購入を追加する", 'new_subscription' => "新しい定期購入", + 'search' => "検索", 'sort' => "並べ替え", 'name' => "名前", 'last_added' => "最終追加日", @@ -47,6 +48,8 @@ $i18n = [ "years" => "年毎", "external_url" => "外部URLにアクセス", "empty_page" => "空のページ", + "clear_filters" => "フィルタをクリア", + "no_matching_subscriptions" => "一致する定期購入がありません", // Subscription form "add_subscription" => "定期購入の追加", "edit_subscription" => "定期購入の編集", diff --git a/includes/i18n/pt.php b/includes/i18n/pt.php index f6d18a5..a73aec7 100644 --- a/includes/i18n/pt.php +++ b/includes/i18n/pt.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Ainda não tem subscrições", "add_first_subscription" => "Adicionar primeira subscrição", 'new_subscription' => "Nova Subscrição", + 'search' => "Pesquisar", 'sort' => "Ordenar", 'name' => "Nome", 'last_added' => "Última Adicionada", @@ -47,6 +48,8 @@ $i18n = [ "years" => "anos", "external_url" => "Visitar URL Externo", "empty_page" => "Página Vazia", + "clear_filters" => "Limpar Filtros", + "no_matching_subscriptions" => "Sem subscrições correspondentes", // Subscription form "add_subscription" => "Adicionar subscrição", "edit_subscription" => "Modificar subscrição", diff --git a/includes/i18n/pt_br.php b/includes/i18n/pt_br.php index b980cb0..f9c14a3 100644 --- a/includes/i18n/pt_br.php +++ b/includes/i18n/pt_br.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Você ainda não tem nenhuma assinatura", "add_first_subscription" => "Adicionar a primeira assinatura", 'new_subscription' => "Nova assinatura", + 'search' => "Pesquisar", 'sort' => "Ordenar", 'name' => "Nome", 'last_added' => "Última adicionada", @@ -47,6 +48,8 @@ $i18n = [ "years" => "anos", "external_url" => "Abrir URL externa", "empty_page" => "Página vazia", + "clear_filters" => "Limpar filtros", + "no_matching_subscriptions" => "Nenhuma assinatura encontrada", // Subscription form "add_subscription" => "Adicionar assinatura", "edit_subscription" => "Editar assinatura", diff --git a/includes/i18n/tr.php b/includes/i18n/tr.php index 340e83d..fa61439 100644 --- a/includes/i18n/tr.php +++ b/includes/i18n/tr.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "Henüz herhangi bir aboneliğiniz yok", "add_first_subscription" => "İlk aboneliği ekle", 'new_subscription' => "Yeni Abonelik", + 'search' => "Ara", 'sort' => "Sırala", 'name' => "İsim", 'last_added' => "Son Eklenen", @@ -47,6 +48,8 @@ $i18n = [ "years" => "yıllar", "external_url" => "Harici URL'yi Ziyaret Et", "empty_page" => "Boş Sayfa", + "clear_filters" => "Filtreleri Temizle", + "no_matching_subscriptions" => "Eşleşen abonelik bulunamadı", // Subscription form "add_subscription" => "Abonelik ekle", "edit_subscription" => "Aboneliği düzenle", diff --git a/includes/i18n/zh_cn.php b/includes/i18n/zh_cn.php index 3bfc386..50ba6e6 100644 --- a/includes/i18n/zh_cn.php +++ b/includes/i18n/zh_cn.php @@ -31,6 +31,7 @@ $i18n = [ "no_subscriptions_yet" => "您还没有任何订阅", "add_first_subscription" => "添加首个订阅", 'new_subscription' => "新订阅", + 'search' => "搜索", 'sort' => "排序", 'name' => "名称", 'last_added' => "创建时间", @@ -50,6 +51,8 @@ $i18n = [ "years" => "年", "external_url" => "访问外部链接", "empty_page" => "空白页面", + "clear_filters" => "清除筛选", + "no_matching_subscriptions" => "没有匹配的订阅", // 订阅表单 "add_subscription" => "添加订阅", diff --git a/includes/i18n/zh_tw.php b/includes/i18n/zh_tw.php index d6896a3..7a9f305 100644 --- a/includes/i18n/zh_tw.php +++ b/includes/i18n/zh_tw.php @@ -28,6 +28,7 @@ $i18n = [ "no_subscriptions_yet" => "目前還沒有任何訂閱", "add_first_subscription" => "新增第一個訂閱", 'new_subscription' => "新訂閱", + 'search' => "搜尋", 'sort' => "排序", 'name' => "名稱", 'last_added' => "建立時間", @@ -47,6 +48,8 @@ $i18n = [ "years" => "年", "external_url" => "檢視外部連結", "empty_page" => "空白頁面", + "clear_filters" => "清除篩選", + "no_matching_subscriptions" => "沒有符合的訂閱", // 訂閱表單 "add_subscription" => "新增訂閱", "edit_subscription" => "編輯訂閱", diff --git a/includes/list_subscriptions.php b/includes/list_subscriptions.php index 35ccf6b..6cf51b8 100644 --- a/includes/list_subscriptions.php +++ b/includes/list_subscriptions.php @@ -98,7 +98,7 @@ $currentPaymentMethodId = $subscription['payment_method_id']; } ?> -
+
diff --git a/includes/version.php b/includes/version.php index 0ce4cf0..c663205 100644 --- a/includes/version.php +++ b/includes/version.php @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/index.php b/index.php index f5d1420..5bdfc0c 100644 --- a/index.php +++ b/index.php @@ -50,20 +50,106 @@ -
- -
-
    -
  • onClick="setSortOption('name')" id="sort-name">
  • -
  • onClick="setSortOption('id')" id="sort-id">
  • -
  • onClick="setSortOption('price')" id="sort-price">
  • -
  • onClick="setSortOption('next_payment')" id="sort-next_payment">
  • -
  • onClick="setSortOption('payer_user_id')" id="sort-payer_user_id">
  • -
  • onClick="setSortOption('category_id')" id="sort-category_id">
  • -
  • onClick="setSortOption('payment_method_id')" id="sort-payment_method_id">
  • -
+
+ + +
+ +
+ 1) { + ?> +
+
+
+ +
+ +
+
+ + 1) { + ?> +
+
+
+ +
+ +
+
+ + 1) { + ?> +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+
    +
  • onClick="setSortOption('name')" id="sort-name">
  • +
  • onClick="setSortOption('id')" id="sort-id">
  • +
  • onClick="setSortOption('price')" id="sort-price">
  • +
  • onClick="setSortOption('next_payment')" id="sort-next_payment">
  • +
  • onClick="setSortOption('payer_user_id')" id="sort-payer_user_id">
  • +
  • onClick="setSortOption('category_id')" id="sort-category_id">
  • +
  • onClick="setSortOption('payment_method_id')" id="sort-payment_method_id">
  • +
+
diff --git a/scripts/dashboard.js b/scripts/dashboard.js index 6a162f7..36848f8 100644 --- a/scripts/dashboard.js +++ b/scripts/dashboard.js @@ -238,7 +238,17 @@ function closeLogoSearch() { function fetchSubscriptions() { const subscriptionsContainer = document.querySelector("#subscriptions"); - const getSubscriptions = "endpoints/subscriptions/get.php"; + let getSubscriptions = "endpoints/subscriptions/get.php"; + + if (activeFilters['category'] !== "") { + getSubscriptions += `?category=${activeFilters['category']}`; + } + if (activeFilters['member'] !== "") { + getSubscriptions += getSubscriptions.includes("?") ? `&member=${activeFilters['member']}` : `?member=${activeFilters['member']}`; + } + if (activeFilters['payment'] !== "") { + getSubscriptions += getSubscriptions.includes("?") ? `&payment=${activeFilters['payment']}` : `?payment=${activeFilters['payment']}`; + } fetch(getSubscriptions) .then(response => response.text()) @@ -246,8 +256,8 @@ function fetchSubscriptions() { if (data) { subscriptionsContainer.innerHTML = data; const mainActions = document.querySelector("#main-actions"); - if (data.includes("empty-page")) { - mainActions.classList.add("hidden"); + if (data.includes("no-matching-subscriptions")) { + // mainActions.classList.add("hidden"); } else { mainActions.classList.remove("hidden"); } @@ -319,4 +329,129 @@ document.addEventListener('DOMContentLoaded', function() { document.querySelector('#sort-options').addEventListener('focus', function() { isSortOptionsOpen = true; }); -}); \ No newline at end of file +}); + +function searchSubscriptions() { + const searchInput = document.querySelector("#search"); + const searchTerm = searchInput.value.trim().toLowerCase(); + + const subscriptions = document.querySelectorAll(".subscription"); + subscriptions.forEach(subscription => { + const name = subscription.getAttribute('data-name').toLowerCase(); + if (!name.includes(searchTerm)) { + subscription.classList.add("hide"); + } else { + subscription.classList.remove("hide"); + } + }); +} + +function closeSubMenus() { + var subMenus = document.querySelectorAll('.filtermenu-submenu-content'); + subMenus.forEach(subMenu => { + subMenu.classList.remove('is-open'); + }); + +} + +const activeFilters = []; +activeFilters['category'] = ""; +activeFilters['member'] = ""; +activeFilters['payment'] = ""; + +document.addEventListener("DOMContentLoaded", function() { + var filtermenu = document.querySelector('#filtermenu-button'); + filtermenu.addEventListener('click', function() { + this.parentElement.querySelector('.filtermenu-content').classList.toggle('is-open'); + closeSubMenus(); + }); + + document.addEventListener('click', function(e) { + var filtermenuContent = document.querySelector('.filtermenu-content'); + if (filtermenuContent.classList.contains('is-open')) { + var subMenus = document.querySelectorAll('.filtermenu-submenu'); + var clickedInsideSubmenu = Array.from(subMenus).some(subMenu => subMenu.contains(e.target) || subMenu === e.target); + + if (!filtermenu.contains(e.target) && !clickedInsideSubmenu) { + closeSubMenus(); + filtermenuContent.classList.remove('is-open'); + } + } + }); +}); + +function toggleSubMenu(subMenu) { + var subMenu = document.getElementById("filter-" + subMenu); + if (subMenu.classList.contains("is-open")) { + closeSubMenus(); + } else { + closeSubMenus(); + subMenu.classList.add("is-open"); + } +} + +document.querySelectorAll('.filter-item').forEach(function(item) { + item.addEventListener('click', function(e) { + const searchInput = document.querySelector("#search"); + searchInput.value = ""; + + if (this.hasAttribute('data-categoryid')) { + const categoryId = this.getAttribute('data-categoryid'); + if (activeFilters['category'] === categoryId) { + activeFilters['category'] = ""; + this.classList.remove('selected'); + } else { + activeFilters['category'] = categoryId; + Array.from(this.parentNode.children).forEach(sibling => { + sibling.classList.remove('selected'); + }); + this.classList.add('selected'); + } + } else if (this.hasAttribute('data-memberid')) { + const memberId = this.getAttribute('data-memberid'); + if (activeFilters['member'] === memberId) { + activeFilters['member'] = ""; + this.classList.remove('selected'); + } else { + activeFilters['member'] = memberId; + Array.from(this.parentNode.children).forEach(sibling => { + sibling.classList.remove('selected'); + }); + this.classList.add('selected'); + } + } else if (this.hasAttribute('data-paymentid')) { + const paymentId = this.getAttribute('data-paymentid'); + if (activeFilters['payment'] === paymentId) { + activeFilters['payment'] = ""; + this.classList.remove('selected'); + } else { + activeFilters['payment'] = paymentId; + Array.from(this.parentNode.children).forEach(sibling => { + sibling.classList.remove('selected'); + }); + this.classList.add('selected'); + } + } + + if (activeFilters['category'] !== "" || activeFilters['member'] !== "" || activeFilters['payment'] !== "") { + document.querySelector('#clear-filters').classList.remove('hide'); + } else { + document.querySelector('#clear-filters').classList.add('hide'); + } + + fetchSubscriptions(); + }); +}); + +function clearFilters() { + const searchInput = document.querySelector("#search"); + searchInput.value = ""; + activeFilters['category'] = ""; + activeFilters['member'] = ""; + activeFilters['payment'] = ""; + document.querySelectorAll('.filter-item').forEach(function(item) { + item.classList.remove('selected'); + }); + document.querySelector('#clear-filters').classList.add('hide'); + fetchSubscriptions(); +} \ No newline at end of file diff --git a/stats.php b/stats.php index 9655bc5..7e0ac55 100644 --- a/stats.php +++ b/stats.php @@ -202,89 +202,90 @@ $numberOfElements = 6;
- -
- 1) { - ?> -
-
-
- -
- -
-
- - 1) { - ?> -
-
-
- -
- -
-
- - 1) { - ?> -
-
-
- -
- -
-
- - -
-
- -
-
+ +
+ 1) { + ?> +
+
+
-
+ foreach ($members as $member) { + $selectedClass = ''; + if (isset($_GET['member']) && $_GET['member'] == $member['id']) { + $selectedClass = 'selected'; + } + ?> +
+ +
+
+ + 1) { + ?> +
+
+
+ +
+ +
+
+ + 1) { + ?> +
+
+
+ +
+ +
+
+ + +
+
+ +
+
+ +
+
diff --git a/styles/styles.css b/styles/styles.css index 0b8f425..8437e01 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -163,7 +163,7 @@ main > .contain { margin: 0px 0px 20px 0px; display: flex; flex-direction: row; - justify-content: end; + justify-content: space-between; gap: 16px; flex-wrap: wrap; position: relative; @@ -176,6 +176,7 @@ main > .contain { @media (max-width: 768px) { .main-actions { justify-content: space-between; + flex-direction: column-reverse; } } @@ -187,12 +188,13 @@ main > .contain { font-weight: 500; text-align: center; vertical-align: middle; + justify-content: center; cursor: pointer; user-select: none; color: #fff; border: 1px solid #007bff; background-color: #007bff; - padding: 14px 30px; + padding: 15px 30px; font-size: 1rem; border-radius: 8px; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; @@ -208,6 +210,28 @@ main > .contain { padding: 14px 20px; } +.top-actions { + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; + width: auto; +} + +.top-actions .search { + flex-grow: 1; +} + +.top-actions > .search > .search-icon { + float: right; + right: 15px; + margin-top: -35px; + position: relative; + z-index: 2; + color: #007bff; + font-size: 20px; +} + .subscriptions { display: flex; flex-direction: column; @@ -229,6 +253,10 @@ main > .contain { cursor: pointer; } +.subscription.hide { + display: none; +} + .subscription.inactive { opacity: 0.6; } @@ -380,7 +408,8 @@ main > .contain { margin-right: 0px;; } -.empty-page { +.empty-page, +.no-matching-subscriptions { display: block; max-width: 90%; margin: auto; @@ -388,15 +417,25 @@ main > .contain { font-size: 20px; } -.empty-page > img { +.empty-page > img, +.no-matching-subscriptions > img { max-width: 100%; } +.no-matching-subscriptions > img { + margin-top: 30px; +} + .empty-page > p { margin: 5px 0px 40px 0px; } -.empty-page > button { +.no-matching-subscriptions > p { + margin: 30px 0px 40px 0px; +} + +.empty-page > button, +.no-matching-subscriptions > button { margin: 0px auto; } @@ -1378,7 +1417,13 @@ input[type="checkbox"] { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); z-index: 3; overflow: hidden; - margin-top: 4px; + margin-top: 6px; +} + +@media (max-width: 354px) { + .on-dashboard .filtermenu-content { + right: -94px; + } } .filtermenu-content.is-open { @@ -1395,6 +1440,10 @@ input[type="checkbox"] { user-select: none; } +.filtermenu-content .filtermenu-submenu.hide { + display: none; +} + .filtermenu-content .filtermenu-submenu:last-of-type .filter-title { border-bottom: none; }