Author SHA1 Message Date
jakob.scheid aecb213c10 test(legal): add tests for getLegalNotice 2026-08-05 15:19:11 +02:00
jakob.scheid 8c62b17e5e feat(legal): add legal notice loading indicator 2026-08-05 14:40:43 +02:00
jakob.scheid 378f19d3b7 feat(legal): add legal notice error handling
The legal notice view now shows an error box if the legal notice could
not be loaded.
2026-08-05 14:38:09 +02:00
jakob.scheid 64f4116820 fix(style): set link color to --dark variable
This change was necessary due to the dynamically loaded legal notice.
Links would have the default color without this fix.
2026-08-05 14:13:42 +02:00
jakob.scheid 8f47a6dde0 feat(legal): load and display legal notice 2026-08-05 14:12:54 +02:00
jakob.scheid 9e0b47b672 feat(legal): add legal notice link in the footer
Added a link to the legal notice in the footer and revised the layout of
the last footer segment: The segment is now a grid container and the
legal links (currently only the legal notice link) are displayed at the
end. The copyright notice remains centered. On small screens, the legal
links are displayed centered below the copyright notice.
2026-08-05 13:33:58 +02:00
jakob.scheid aecd23ddf9 feat(legal): add route for the legal notice 2026-08-05 13:33:52 +02:00
jakob.scheid de5bee2c4a feat(settings): add composable for settings values
Added the composable useSettings that provides the function getSetting.
This function returns the set value of the setting, or otherwise the
default value. If there is no default value, it returns undefined.
2026-08-05 00:32:53 +02:00
5 changed files with 48 additions and 88 deletions
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { FetchError } from '../../errors'; import { FetchError } from '../errors';
import { getLegalNotice } from '../fetchLegalContent'; import { getLegalNotice } from './fetchLegalContent';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
let fetchSpy; let fetchSpy;
+2 -3
View File
@@ -27,10 +27,9 @@ const legalNoticeHtml = ref(null);
onMounted(async () => { onMounted(async () => {
try { try {
legalNoticeHtml.value = await getLegalNotice(); legalNoticeHtml.value = await getLegalNotice();
loading.value = false;
} catch { } catch {
legalNoticeError.value = 'legal.notice.loadError'; legalNoticeError.value = 'legal.notice.loadError';
} finally {
loading.value = false;
} }
}); });
</script> </script>
@@ -43,7 +42,7 @@ onMounted(async () => {
</h1> </h1>
</header> </header>
<main> <main>
<div class="legal-notice-loading" v-if="loading"> <div v-if="loading">
{{ t('loading') }} {{ t('loading') }}
</div> </div>
<div class="error" v-else-if="legalNoticeError"> <div class="error" v-else-if="legalNoticeError">
@@ -1,78 +0,0 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { FetchError } from '../../errors.js';
import LegalNoticeView from '../LegalNoticeView.vue';
import { mountComponent } from '@/test-utils/mountComponent';
import { flushPromises } from '@vue/test-utils';
import { afterEach, describe, expect, test, vi } from 'vitest';
const getLegalNotice = vi.hoisted(() => vi.fn().mockResolvedValue('legal notice'));
vi.mock('../../services/fetchLegalContent', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
getLegalNotice
};
});
afterEach(() => getLegalNotice.mockRestore());
describe('LegalNoticeView', () => {
test('shows loading indicator', () => {
const wrapper = mountComponent(LegalNoticeView);
const legalNoticeLoading = wrapper.find('.legal-notice-loading');
const legalNoticeError = wrapper.find('.error');
const legalNotice = wrapper.find('.legal-notice');
expect(legalNoticeLoading.exists()).toBe(true);
expect(legalNoticeError.exists()).toBe(false);
expect(legalNotice.exists()).toBe(false);
});
test('shows error', async () => {
getLegalNotice.mockRejectedValue(new FetchError());
const wrapper = mountComponent(LegalNoticeView);
await flushPromises();
await wrapper.vm.$nextTick();
const legalNoticeLoading = wrapper.find('.legal-notice-loading');
const legalNoticeError = wrapper.find('.error');
const legalNotice = wrapper.find('.legal-notice');
expect(legalNoticeLoading.exists()).toBe(false);
expect(legalNoticeError.exists()).toBe(true);
expect(legalNotice.exists()).toBe(false);
});
test('shows legal notice', async () => {
const wrapper = mountComponent(LegalNoticeView);
await flushPromises();
await wrapper.vm.$nextTick();
const legalNoticeLoading = wrapper.find('.legal-notice-loading');
const legalNoticeError = wrapper.find('.error');
const legalNotice = wrapper.find('.legal-notice');
expect(legalNoticeLoading.exists()).toBe(false);
expect(legalNoticeError.exists()).toBe(false);
expect(legalNotice.exists()).toBe(true);
});
});
@@ -0,0 +1,43 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useSettingsStore } from '../stores/settingsStore';
import { getSettingRecursively } from '../utils/getSetting';
import { loadSettingsConfig } from '../utils/settingsParser';
/**
* Provides access to the stored settings.
*/
export const useSettings = function useSettings () {
/**
* Returns the value of a specific setting.
* @param {string[]} key - The settings key.
* @return The value of the setting.
*/
const getSetting = async function getSetting (key) {
const settingsStore = useSettingsStore();
const settings = await loadSettingsConfig();
return (
settingsStore.get(key) ??
getSettingRecursively(
key, settings.contents ?? []
)?.default
);
};
return { getSetting };
};
@@ -61,10 +61,6 @@ const toggleSidebar = function toggleSidebar () {
} }
@media (max-width: 48rem) { @media (max-width: 48rem) {
.sidebar {
border: none;
}
.sidebar:not(.expanded) .sidebar-expand-button { .sidebar:not(.expanded) .sidebar-expand-button {
display: none; display: none;
} }