diff --git a/dashboard/public/translation/en.json b/dashboard/public/translation/en.json
index 3b20e92fb..d698e8d2b 100644
--- a/dashboard/public/translation/en.json
+++ b/dashboard/public/translation/en.json
@@ -680,7 +680,7 @@
}
},
"branding": {
- "title": "Branding",
+ "title": "Appearance",
"cloudronName": "Cloudron Name",
"logo": "Logo",
"footer": {
@@ -888,7 +888,7 @@
"refresh": "Refresh"
},
"settings": {
- "title": "Settings",
+ "title": "System",
"appstoreAccount": {
"title": "Cloudron.io Account",
"description": "A Cloudron.io account is used to access the App Store and manage your subscription.",
@@ -1000,7 +1000,7 @@
}
},
"system": {
- "title": "System Info",
+ "title": "Metrics",
"diskUsage": {
"title": "Disk Usage",
"mountedAt": "{{ filesystem }} mounted at {{ mountpoint }}",
diff --git a/dashboard/src/Index.vue b/dashboard/src/Index.vue
index 0b8405630..dd38e6b7f 100644
--- a/dashboard/src/Index.vue
+++ b/dashboard/src/Index.vue
@@ -15,7 +15,7 @@ import AppsView from './views/AppsView.vue';
import AppConfigureView from './views/AppConfigureView.vue';
import AppstoreView from './views/AppstoreView.vue';
import BackupsView from './views/BackupsView.vue';
-import BrandingView from './views/BrandingView.vue';
+import AppearanceView from './views/AppearanceView.vue';
import DomainsView from './views/DomainsView.vue';
import EmailDomainView from './views/EmailDomainView.vue';
import EmailMailboxesView from './views/EmailMailboxesView.vue';
@@ -27,9 +27,9 @@ import EventlogView from './views/EventlogView.vue';
import NetworkView from './views/NetworkView.vue';
import ProfileView from './views/ProfileView.vue';
import ServicesView from './views/ServicesView.vue';
-import SettingsView from './views/SettingsView.vue';
import SupportView from './views/SupportView.vue';
import SystemView from './views/SystemView.vue';
+import MetricsView from './views/MetricsView.vue';
import UserDirectorySettingsView from './views/UserDirectorySettingsView.vue';
import UserDirectoryLdapProviderView from './views/UserDirectoryLdapProviderView.vue';
import UserDirectoryOpenIdProviderView from './views/UserDirectoryOpenIdProviderView.vue';
@@ -37,11 +37,11 @@ import UsersView from './views/UsersView.vue';
import VolumesView from './views/VolumesView.vue';
const VIEWS = {
- APPS: 'apps',
APP: 'app',
+ APPEARANCE: 'appearance',
+ APPS: 'apps',
APPSTORE: 'appstore',
BACKUPS: 'backups',
- BRANDING: 'branding',
DOMAINS: 'domains',
EMAIL_DOMAIN: 'email-domain',
EMAIL_MAILBOXES: 'email-mailboxes',
@@ -50,10 +50,10 @@ const VIEWS = {
EMAIL_EVENTLOG: 'email-eventlog',
EMAIL_STATUS: 'email-status',
EVENTLOG: 'eventlog',
+ METRICS: 'metrics',
NETWORK: 'network',
PROFILE: 'profile',
SERVICES: 'services',
- SETTINGS: 'settings',
SUPPORT: 'support',
SYSTEM: 'system',
USER_DIRECTORY_SETTINGS: 'user-directory-settings',
@@ -137,8 +137,8 @@ function onHashChange() {
view.value = VIEWS.APP;
} else if (v === VIEWS.BACKUPS && profile.value.isAtLeastAdmin) {
view.value = VIEWS.BACKUPS;
- } else if (v === VIEWS.BRANDING && profile.value.isAtLeastAdmin) {
- view.value = VIEWS.BRANDING;
+ } else if (v === VIEWS.APPEARANCE && profile.value.isAtLeastAdmin) {
+ view.value = VIEWS.APPEARANCE;
} else if (v === VIEWS.DOMAINS && profile.value.isAtLeastAdmin) {
view.value = VIEWS.DOMAINS;
} else if (v === VIEWS.EMAIL_DOMAIN && profile.value.isAtLeastMailManager) {
@@ -155,14 +155,14 @@ function onHashChange() {
view.value = VIEWS.EMAIL_STATUS;
} else if (v === VIEWS.EVENTLOG && profile.value.isAtLeastAdmin) {
view.value = VIEWS.EVENTLOG;
+ } else if (v === VIEWS.METRICS && profile.value.isAtLeastAdmin) {
+ view.value = VIEWS.METRICS;
} else if (v === VIEWS.NETWORK && profile.value.isAtLeastAdmin) {
view.value = VIEWS.NETWORK;
} else if (v.indexOf(VIEWS.PROFILE) === 0) {
view.value = VIEWS.PROFILE;
} else if (v === VIEWS.SERVICES && profile.value.isAtLeastAdmin) {
view.value = VIEWS.SERVICES;
- } else if (v === VIEWS.SETTINGS && profile.value.isAtLeastAdmin) {
- view.value = VIEWS.SETTINGS;
} else if (v === VIEWS.SUPPORT && profile.value.isAtLeastOwner) {
view.value = VIEWS.SUPPORT;
} else if (v === VIEWS.SYSTEM && profile.value.isAtLeastAdmin) {
@@ -256,7 +256,7 @@ onMounted(async () => {
-
+
@@ -272,7 +272,7 @@ onMounted(async () => {
-
+
-
+
@@ -294,7 +294,7 @@ onMounted(async () => {
-
+
@@ -303,10 +303,10 @@ onMounted(async () => {
+
-
diff --git a/dashboard/src/views/BrandingView.vue b/dashboard/src/views/AppearanceView.vue
similarity index 100%
rename from dashboard/src/views/BrandingView.vue
rename to dashboard/src/views/AppearanceView.vue
diff --git a/dashboard/src/views/MetricsView.vue b/dashboard/src/views/MetricsView.vue
new file mode 100644
index 000000000..4791575ec
--- /dev/null
+++ b/dashboard/src/views/MetricsView.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t('system.info.platformVersion') }}
+
v{{ config.version }} ({{ config.ubuntuVersion }})
+
+
+
{{ $t('system.info.vendor') }}
+
{{ info.sysVendor }}
+
+
+
{{ $t('system.info.product') }}
+
{{ info.productName }}
+
+
+
CPU
+
{{ cpus.length ? `${cpus.length} Core "${cpus[0].model}"` : '' }}
+
+
+
{{ $t('system.info.memory') }}
+
{{ prettyDecimalSize(memory.memory) }} RAM & {{ prettyDecimalSize(memory.swap) }} Swap
+
+
+
{{ $t('system.info.uptime') }}
+
{{ uptime }}
+
+
+
{{ $t('system.info.activationTime') }}
+
{{ activeSince }}
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/views/SettingsView.vue b/dashboard/src/views/SettingsView.vue
deleted file mode 100644
index 51c30c102..000000000
--- a/dashboard/src/views/SettingsView.vue
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('settings.language.description') }}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dashboard/src/views/SystemView.vue b/dashboard/src/views/SystemView.vue
index 4791575ec..51c30c102 100644
--- a/dashboard/src/views/SystemView.vue
+++ b/dashboard/src/views/SystemView.vue
@@ -4,113 +4,94 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
-import { ref, useTemplateRef, onMounted } from 'vue';
-import { Button, InputDialog } from 'pankow';
+import { ref, onMounted } from 'vue';
+import { SingleSelect } from 'pankow';
import moment from 'moment-timezone';
-import { prettyDecimalSize, sleep } from 'pankow/utils';
+import SettingsItem from '../components/SettingsItem.vue';
import Section from '../components/Section.vue';
-import SystemMetrics from '../components/SystemMetrics.vue';
-import DiskUsage from '../components/DiskUsage.vue';
-import SystemModel from '../models/SystemModel.js';
-import DashboardModel from '../models/DashboardModel.js';
+import CloudronAccount from '../components/CloudronAccount.vue';
+import SystemUpdate from '../components/SystemUpdate.vue';
+import PrivateRegistry from '../components/PrivateRegistry.vue';
+import CloudronModel from '../models/CloudronModel.js';
-const systemModel = SystemModel.create();
-const dashboardModel = DashboardModel.create();
+const cloudronModel = CloudronModel.create();
-const config = ref({});
-const info = ref({});
-const memory = ref({});
-const cpus = ref({});
-const uptime = ref('');
-const activeSince = ref('');
+// Timezone
+const allTimezones = moment.tz.names().map(t => { return { id: t }; });
+const timeZone = ref('');
+const currentTimeZone = ref('');
-const inputDialog = useTemplateRef('inputDialog');
-async function onReboot() {
- const confirmed = await inputDialog.value.confirm({
- title: t('main.rebootDialog.title'),
- message: t('main.rebootDialog.description'),
- confirmLabel: t('main.rebootDialog.rebootAction'),
- confirmStyle: 'danger',
- rejectLabel: t('main.dialog.cancel'),
- });
+async function onTimeZoneChange(value) {
+ const [error] = await cloudronModel.setTimeZone(value);
+ if (error) return console.error(error);
- if (!confirmed) return;
+ currentTimeZone.value = value;
+}
- await systemModel.reboot();
+// Language
+const allLanguages = ref([]);
+const language = ref('');
+const currentLanguage = ref('');
- // now poll until the backend does not respond anymore to trigger the unreachable overlay
- while (true) {
- const [error] = await dashboardModel.config();
- if (error) break;
- await sleep(1000);
- }
+async function onLanguageChange(value) {
+ const [error] = await cloudronModel.setLanguage(value);
+ if (error) return console.error(error);
+
+ currentLanguage.value = value;
}
onMounted(async () => {
- let [error, result] = await systemModel.memory();
+ let [error, result] = await cloudronModel.getTimeZone();
if (error) return console.error(error);
- memory.value = result;
- [error, result] = await systemModel.cpus();
+ timeZone.value = result;
+ currentTimeZone.value = result;
+
+ [error, result] = await cloudronModel.languages();
if (error) return console.error(error);
- cpus.value = result;
- [error, result] = await systemModel.info();
+ allLanguages.value = result.map(l => {
+ return {
+ id: l,
+ display: t(`lang.${l}`)
+ };
+ });
+
+ [error, result] = await cloudronModel.getLanguage();
if (error) return console.error(error);
- info.value = result;
- uptime.value = moment.duration(info.value.uptimeSecs, 'seconds').locale(navigator.language).humanize();
- activeSince.value = info.value.activationTime ? moment(info.value.activationTime).fromNow() : 'unknown';
-
- [error, result] = await dashboardModel.config();
- if (error) return console.error(error);
- config.value = result;
+ language.value = result;
+ currentLanguage.value = result;
});
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
{{ $t('system.info.platformVersion') }}
-
v{{ config.version }} ({{ config.ubuntuVersion }})
-
-
-
{{ $t('system.info.vendor') }}
-
{{ info.sysVendor }}
-
-
-
{{ $t('system.info.product') }}
-
{{ info.productName }}
-
-
-
CPU
-
{{ cpus.length ? `${cpus.length} Core "${cpus[0].model}"` : '' }}
-
-
-
{{ $t('system.info.memory') }}
-
{{ prettyDecimalSize(memory.memory) }} RAM & {{ prettyDecimalSize(memory.swap) }} Swap
-
-
-
{{ $t('system.info.uptime') }}
-
{{ uptime }}
-
-
-
{{ $t('system.info.activationTime') }}
-
{{ activeSince }}
-
+
+
+
+ {{ $t('settings.language.description') }}
+
+
+
+
+
-
-
-
+
+
+