Skip to content

Commit b80ae95

Browse files
committed
feat: handle resource 404
Fixes #226
1 parent 15dc299 commit b80ae95

File tree

6 files changed

+57
-16
lines changed

6 files changed

+57
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ As this project is a user-facing application, the places in the semantic version
1818
- Sort texts by filename/size [#46](https://github.com/spraakbanken/mink-frontend/issues/46)
1919
- View result file [#170](https://github.com/spraakbanken/mink-frontend/issues/170)
2020
- Explain export file types [#192](https://github.com/spraakbanken/mink-frontend/issues/192)
21+
- Handle resource not found [#226](https://github.com/spraakbanken/mink-frontend/issues/226)
2122
- List allowed source file extensions on upload area [#234](https://github.com/spraakbanken/mink-frontend/issues/234)
2223

2324
## [1.16.1](https://github.com/spraakbanken/mink-frontend/releases/tag/v1.16.1) (2026-02-16)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { isAxiosError } from "axios";
2+
import { useRoute, useRouter } from "vue-router";
3+
4+
export default function useNotFound() {
5+
const router = useRouter();
6+
const route = useRoute();
7+
8+
function showNotFoundPage() {
9+
router.push({
10+
name: "notfound",
11+
params: { pathMatch: route.path.substring(1).split("/") },
12+
});
13+
}
14+
15+
return {
16+
/** Handle an Axios 404 error, rethrow others */
17+
handle404(reason: unknown) {
18+
if (isAxiosError(reason) && reason.status == 404) showNotFoundPage();
19+
else throw reason;
20+
},
21+
22+
/** Redirect to the "Page not found" route without changing the URL */
23+
showNotFoundPage,
24+
};
25+
}

src/corpus/CorpusView.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
<script setup lang="ts">
2-
import { useCorpus } from "./corpus.composable";
32
import useCorpusIdParam from "@/corpus/corpusIdParam.composable";
43
import PageTitle from "@/components/PageTitle.vue";
54
import useLocale from "@/i18n/locale.composable";
5+
import { useCorpusStore } from "@/store/corpus.store";
6+
import { computedAsync } from "@vueuse/core";
7+
import useMessenger from "@/message/messenger.composable";
8+
import useNotFound from "@/components/notfound.composable";
69
710
const corpusId = useCorpusIdParam();
8-
const { corpus } = useCorpus(corpusId);
11+
const { loadCorpus } = useCorpusStore();
912
const { th } = useLocale();
13+
const { alertError } = useMessenger();
14+
const { handle404 } = useNotFound();
15+
16+
const corpus = computedAsync(() =>
17+
loadCorpus(corpusId).catch(handle404).catch(alertError),
18+
);
1019
</script>
1120

1221
<template>
@@ -22,5 +31,5 @@ const { th } = useLocale();
2231
{{ th(corpus?.name) || corpusId }}
2332
</template>
2433
</PageTitle>
25-
<router-view />
34+
<router-view v-if="corpus" />
2635
</template>

src/metadata/MetadataView.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
<script setup lang="ts">
2-
import { computed } from "vue";
32
import useMetadata from "@/metadata/metadata.composable";
4-
import { useResourceStore } from "@/store/resource.store";
53
import useResourceIdParam from "@/resource/resourceIdParam.composable";
64
import PageTitle from "@/components/PageTitle.vue";
75
import useMessenger from "@/message/messenger.composable";
6+
import { computedAsync } from "@vueuse/core";
7+
import useNotFound from "@/components/notfound.composable";
88
9-
const resourceStore = useResourceStore();
109
const resourceId = useResourceIdParam();
1110
const { loadMetadata } = useMetadata(resourceId);
1211
const { alertError } = useMessenger();
12+
const { handle404 } = useNotFound();
1313
14-
const metadata = computed(() => resourceStore.resources[resourceId]);
15-
16-
loadMetadata().catch(alertError);
14+
const metadata = computedAsync(() =>
15+
loadMetadata().catch(handle404).catch(alertError),
16+
);
1717
</script>
1818

1919
<template>

src/metadata/metadata.composable.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import api from "@/api/api";
22
import { useResourceStore } from "@/store/resource.store";
33
import useSpin from "@/spin/spin.composable";
4+
import type { Metadata } from "@/store/resource.types";
45

56
/** Tracks fully loaded resources, so subsequent load calls can be skipped. */
67
const isFresh: Record<string, true> = {};
@@ -10,20 +11,22 @@ export default function useMetadata(resourceId: string) {
1011
const { spin } = useSpin();
1112

1213
/** Load data about a metadata and store it. */
13-
async function loadMetadata(): Promise<void> {
14+
async function loadMetadata(): Promise<Metadata | undefined> {
1415
// Make sure the resource has an entry in the store.
15-
await resourceStore.loadResource(resourceId);
16+
const resource = (await resourceStore.loadResource(resourceId)) as Metadata;
1617

1718
// Skip if already loaded.
18-
if (isFresh[resourceId]) return;
19-
20-
// Load remaining essential info, unless removed.
21-
if (resourceId in resourceStore.metadatas) {
22-
await loadYaml();
19+
if (!isFresh[resourceId]) {
20+
// Load remaining essential info, unless removed.
21+
if (resourceId in resourceStore.metadatas) {
22+
await loadYaml();
23+
}
2324
}
2425

2526
// Remember to skip loading next time.
2627
isFresh[resourceId] = true;
28+
29+
return resource;
2730
}
2831

2932
/** Load and store the metadata yaml string. */

src/resource/ResourceRedirectView.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import useResourceIdParam from "@/resource/resourceIdParam.composable";
44
import { useResourceStore } from "@/store/resource.store";
55
import SpinIndicator from "@/spin/SpinIndicator.vue";
66
import useMessenger from "@/message/messenger.composable";
7+
import useNotFound from "@/components/notfound.composable";
78
89
const router = useRouter();
910
const resourceId = useResourceIdParam();
1011
const resourceStore = useResourceStore();
1112
const { alertError } = useMessenger();
13+
const { handle404 } = useNotFound();
1214
1315
// Load resource and immediately redirect according to its type.
1416
resourceStore
@@ -17,6 +19,7 @@ resourceStore
1719
if (resource) router.push(`/library/${resource.type}/${resourceId}`);
1820
else router.push("/library");
1921
})
22+
.catch(handle404)
2023
.catch(alertError);
2124
</script>
2225

0 commit comments

Comments
 (0)