Cloudron makes it easy to run web apps like WordPress, Nextcloud, GitLab on your server. Find out more or install now.


Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Bookmarks
  • Search
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo

Cloudron Forum

Apps | Demo | Docs | Install
  1. Cloudron Forum
  2. Support
  3. Cloudron 9: UI Issues in the system and email eventlog

Cloudron 9: UI Issues in the system and email eventlog

Scheduled Pinned Locked Moved Unsolved Support
user interfaceeventlog
5 Posts 3 Posters 47 Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • milian.hackradtM Offline
    milian.hackradtM Offline
    milian.hackradt
    wrote last edited by joseph
    #1

    I ran into two issues with the eventlog pages (/system-eventlog and /email-eventlog).

    When search results don't fill the result element enough to create a scrollbar, infinite scroll breaks. If you search for something that returns only a few results, there's no scrollbar and no way to easily trigger loading more entries. You can technically work around this by expanding detail rows until a scrollbar appears and then scroll down to trigger the process of loading the next entries.

    The second issue is a pagination bug in the email-eventlog. If you've scrolled down before searching, the new search keeps the old page number instead of resetting to page 1. You end up missing earlier entries, and the reload button also doesn't fix it. Looks like this is just a simple case of a missing reset of the page value as it is there in system-eventlog but missing in email-eventlog.

    As far as I can tell, this only affects the system-eventlog and email-eventlog pages, there might be other pages I missed though.

    I've put together two patch proposals that fix the problems. The first idea automatically fetches pages until a scrollbar shows up and the other one adds a "Load More" button users can click to fetch more entries. Both solutions add the reset of the page value in the email-eventlog.

    eventlog_auto_load.patch
    diff --git a/dashboard/src/views/EmailEventlogView.vue b/dashboard/src/views/EmailEventlogView.vue
    index 172e7d383..b404f87e0 100644
    --- a/dashboard/src/views/EmailEventlogView.vue
    +++ b/dashboard/src/views/EmailEventlogView.vue
    @@ -1,6 +1,6 @@
     <script setup>
     
    -import { ref, reactive, onMounted, watch, useTemplateRef } from 'vue';
    +import { ref, reactive, onMounted, watch, useTemplateRef, nextTick } from 'vue';
     import { Button, TextInput, MultiSelect } from '@cloudron/pankow';
     import { useDebouncedRef, prettyEmailAddresses, prettyLongDate } from '@cloudron/pankow/utils';
     import MailModel from '../models/MailModel.js';
    @@ -29,6 +29,7 @@ const eventlogContainer = useTemplateRef('eventlogContainer');
     
     async function onRefresh() {
       refreshBusy.value = true;
    +  page.value = 1;
     
       const [error, result] = await mailModel.eventlog(types.join(','), search.value, page.value, perPage.value);
       if (error) return console.error(error);
    @@ -36,15 +37,28 @@ async function onRefresh() {
       eventlogs.value = result;
     
       refreshBusy.value = false;
    +
    +  await nextTick();
    +  while (eventlogContainer.value && eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    +    const hasMore = await fetchMore();
    +    if (!hasMore) break;
    +    await nextTick();
    +  }
     }
     
     async function fetchMore() {
       page.value++;
     
       const [error, result] = await mailModel.eventlog(types.join(','), search.value, page.value, perPage.value);
    -  if (error) return console.error(error);
    +  if (error) {
    +    console.error(error);
    +    return false;
    +  }
    +
    +  if (!result || result.length === 0) return false;
     
       eventlogs.value = eventlogs.value.concat(result);
    +  return true;
     }
     
     async function onScroll(event) {
    @@ -57,10 +71,6 @@ watch(search, onRefresh);
     
     onMounted(async () => {
       await onRefresh();
    -
    -  while (eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    -    await fetchMore();
    -  }
     });
     
     </script>
    diff --git a/dashboard/src/views/EventlogView.vue b/dashboard/src/views/EventlogView.vue
    index f30712ca9..32c88a616 100644
    --- a/dashboard/src/views/EventlogView.vue
    +++ b/dashboard/src/views/EventlogView.vue
    @@ -1,6 +1,6 @@
     <script setup>
     
    -import { ref, reactive, onMounted, onUnmounted, watch, useTemplateRef } from 'vue';
    +import { ref, reactive, onMounted, onUnmounted, watch, useTemplateRef, nextTick } from 'vue';
     import { Button, TextInput, MultiSelect } from '@cloudron/pankow';
     import { useDebouncedRef, copyToClipboard, prettyLongDate, prettyShortDate } from '@cloudron/pankow/utils';
     import AppsModel from '../models/AppsModel.js';
    @@ -120,12 +120,24 @@ async function onRefresh() {
       });
     
       refreshBusy.value = false;
    +
    +  await nextTick();
    +  while (eventlogContainer.value && eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    +    const hasMore = await fetchMore();
    +    if (!hasMore) break;
    +    await nextTick();
    +  }
     }
     
     async function fetchMore() {
       page.value++;
       const [error, result] = await eventlogsModel.search(actions.join(','), search.value, page.value, perPage.value);
    -  if (error) return console.error(error);
    +  if (error) {
    +    console.error(error);
    +    return false;
    +  }
    +
    +  if (!result || result.length === 0) return false;
     
       eventlogs.value = eventlogs.value.concat(result.map(e => {
         return {
    @@ -135,6 +147,8 @@ async function fetchMore() {
           source: eventlogSource(e, e.appId ? getApp(e.appId) : null)
         };
       }));
    +
    +  return true;
     }
     
     async function onScroll(event) {
    @@ -163,10 +177,6 @@ onMounted(async () => {
       onHashChange();
       if (!search.value) {
         onRefresh();
    -
    -    while (eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    -      await fetchMore();
    -    }
       }
     });
    
    
    eventlog_button_load.patch
    diff --git a/dashboard/public/translation/da.json b/dashboard/public/translation/da.json
    index d0e748654..ceeb78690 100644
    --- a/dashboard/public/translation/da.json
    +++ b/dashboard/public/translation/da.json
    @@ -52,7 +52,9 @@
                 "users": "Brugere"
             },
             "statusEnabled": "Aktiveret",
    -        "loadingPlaceholder": "Indlæsning"
    +        "loadingPlaceholder": "Indlæsning",
    +        "loadMore": "Indlæs mere",
    +        "noMoreResults": "Ikke flere resultater"
         },
         "appstore": {
             "category": {
    diff --git a/dashboard/public/translation/de.json b/dashboard/public/translation/de.json
    index 77915f281..a10a89975 100644
    --- a/dashboard/public/translation/de.json
    +++ b/dashboard/public/translation/de.json
    @@ -61,7 +61,9 @@
                 "users": "User",
                 "groups": "Gruppen"
             },
    -        "loadingPlaceholder": "Laden"
    +        "loadingPlaceholder": "Laden",
    +        "loadMore": "Mehr laden",
    +        "noMoreResults": "Keine weiteren Ergebnisse"
         },
         "network": {
             "title": "Netzwerk",
    diff --git a/dashboard/public/translation/en.json b/dashboard/public/translation/en.json
    index 177baa39d..77f08d8bb 100644
    --- a/dashboard/public/translation/en.json
    +++ b/dashboard/public/translation/en.json
    @@ -65,6 +65,8 @@
             },
             "statusEnabled": "Enabled",
             "loadingPlaceholder": "Loading",
    +        "loadMore": "Load More",
    +        "noMoreResults": "No more results",
             "platform": {
                 "startupFailed": "Platform startup failed"
             }
    diff --git a/dashboard/public/translation/es.json b/dashboard/public/translation/es.json
    index 600bc0bd1..1bbdbb51c 100644
    --- a/dashboard/public/translation/es.json
    +++ b/dashboard/public/translation/es.json
    @@ -76,7 +76,9 @@
                 "groups": "Grupos"
             },
             "statusEnabled": "Habilitado",
    -        "loadingPlaceholder": "Cargando"
    +        "loadingPlaceholder": "Cargando",
    +        "loadMore": "Cargar más",
    +        "noMoreResults": "No hay más resultados"
         },
         "apps": {
             "searchPlaceholder": "Busca Aplicaciones",
    diff --git a/dashboard/public/translation/fr.json b/dashboard/public/translation/fr.json
    index 1bc5d7c99..6922c3bf2 100644
    --- a/dashboard/public/translation/fr.json
    +++ b/dashboard/public/translation/fr.json
    @@ -52,7 +52,9 @@
             "navbar": {
                 "users": "Utilisateurs"
             },
    -        "loadingPlaceholder": "Chargement"
    +        "loadingPlaceholder": "Chargement",
    +        "loadMore": "Charger plus",
    +        "noMoreResults": "Plus de résultats"
         },
         "users": {
             "users": {
    diff --git a/dashboard/public/translation/id.json b/dashboard/public/translation/id.json
    index d619b088e..3d67a3e0e 100644
    --- a/dashboard/public/translation/id.json
    +++ b/dashboard/public/translation/id.json
    @@ -11,6 +11,8 @@
             },
             "table": {
                 "date": "Tanggal"
    -        }
    +        },
    +        "loadMore": "Load More",
    +        "noMoreResults": "No more results"
         }
     }
    diff --git a/dashboard/public/translation/it.json b/dashboard/public/translation/it.json
    index 343972b13..f80e9c7f3 100644
    --- a/dashboard/public/translation/it.json
    +++ b/dashboard/public/translation/it.json
    @@ -24,7 +24,9 @@
                 "description": "Usa questo per applicare gli aggiornamenti di sicurezza o se hai riscontrato comportamenti inaspettati. Tutte le app e i servizi attivi attualmente su questo Cloudron saranno automaticamente riavviati quando il riavvio sarà completato.",
                 "title": "Vuoi davvero riavviare il server?"
             },
    -        "searchPlaceholder": "Cerca"
    +        "searchPlaceholder": "Cerca",
    +        "loadMore": "Carica altro",
    +        "noMoreResults": "Nessun altro risultato"
         },
         "apps": {
             "searchPlaceholder": "Cerca una App",
    diff --git a/dashboard/public/translation/ja.json b/dashboard/public/translation/ja.json
    index b05674b59..5826c28fb 100644
    --- a/dashboard/public/translation/ja.json
    +++ b/dashboard/public/translation/ja.json
    @@ -20,7 +20,9 @@
                 "cancel": "キャンセル"
             },
             "logout": "ログアウト",
    -        "offline": "Cloudronはオフラインです。再接続中…"
    +        "offline": "Cloudronはオフラインです。再接続中…",
    +        "loadMore": "さらに読み込む",
    +        "noMoreResults": "これ以上の結果はありません"
         },
         "apps": {
             "searchPlaceholder": "アプリを探す",
    diff --git a/dashboard/public/translation/nl.json b/dashboard/public/translation/nl.json
    index 9bb891d25..c418e4110 100644
    --- a/dashboard/public/translation/nl.json
    +++ b/dashboard/public/translation/nl.json
    @@ -65,6 +65,8 @@
             },
             "statusEnabled": "Ingeschakeld",
             "loadingPlaceholder": "Laden",
    +        "loadMore": "Meer laden",
    +        "noMoreResults": "Geen resultaten meer",
             "platform": {
                 "startupFailed": "Platformstart mislukt"
             }
    diff --git a/dashboard/public/translation/pl.json b/dashboard/public/translation/pl.json
    index e8e4c5876..42055f742 100644
    --- a/dashboard/public/translation/pl.json
    +++ b/dashboard/public/translation/pl.json
    @@ -50,7 +50,9 @@
                 "cancel": "Anuluj"
             },
             "logout": "Wyloguj",
    -        "offline": "Cloudron jest niedostępny. Odnawiam połączenie…"
    +        "offline": "Cloudron jest niedostępny. Odnawiam połączenie…",
    +        "loadMore": "Załaduj więcej",
    +        "noMoreResults": "Brak więcej wyników"
         },
         "apps": {
             "searchPlaceholder": "Szukaj Aplikacji",
    diff --git a/dashboard/public/translation/pt.json b/dashboard/public/translation/pt.json
    index 3d5181bc4..72d43367d 100644
    --- a/dashboard/public/translation/pt.json
    +++ b/dashboard/public/translation/pt.json
    @@ -62,7 +62,9 @@
                 "groups": "Grupos"
             },
             "statusEnabled": "Ativado",
    -        "loadingPlaceholder": "A carregar"
    +        "loadingPlaceholder": "A carregar",
    +        "loadMore": "Carregar mais",
    +        "noMoreResults": "Não há mais resultados"
         },
         "appstore": {
             "category": {
    diff --git a/dashboard/public/translation/ru.json b/dashboard/public/translation/ru.json
    index 2d07f0528..c10a1aeb0 100644
    --- a/dashboard/public/translation/ru.json
    +++ b/dashboard/public/translation/ru.json
    @@ -63,7 +63,9 @@
                 "groups": "Группы"
             },
             "statusEnabled": "Включено",
    -        "loadingPlaceholder": "Загрузка"
    +        "loadingPlaceholder": "Загрузка",
    +        "loadMore": "Загрузить ещё",
    +        "noMoreResults": "Больше нет результатов"
         },
         "appstore": {
             "category": {
    diff --git a/dashboard/public/translation/vi.json b/dashboard/public/translation/vi.json
    index 17023bca1..5b4a374f9 100644
    --- a/dashboard/public/translation/vi.json
    +++ b/dashboard/public/translation/vi.json
    @@ -54,7 +54,9 @@
             "navbar": {
                 "users": "Người dùng"
             },
    -        "loadingPlaceholder": "Đang tải"
    +        "loadingPlaceholder": "Đang tải",
    +        "loadMore": "Tải thêm",
    +        "noMoreResults": "Không còn kết quả"
         },
         "appstore": {
             "title": "Cửa hàng App",
    diff --git a/dashboard/public/translation/zh_Hans.json b/dashboard/public/translation/zh_Hans.json
    index 66bd4d730..ee7f34ef4 100644
    --- a/dashboard/public/translation/zh_Hans.json
    +++ b/dashboard/public/translation/zh_Hans.json
    @@ -204,7 +204,9 @@
             "statusEnabled": "已启用",
             "navbar": {
                 "users": "用户"
    -        }
    +        },
    +        "loadMore": "加载更多",
    +        "noMoreResults": "没有更多结果"
         },
         "appstore": {
             "title": "App Store",
    diff --git a/dashboard/src/views/EmailEventlogView.vue b/dashboard/src/views/EmailEventlogView.vue
    index 172e7d383..1387b3c65 100644
    --- a/dashboard/src/views/EmailEventlogView.vue
    +++ b/dashboard/src/views/EmailEventlogView.vue
    @@ -20,6 +20,8 @@ const availableTypes = [
     ];
     
     const refreshBusy = ref(false);
    +const loadMoreBusy = ref(false);
    +const hasMoreResults = ref(true);
     const eventlogs = ref([]);
     const search = useDebouncedRef('');
     const page = ref(1);
    @@ -29,22 +31,40 @@ const eventlogContainer = useTemplateRef('eventlogContainer');
     
     async function onRefresh() {
       refreshBusy.value = true;
    +  page.value = 1;
    +  hasMoreResults.value = true;
     
       const [error, result] = await mailModel.eventlog(types.join(','), search.value, page.value, perPage.value);
       if (error) return console.error(error);
     
       eventlogs.value = result;
    -
    +  hasMoreResults.value = result.length === perPage.value;
       refreshBusy.value = false;
     }
     
     async function fetchMore() {
    +  if (!hasMoreResults.value) return;
    +
    +  loadMoreBusy.value = true;
       page.value++;
     
       const [error, result] = await mailModel.eventlog(types.join(','), search.value, page.value, perPage.value);
    -  if (error) return console.error(error);
    +
    +  if (error) {
    +    console.error(error);
    +    loadMoreBusy.value = false;
    +    return;
    +  }
    +
    +  if (!result || result.length === 0) {
    +    hasMoreResults.value = false;
    +    loadMoreBusy.value = false;
    +    return;
    +  }
     
       eventlogs.value = eventlogs.value.concat(result);
    +  hasMoreResults.value = result.length === perPage.value;
    +  loadMoreBusy.value = false;
     }
     
     async function onScroll(event) {
    @@ -57,10 +77,6 @@ watch(search, onRefresh);
     
     onMounted(async () => {
       await onRefresh();
    -
    -  while (eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    -    await fetchMore();
    -  }
     });
     
     </script>
    @@ -134,6 +150,11 @@ onMounted(async () => {
               </tbody>
             </table>
           </div>
    +      <div style="display: flex; justify-content: center; padding: 20px;">
    +        <Button @click="fetchMore" :loading="loadMoreBusy" :disabled="!hasMoreResults">
    +          {{ hasMoreResults ? $t('main.loadMore') : $t('main.noMoreResults') }}
    +        </Button>
    +      </div>
         </div>
       </div>
     </template>
    diff --git a/dashboard/src/views/EventlogView.vue b/dashboard/src/views/EventlogView.vue
    index f30712ca9..b1588600f 100644
    --- a/dashboard/src/views/EventlogView.vue
    +++ b/dashboard/src/views/EventlogView.vue
    @@ -95,6 +95,8 @@ const availableActions = [
     ];
     
     const refreshBusy = ref(false);
    +const loadMoreBusy = ref(false);
    +const hasMoreResults = ref(true);
     const apps = ref([]);
     const eventlogs = ref([]);
     const search = useDebouncedRef('');
    @@ -106,6 +108,7 @@ const eventlogContainer = useTemplateRef('eventlogContainer');
     async function onRefresh() {
       refreshBusy.value = true;
       page.value = 1;
    +  hasMoreResults.value = true;
     
       const [error, result] = await eventlogsModel.search(actions.join(','), search.value, page.value, perPage.value);
       if (error) return console.error(error);
    @@ -119,22 +122,40 @@ async function onRefresh() {
         };
       });
     
    +  hasMoreResults.value = result.length === perPage.value;
       refreshBusy.value = false;
     }
     
     async function fetchMore() {
    +  if (!hasMoreResults.value) return;
    +
    +  loadMoreBusy.value = true;
       page.value++;
       const [error, result] = await eventlogsModel.search(actions.join(','), search.value, page.value, perPage.value);
    -  if (error) return console.error(error);
    +
    +  if (error) {
    +    console.error(error);
    +    loadMoreBusy.value = false;
    +    return;
    +  }
    +
    +  if (!result || result.length === 0) {
    +    hasMoreResults.value = false;
    +    loadMoreBusy.value = false;
    +    return;
    +  }
     
       eventlogs.value = eventlogs.value.concat(result.map(e => {
         return {
           id: Symbol(),
           raw: e,
    -      details: eventlogDetails(e, e.appId ? getApp(e.appId) : null),
    -      source: eventlogSource(e, e.appId ? getApp(e.appId) : null)
    +      details: eventlogDetails(e, e.data?.appId ? getApp(e.data.appId) : null),
    +      source: eventlogSource(e, e.data?.appId ? getApp(e.data.appId) : null)
         };
       }));
    +
    +  hasMoreResults.value = result.length === perPage.value;
    +  loadMoreBusy.value = false;
     }
     
     async function onScroll(event) {
    @@ -163,10 +184,6 @@ onMounted(async () => {
       onHashChange();
       if (!search.value) {
         onRefresh();
    -
    -    while (eventlogContainer.value.scrollHeight <= eventlogContainer.value.offsetHeight) {
    -      await fetchMore();
    -    }
       }
     });
     
    @@ -203,6 +220,11 @@ onUnmounted(() => {
               </div>
             </div>
           </div>
    +      <div style="display: flex; justify-content: center; padding: 20px;">
    +        <Button @click="fetchMore" :loading="loadMoreBusy" :disabled="!hasMoreResults">
    +          {{ hasMoreResults ? $t('main.loadMore') : $t('main.noMoreResults') }}
    +        </Button>
    +      </div>
         </div>
       </div>
     </template>
    
    
    1 Reply Last reply
    3
    • nebulonN Offline
      nebulonN Offline
      nebulon
      Staff
      wrote last edited by
      #2

      Thanks for the detailed description. The page reset on reload was an oversight, good to get that fixed.

      Still working on the refresh, when search changes and results do not fill up the view. Overall we have to rework eventlogs (both system and mail). Currently the pagination does not work well with the fetchMore(), especially when one tries to track down an issue with a combination of search and filters. It is very hard to get context around those events, as the pagination works with the query not the time of the events 😕

      milian.hackradtM 1 Reply Last reply
      2
      • robiR Offline
        robiR Offline
        robi
        wrote last edited by
        #3

        It would be interesting if for every event an agent went through all the relevant logs and pulled out the time stamped entries.

        No more hunting for logs.

        Conscious tech

        1 Reply Last reply
        0
        • nebulonN nebulon

          Thanks for the detailed description. The page reset on reload was an oversight, good to get that fixed.

          Still working on the refresh, when search changes and results do not fill up the view. Overall we have to rework eventlogs (both system and mail). Currently the pagination does not work well with the fetchMore(), especially when one tries to track down an issue with a combination of search and filters. It is very hard to get context around those events, as the pagination works with the query not the time of the events 😕

          milian.hackradtM Offline
          milian.hackradtM Offline
          milian.hackradt
          wrote last edited by milian.hackradt
          #4

          @nebulon said in Cloudron 9: UI Issues in the system and email eventlog:

          Still working on the refresh, when search changes and results do not fill up the view. Overall we have to rework eventlogs (both system and mail). Currently the pagination does not work well with the fetchMore(), especially when one tries to track down an issue with a combination of search and filters. It is very hard to get context around those events, as the pagination works with the query not the time of the events 😕

          I might be misunderstanding you, but the issue happens even without using a combination of search or filters. The email-eventlog has per_page set to 10, which isn't enough to create a scrollbar on a modern monitor with the browser being maximized, so infinite scroll can basically never work when you search for anything. The system-eventlog has the same problem on larger displays despite using per_page of 40. Maybe providing the user with a button to manually trigger the loading of more items is the way to go here? Or maybe you could just load more items "per page"?

          1 Reply Last reply
          1
          • nebulonN Offline
            nebulonN Offline
            nebulon
            Staff
            wrote last edited by
            #5

            The code actually would fetch more pages until it starts scrolling to fill up the screen, but only of course if there are enough results to show: https://git.cloudron.io/platform/box/-/commit/b53da61e7cec3c919ac11ebd65a8ccce03f7f611

            Maybe there is some other bug you are hitting, here on this screen if I change page size to 5 it fetches about 20 pages

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Bookmarks
            • Search