dont use [...path] routing since it breaks building statically

This commit is contained in:
2025-07-18 11:02:48 +02:00
parent 5ff19ef372
commit ecb201dc69
13 changed files with 300 additions and 188 deletions

View File

@@ -0,0 +1,128 @@
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import type { BreadcrumbData } from '~/types';
const route = useRoute();
const store = useWarrenStore();
const routeCrumbs = computed<BreadcrumbData[]>(() => {
if (route.path === '/') {
return [{ name: '/', href: '/' }];
}
const rawCrumbs = route.path.split('/');
const crumbs: BreadcrumbData[] = [];
crumbs[0] = {
name: '/',
href: '/',
};
for (let i = 1; i < rawCrumbs.length; i++) {
crumbs.push({
name: rawCrumbs[i],
href: rawCrumbs.slice(0, i + 1).join('/'),
});
}
return crumbs;
});
type WarrenBreadcrumbData = {
name: string;
action: () => void;
};
const warrenCrumbs = computed<WarrenBreadcrumbData[]>(() => {
if (store.current == null) {
return [];
}
const warren = store.warrens[store.current.warrenId];
const rawCrumbs = store.current.path.split('/').filter((c) => c.length > 0);
const crumbs: WarrenBreadcrumbData[] = [
{
name: '/',
action: async () => {
await navigateTo({
path: '/',
});
store.clearCurrentWarren();
},
},
{
name: 'warrens',
action: async () => {
await navigateTo({
path: '/warrens',
});
store.clearCurrentWarren();
},
},
{
name: warren.name,
action: () => store.setCurrentWarren(warren.id, '/'),
},
];
for (let i = 0; i < rawCrumbs.length; i++) {
const path = rawCrumbs.slice(0, i + 1).join('/');
crumbs.push({
name: rawCrumbs[i],
action: () => store.setCurrentWarrenPath(path),
});
}
return crumbs;
});
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<template v-if="store.current == null">
<template v-for="(crumb, i) in routeCrumbs" :key="i">
<BreadcrumbItem>
<NuxtLink
v-if="i < routeCrumbs.length - 1"
:to="crumb.href"
as-child
>
<BreadcrumbLink>
{{ crumb.name }}
</BreadcrumbLink>
</NuxtLink>
<BreadcrumbPage v-else>{{ crumb.name }}</BreadcrumbPage>
</BreadcrumbItem>
<BreadcrumbSeparator
v-if="i < routeCrumbs.length - 1"
class="hidden md:block"
/>
</template>
</template>
<template
v-for="(crumb, i) in warrenCrumbs"
v-else-if="route.path.startsWith('/warrens')"
:key="i"
>
<BreadcrumbItem
v-if="i < warrenCrumbs.length - 1"
class="cursor-pointer"
@click="crumb.action"
>
<BreadcrumbLink>
{{ crumb.name }}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbPage v-else>{{ crumb.name }}</BreadcrumbPage>
<BreadcrumbSeparator
v-if="i < warrenCrumbs.length - 1"
class="hidden md:block"
/>
</template>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@@ -9,8 +9,7 @@ import {
import { deleteWarrenDirectory, deleteWarrenFile } from '~/lib/api/warrens'; import { deleteWarrenDirectory, deleteWarrenFile } from '~/lib/api/warrens';
import type { DirectoryEntry } from '~/types'; import type { DirectoryEntry } from '~/types';
const route = useRoute(); const warrenStore = useWarrenStore();
const warrenRoute = useWarrenRoute();
const renameDialog = useRenameDirectoryDialog(); const renameDialog = useRenameDirectoryDialog();
const { entry, disabled } = defineProps<{ const { entry, disabled } = defineProps<{
@@ -21,12 +20,25 @@ const { entry, disabled } = defineProps<{
const deleting = ref(false); const deleting = ref(false);
async function submitDelete(force: boolean = false) { async function submitDelete(force: boolean = false) {
if (warrenStore.current == null) {
return;
}
deleting.value = true; deleting.value = true;
if (entry.fileType === 'directory') { if (entry.fileType === 'directory') {
await deleteWarrenDirectory(warrenRoute.value, entry.name, force); await deleteWarrenDirectory(
warrenStore.current.warrenId,
warrenStore.current.path,
entry.name,
force
);
} else { } else {
await deleteWarrenFile(warrenRoute.value, entry.name); await deleteWarrenFile(
warrenStore.current.warrenId,
warrenStore.current.path,
entry.name
);
} }
deleting.value = false; deleting.value = false;
@@ -40,32 +52,32 @@ async function openRenameDialog() {
<template> <template>
<ContextMenu> <ContextMenu>
<ContextMenuTrigger> <ContextMenuTrigger>
<NuxtLink <button
:to="joinPaths(route.path, entry.name)"
:class="[ :class="[
'bg-accent/30 border-border flex w-52 flex-row gap-4 rounded-md border-1 px-4 py-2 select-none', 'bg-accent/30 border-border flex w-52 flex-row gap-4 overflow-hidden rounded-md border-1 px-4 py-2 select-none',
{ {
'pointer-events-none': 'pointer-events-none':
disabled || entry.fileType === 'file', disabled || entry.fileType === 'file',
}, },
]" ]"
@click="() => warrenStore.addToCurrentWarrenPath(entry.name)"
> >
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<Icon class="size-6" :name="getFileIcon(entry.mimeType)" /> <Icon class="size-6" :name="getFileIcon(entry.mimeType)" />
</div> </div>
<div <div
class="flex flex-col gap-0 truncate overflow-hidden leading-6" class="flex w-full flex-col items-start justify-stretch gap-0 overflow-hidden text-left leading-6"
> >
<span>{{ entry.name }}</span> <span class="w-full truncate">{{ entry.name }}</span>
<NuxtTime <NuxtTime
v-if="entry.createdAt != null" v-if="entry.createdAt != null"
:datetime="entry.createdAt * 1000" :datetime="entry.createdAt * 1000"
class="text-muted-foreground text-sm" class="text-muted-foreground w-full truncate text-sm"
relative relative
></NuxtTime> ></NuxtTime>
</div> </div>
</NuxtLink> </button>
</ContextMenuTrigger> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuItem @select="openRenameDialog"> <ContextMenuItem @select="openRenameDialog">

View File

@@ -10,20 +10,28 @@ import {
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { createDirectory } from '~/lib/api/warrens'; import { createDirectory } from '~/lib/api/warrens';
const warrenRoute = useWarrenRoute(); const warrenStore = useWarrenStore();
const dialog = useCreateDirectoryDialog(); const dialog = useCreateDirectoryDialog();
const creating = ref(false); const creating = ref(false);
const directoryNameValid = computed(() => dialog.value.trim().length > 0); const directoryNameValid = computed(() => dialog.value.trim().length > 0);
async function submit() { async function submit() {
if (!directoryNameValid.value || creating.value) { if (
!directoryNameValid.value ||
creating.value ||
warrenStore.current == null
) {
return; return;
} }
creating.value = true; creating.value = true;
const { success } = await createDirectory(warrenRoute.value, dialog.value); const { success } = await createDirectory(
warrenStore.current.warrenId,
warrenStore.current.path,
dialog.value
);
creating.value = false; creating.value = false;

View File

@@ -9,21 +9,27 @@ import {
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { renameWarrenEntry } from '~/lib/api/warrens'; import { renameWarrenEntry } from '~/lib/api/warrens';
const warrenRoute = useWarrenRoute(); const warrenStore = useWarrenStore();
const dialog = useRenameDirectoryDialog(); const dialog = useRenameDirectoryDialog();
const renaming = ref(false); const renaming = ref(false);
const directoryNameValid = computed(() => dialog.value.trim().length > 0); const directoryNameValid = computed(() => dialog.value.trim().length > 0);
async function submit() { async function submit() {
if (dialog.entry == null || !directoryNameValid.value || renaming.value) { if (
dialog.entry == null ||
!directoryNameValid.value ||
renaming.value ||
warrenStore.current == null
) {
return; return;
} }
renaming.value = true; renaming.value = true;
const { success } = await renameWarrenEntry( const { success } = await renameWarrenEntry(
warrenRoute.value, warrenStore.current.warrenId,
warrenStore.current.path,
dialog.entry.name, dialog.entry.name,
dialog.value dialog.value
); );

View File

@@ -16,9 +16,16 @@ import { uploadToWarren } from '~/lib/api/warrens';
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import UploadListEntry from './UploadListEntry.vue'; import UploadListEntry from './UploadListEntry.vue';
const warrenStore = useWarrenStore();
const uploadStore = useUploadStore(); const uploadStore = useUploadStore();
const warrenRoute = useWarrenRoute(); const currentAndUploadRouteMatch = computed(
() =>
uploadStore.destination != null &&
warrenStore.current != null &&
uploadStore.destination.warrenId == warrenStore.current.warrenId &&
uploadStore.destination.path == warrenStore.current.path
);
const fileInputElement = ref<HTMLInputElement>( const fileInputElement = ref<HTMLInputElement>(
null as unknown as HTMLInputElement null as unknown as HTMLInputElement
@@ -28,7 +35,7 @@ const uploading = ref(false);
const presentPaths = new Set<string>(); const presentPaths = new Set<string>();
function onFilesChanged(event: Event) { function onFilesChanged(event: Event) {
if (warrenRoute.value == null) { if (warrenStore.current == null) {
toast.warning('Upload', { toast.warning('Upload', {
description: 'Enter a warren before attempting to upload files', description: 'Enter a warren before attempting to upload files',
}); });
@@ -45,9 +52,9 @@ function onFilesChanged(event: Event) {
return; return;
} }
if (uploadStore.path == null) { if (uploadStore.destination == null) {
uploadStore.path = warrenRoute.value; uploadStore.destination = warrenStore.current;
} else if (uploadStore.path !== warrenRoute.value) { } else if (currentAndUploadRouteMatch.value) {
toast.warning('Upload', { toast.warning('Upload', {
description: description:
'The unfinished items belong to a different directory. Remove them before attempting to upload to a different directory.', 'The unfinished items belong to a different directory. Remove them before attempting to upload to a different directory.',
@@ -78,7 +85,7 @@ function removeFile(index: number) {
presentPaths.delete(file.data.name); presentPaths.delete(file.data.name);
if (uploadStore.files.length < 1) { if (uploadStore.files.length < 1) {
uploadStore.path = null; uploadStore.destination = null;
uploadStore.progress = null; uploadStore.progress = null;
} }
} }
@@ -94,7 +101,7 @@ function clearCompletedFiles() {
}); });
if (uploadStore.files.length < 1) { if (uploadStore.files.length < 1) {
uploadStore.path = null; uploadStore.destination = null;
uploadStore.progress = null; uploadStore.progress = null;
} }
} }
@@ -126,7 +133,7 @@ function updateFileListStatus(loadedBytes: number, ended: boolean = false) {
} }
async function submit() { async function submit() {
if (uploadStore.path == null) { if (uploadStore.destination == null) {
return; return;
} }
@@ -154,7 +161,8 @@ async function submit() {
}; };
const { success } = await uploadToWarren( const { success } = await uploadToWarren(
uploadStore.path, uploadStore.destination.warrenId,
uploadStore.destination.path,
dt.files, dt.files,
(loaded, total) => { (loaded, total) => {
if (uploadStore.progress != null) { if (uploadStore.progress != null) {
@@ -192,10 +200,13 @@ async function submit() {
<DialogDescription <DialogDescription
>Upload files to >Upload files to
<span class="text-foreground">{{ <span class="text-foreground">{{
uploadStore.path == null || currentAndUploadRouteMatch ||
warrenRoute === uploadStore.path uploadStore.destination == null
? 'the current directory' ? 'the current directory'
: routeWithWarrenName(uploadStore.path) : routeWithWarrenName(
uploadStore.destination!.warrenId,
uploadStore.destination!.path
)
}}</span></DialogDescription }}</span></DialogDescription
> >
</DialogHeader> </DialogHeader>

View File

@@ -13,8 +13,6 @@ import UploadDialog from '~/components/actions/UploadDialog.vue';
const uploadStore = useUploadStore(); const uploadStore = useUploadStore();
const route = useRoute(); const route = useRoute();
const breadcrumbs = computed(() => getBreadcrumbs(route.path));
</script> </script>
<template> <template>
@@ -27,33 +25,7 @@ const breadcrumbs = computed(() => getBreadcrumbs(route.path));
<div class="flex w-full items-center gap-2 px-4"> <div class="flex w-full items-center gap-2 px-4">
<SidebarTrigger class="[&_svg]:size-4" /> <SidebarTrigger class="[&_svg]:size-4" />
<Separator orientation="vertical" class="mr-2 !h-4" /> <Separator orientation="vertical" class="mr-2 !h-4" />
<Breadcrumb> <AppBreadcrumbs />
<BreadcrumbList>
<template
v-for="(crumb, i) in breadcrumbs"
:key="i"
>
<BreadcrumbItem>
<NuxtLink
v-if="i < breadcrumbs.length - 1"
:to="crumb.href"
as-child
>
<BreadcrumbLink>
{{ crumb.name }}
</BreadcrumbLink>
</NuxtLink>
<BreadcrumbPage v-else>{{
crumb.name
}}</BreadcrumbPage>
</BreadcrumbItem>
<BreadcrumbSeparator
v-if="i < breadcrumbs.length - 1"
class="hidden md:block"
/>
</template>
</BreadcrumbList>
</Breadcrumb>
<div <div
class="ml-auto flex flex-row-reverse items-center gap-2" class="ml-auto flex flex-row-reverse items-center gap-2"

View File

@@ -27,17 +27,9 @@ export async function getWarrens(): Promise<Record<string, Warren>> {
} }
export async function getWarrenDirectory( export async function getWarrenDirectory(
warrenId: string,
path: string path: string
): Promise<DirectoryEntry[]> { ): Promise<DirectoryEntry[]> {
// eslint-disable-next-line prefer-const
let [warrenId, rest] = splitOnce(path, '/');
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest);
}
const { data, error } = await useFetch< const { data, error } = await useFetch<
ApiResponse<{ files: DirectoryEntry[] }> ApiResponse<{ files: DirectoryEntry[] }>
>(getApiUrl(`warrens/files`), { >(getApiUrl(`warrens/files`), {
@@ -47,7 +39,7 @@ export async function getWarrenDirectory(
}, },
body: JSON.stringify({ body: JSON.stringify({
warrenId, warrenId,
path: rest, path,
}), }),
}); });
@@ -61,19 +53,15 @@ export async function getWarrenDirectory(
} }
export async function createDirectory( export async function createDirectory(
warrenId: string,
path: string, path: string,
directoryName: string directoryName: string
): Promise<{ success: boolean }> { ): Promise<{ success: boolean }> {
// eslint-disable-next-line prefer-const if (!path.endsWith('/')) {
let [warrenId, rest] = splitOnce(path, '/'); path += '/';
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest) + '/';
} }
rest += directoryName; path += directoryName;
const { status } = await useFetch(getApiUrl(`warrens/files/directory`), { const { status } = await useFetch(getApiUrl(`warrens/files/directory`), {
method: 'POST', method: 'POST',
@@ -82,7 +70,7 @@ export async function createDirectory(
}, },
body: JSON.stringify({ body: JSON.stringify({
warrenId, warrenId,
path: rest, path,
}), }),
}); });
@@ -105,20 +93,16 @@ export async function createDirectory(
} }
export async function deleteWarrenDirectory( export async function deleteWarrenDirectory(
warrenId: string,
path: string, path: string,
directoryName: string, directoryName: string,
force: boolean force: boolean
): Promise<{ success: boolean }> { ): Promise<{ success: boolean }> {
// eslint-disable-next-line prefer-const if (!path.endsWith('/')) {
let [warrenId, rest] = splitOnce(path, '/'); path += '/';
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest) + '/';
} }
rest += directoryName; path += directoryName;
const { status } = await useFetch(getApiUrl(`warrens/files/directory`), { const { status } = await useFetch(getApiUrl(`warrens/files/directory`), {
method: 'DELETE', method: 'DELETE',
@@ -127,7 +111,7 @@ export async function deleteWarrenDirectory(
}, },
body: JSON.stringify({ body: JSON.stringify({
warrenId, warrenId,
path: rest, path,
force, force,
}), }),
}); });
@@ -152,19 +136,15 @@ export async function deleteWarrenDirectory(
} }
export async function deleteWarrenFile( export async function deleteWarrenFile(
warrenId: string,
path: string, path: string,
fileName: string fileName: string
): Promise<{ success: boolean }> { ): Promise<{ success: boolean }> {
// eslint-disable-next-line prefer-const if (!path.endsWith('/')) {
let [warrenId, rest] = splitOnce(path, '/'); path += '/';
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest) + '/';
} }
rest += fileName; path += fileName;
const { status } = await useFetch(getApiUrl(`warrens/files/file`), { const { status } = await useFetch(getApiUrl(`warrens/files/file`), {
method: 'DELETE', method: 'DELETE',
@@ -173,7 +153,7 @@ export async function deleteWarrenFile(
}, },
body: JSON.stringify({ body: JSON.stringify({
warrenId, warrenId,
path: rest, path,
}), }),
}); });
@@ -197,19 +177,11 @@ export async function deleteWarrenFile(
} }
export async function uploadToWarren( export async function uploadToWarren(
warrenId: string,
path: string, path: string,
files: FileList, files: FileList,
onProgress: ((loaded: number, total: number) => void) | undefined onProgress: ((loaded: number, total: number) => void) | undefined
): Promise<{ success: boolean }> { ): Promise<{ success: boolean }> {
// eslint-disable-next-line prefer-const
let [warrenId, rest] = splitOnce(path, '/');
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest);
}
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', getApiUrl(`warrens/files/upload`)); xhr.open('POST', getApiUrl(`warrens/files/upload`));
xhr.upload.onprogress = (e) => { xhr.upload.onprogress = (e) => {
@@ -232,7 +204,7 @@ export async function uploadToWarren(
const body = new FormData(); const body = new FormData();
body.set('warrenId', warrenId); body.set('warrenId', warrenId);
body.set('path', rest); body.set('path', path);
for (const file of files) { for (const file of files) {
body.append('files', file); body.append('files', file);
} }
@@ -260,20 +232,15 @@ export async function uploadToWarren(
} }
export async function renameWarrenEntry( export async function renameWarrenEntry(
warrenId: string,
path: string, path: string,
currentName: string, currentName: string,
newName: string newName: string
): Promise<{ success: boolean }> { ): Promise<{ success: boolean }> {
// eslint-disable-next-line prefer-const if (!path.endsWith('/')) {
let [warrenId, rest] = splitOnce(path, '/'); path += '/';
if (rest == null) {
rest = '/';
} else {
rest = '/' + decodeURI(rest) + '/';
} }
path += currentName;
rest += currentName;
const { status } = await useFetch(getApiUrl(`warrens/files/rename`), { const { status } = await useFetch(getApiUrl(`warrens/files/rename`), {
method: 'PATCH', method: 'PATCH',
@@ -282,7 +249,7 @@ export async function renameWarrenEntry(
}, },
body: JSON.stringify({ body: JSON.stringify({
warrenId, warrenId,
path: rest, path,
newName, newName,
}), }),
}); });

View File

@@ -7,9 +7,24 @@ definePageMeta({
middleware: ['authenticated'], middleware: ['authenticated'],
}); });
const entries = useAsyncData('current-directory', () => const warrenStore = useWarrenStore();
getWarrenDirectory(useWarrenRoute().value)
).data; if (warrenStore.current == null) {
await navigateTo({
path: '/warrens',
});
}
const entries = useAsyncData('current-directory', async () => {
if (warrenStore.current == null) {
return null;
}
return await getWarrenDirectory(
warrenStore.current.warrenId,
warrenStore.current.path
);
}).data;
</script> </script>
<template> <template>

View File

@@ -1,26 +1,35 @@
<script setup lang="ts"> <script setup lang="ts">
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
import type { Warren } from '~/types/warrens';
definePageMeta({ definePageMeta({
middleware: ['authenticated'], middleware: ['authenticated'],
}); });
const store = useWarrenStore(); const store = useWarrenStore();
function selectWarren(warren: Warren) {
store.setCurrentWarren(warren.id, '/');
navigateTo({
path: '/warrens/files',
});
}
</script> </script>
<template> <template>
<ScrollArea class="h-full w-full"> <ScrollArea class="h-full w-full">
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<NuxtLink <Button
v-for="(warren, uuid) in store.warrens" v-for="(warren, uuid) in store.warrens"
:key="uuid" :key="uuid"
:to="`/warrens/${uuid}`" class="h-12 w-44"
variant="outline"
size="lg"
@click="() => selectWarren(warren)"
> >
<Button class="h-12 w-44" variant="outline" size="lg"> <Icon name="lucide:folder-root" />
<Icon name="lucide:folder-root" /> <span clas="truncate">{{ warren.name }}</span>
<span clas="truncate">{{ warren.name }}</span> </Button>
</Button>
</NuxtLink>
</div> </div>
</ScrollArea> </ScrollArea>
</template> </template>

View File

@@ -2,20 +2,48 @@ import { defineStore } from 'pinia';
import type { DirectoryEntry } from '~/types'; import type { DirectoryEntry } from '~/types';
import type { Warren } from '~/types/warrens'; import type { Warren } from '~/types/warrens';
export const useWarrenStore = defineStore< export const useWarrenStore = defineStore('warrens', {
'warrens',
{
warrens: Record<string, Warren>;
}
>('warrens', {
state: () => ({ state: () => ({
warrens: {}, warrens: {} as Record<string, Warren>,
upload: null, current: null as { warrenId: string; path: string } | null,
}), }),
}); actions: {
async setCurrentWarren(warrenId: string, path: string) {
this.current = {
warrenId,
path,
};
await refreshNuxtData('current-directory');
},
async addToCurrentWarrenPath(path: string) {
if (this.current == null) {
return;
}
export const useWarrenRoute = () => if (!this.current.path.endsWith('/')) {
computed(() => useRoute().path.split('/warrens/')[1]); this.current.path += '/';
}
this.current.path += path;
await refreshNuxtData('current-directory');
},
async setCurrentWarrenPath(path: string) {
if (this.current == null) {
return;
}
if (!path.startsWith('/')) {
path = '/' + path;
}
this.current.path = path;
await refreshNuxtData('current-directory');
},
clearCurrentWarren() {
this.current = null;
},
},
});
export const useCreateDirectoryDialog = defineStore('create_directory_dialog', { export const useCreateDirectoryDialog = defineStore('create_directory_dialog', {
state: () => ({ state: () => ({

View File

@@ -5,7 +5,7 @@ export const useUploadStore = defineStore<
{ {
startIndex: number; startIndex: number;
files: UploadFile[]; files: UploadFile[];
path: string | null; destination: { warrenId: string; path: string } | null;
progress: { progress: {
loadedBytes: number; loadedBytes: number;
totalBytes: number; totalBytes: number;
@@ -15,7 +15,7 @@ export const useUploadStore = defineStore<
state: () => ({ state: () => ({
startIndex: 0, startIndex: 0,
files: [], files: [],
path: null, destination: null,
progress: null, progress: null,
}), }),
}); });

View File

@@ -8,14 +8,14 @@ export function getApiUrl(path: string): string {
* @param path - The warren path (e.g. `a3f79579-9155-4492-a579-b0253c8d3bf8/my-directory/`) * @param path - The warren path (e.g. `a3f79579-9155-4492-a579-b0253c8d3bf8/my-directory/`)
* @returns A prettier path `a3f79579-9155-4492-a579-b0253c8d3bf8/my-directory` -> `my-warren/my-directory` * @returns A prettier path `a3f79579-9155-4492-a579-b0253c8d3bf8/my-directory` -> `my-warren/my-directory`
*/ */
export function routeWithWarrenName(path: string): string { export function routeWithWarrenName(warrenId: string, path: string): string {
const warrens = useWarrenStore().warrens; const warrenStore = useWarrenStore();
const id = path.split('/')[0]; if (!(warrenId in warrenStore.warrens)) {
if (!(id in warrens)) {
return path; return path;
} }
return path.replace(id, warrens[id].name); const warrenName = warrenStore.warrens[warrenId].name;
return `${warrenName}${path}`;
} }

View File

@@ -1,53 +1,9 @@
import type { BreadcrumbData } from '~/types';
export function getBreadcrumbs(path: string): BreadcrumbData[] {
const { warrens } = useWarrenStore();
const crumbs = path
.split('/')
.filter((v) => v.length > 0)
.map((v) => ({
name: v,
href: '#',
}));
crumbs.unshift({ name: '/', href: '/' });
for (let i = 1; i < crumbs.length; i++) {
crumbs[i].name = decodeURI(crumbs[i].name);
crumbs[i].href =
'/' +
path
.split('/')
.slice(1, i + 1)
.join('/');
}
if (
crumbs.length >= 3 &&
crumbs[1].href === '/warrens' &&
crumbs[2].name in warrens
) {
crumbs[2].name = warrens[crumbs[2].name].name;
}
return crumbs;
}
export function preventDefault(event: Event) { export function preventDefault(event: Event) {
event.preventDefault(); event.preventDefault();
return event; return event;
} }
export function joinPaths(base: string, other: string): string {
if (!base.endsWith('/')) {
base += '/';
}
return base + other;
}
export function splitOnce( export function splitOnce(
str: string, str: string,
search: string search: string