list warrens + explore nested folders
This commit is contained in:
3
frontend/.env.dev
Normal file
3
frontend/.env.dev
Normal file
@@ -0,0 +1,3 @@
|
||||
# this file is ignored when the app is built since we're using SSG
|
||||
|
||||
NUXT_PUBLIC_API_BASE="http://127.0.0.1:8080/api"
|
||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -22,3 +22,4 @@ logs
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.dev
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { getWarrens } from './lib/api/warrens';
|
||||
|
||||
const store = useWarrenStore();
|
||||
|
||||
store.warrens = await getWarrens();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtRouteAnnouncer />
|
||||
<NuxtLoadingIndicator />
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"@nuxt/icon": "1.15.0",
|
||||
"@nuxt/image": "1.10.0",
|
||||
"@nuxt/test-utils": "3.19.2",
|
||||
"@pinia/nuxt": "^0.11.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -16,6 +17,7 @@
|
||||
"eslint": "^9.0.0",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"nuxt": "^3.17.6",
|
||||
"pinia": "^3.0.3",
|
||||
"reka-ui": "^2.3.2",
|
||||
"shadcn-nuxt": "2.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
@@ -369,6 +371,8 @@
|
||||
|
||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
||||
|
||||
"@pinia/nuxt": ["@pinia/nuxt@0.11.1", "", { "dependencies": { "@nuxt/kit": "^3.9.0" }, "peerDependencies": { "pinia": "^3.0.3" } }, "sha512-tCD8ioWhhIHKwm8Y9VvyhBAV/kK4W5uGBIYbI5iM4N1t7duOqK6ECBUavrMxMolELayqqMLb9+evegrh3S7s2A=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
@@ -547,7 +551,7 @@
|
||||
|
||||
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.17", "", { "dependencies": { "@vue/compiler-dom": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ=="],
|
||||
|
||||
"@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||
"@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="],
|
||||
|
||||
"@vue/devtools-core": ["@vue/devtools-core@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7", "@vue/devtools-shared": "^7.7.7", "mitt": "^3.0.1", "nanoid": "^5.1.0", "pathe": "^2.0.3", "vite-hot-client": "^2.0.4" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ=="],
|
||||
|
||||
@@ -1445,6 +1449,8 @@
|
||||
|
||||
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||
|
||||
"pinia": ["pinia@3.0.3", "", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA=="],
|
||||
|
||||
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
|
||||
@@ -2199,6 +2205,8 @@
|
||||
|
||||
"vite-plugin-vue-tracer/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||
|
||||
"winston/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"winston/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
@@ -14,18 +14,7 @@ import {
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const warrens = [
|
||||
{
|
||||
title: 'Thyr',
|
||||
url: '/warrens/Thyr',
|
||||
icon: h(Icon, { name: 'lucide:folder-root' }),
|
||||
},
|
||||
{
|
||||
title: 'Serc',
|
||||
url: '/warrens/Serc',
|
||||
icon: h(Icon, { name: 'lucide:folder-root' }),
|
||||
},
|
||||
];
|
||||
const store = useWarrenStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -57,22 +46,24 @@ const warrens = [
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
<SidebarMenuSubItem
|
||||
v-for="warren in warrens"
|
||||
:key="warren.title"
|
||||
v-for="(warren, uuid) in store.warrens"
|
||||
:key="uuid"
|
||||
>
|
||||
<SidebarMenuSubButton
|
||||
as-child
|
||||
:tooltip="warren.title"
|
||||
:tooltip="warren.name"
|
||||
:is-active="
|
||||
warren.url === route.path
|
||||
route.path.startsWith(
|
||||
`/warrens/${uuid}`
|
||||
)
|
||||
"
|
||||
class="transition-all"
|
||||
>
|
||||
<NuxtLink :to="warren.url">
|
||||
<component
|
||||
:is="warren.icon"
|
||||
></component>
|
||||
<span>{{ warren.title }}</span>
|
||||
<NuxtLink :to="`/warrens/${uuid}`">
|
||||
<Icon
|
||||
name="lucide:folder-root"
|
||||
/>
|
||||
<span>{{ warren.name }}</span>
|
||||
</NuxtLink>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { DirectoryEntryType } from '~/types';
|
||||
import type { FileType } from '~/types';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const { name, entryType } = defineProps<{
|
||||
const { name, entryType, disabled } = defineProps<{
|
||||
name: string;
|
||||
entryType: DirectoryEntryType;
|
||||
entryType: FileType;
|
||||
disabled: boolean,
|
||||
}>();
|
||||
|
||||
const iconName = entryType === 'file' ? 'lucide:file' : 'lucide:folder';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button class="w-36 h-12" variant="outline" size="lg">
|
||||
<NuxtLink
|
||||
class="flex flex-row items-center gap-1.5"
|
||||
:to="joinPaths(route.path, name)"
|
||||
<NuxtLink
|
||||
:to="joinPaths(route.path, name)"
|
||||
:class="['select-none', { 'pointer-events-none': disabled }]"
|
||||
>
|
||||
<Button
|
||||
class="w-44 h-12"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<Icon :name="iconName" />
|
||||
<span>{{ name }}</span>
|
||||
</NuxtLink>
|
||||
</Button>
|
||||
<span class="truncate">{{ name }}</span>
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
@@ -1,34 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import type { DirectoryEntryType } from '~/types';
|
||||
const items: Array<{ name: string; entryType: DirectoryEntryType }> = [
|
||||
/* {
|
||||
name: 'File A',
|
||||
entryType: 'file',
|
||||
},
|
||||
{
|
||||
name: 'File B',
|
||||
entryType: 'file',
|
||||
}, */
|
||||
{
|
||||
name: 'Directory A',
|
||||
entryType: 'directory',
|
||||
},
|
||||
{
|
||||
name: 'Directory B',
|
||||
entryType: 'directory',
|
||||
},
|
||||
];
|
||||
import type { DirectoryEntry } from '~/types';
|
||||
const { entries } = defineProps<{
|
||||
entries: DirectoryEntry[];
|
||||
}>();
|
||||
|
||||
const { isLoading } = useLoadingIndicator();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ScrollArea class="w-full h-full">
|
||||
<div class="flex flex-row gap-2">
|
||||
<DirectoryEntry
|
||||
v-for="item in items"
|
||||
:key="item.name"
|
||||
:name="item.name"
|
||||
:entry-type="item.entryType"
|
||||
v-for="entry in entries"
|
||||
:key="entry.name"
|
||||
:name="entry.name"
|
||||
:entry-type="entry.fileType"
|
||||
:disabled="isLoading"
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
29
frontend/lib/api/warrens.ts
Normal file
29
frontend/lib/api/warrens.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { DirectoryEntry } from '~/types';
|
||||
import type { Warren } from '~/types/warrens';
|
||||
|
||||
export async function getWarrens(): Promise<Record<string, Warren>> {
|
||||
const arr: Warren[] = await $fetch(getApiUrl('warrens'), {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
const warrens: Record<string, Warren> = {};
|
||||
|
||||
for (const warren of arr) {
|
||||
warrens[warren.id] = warren;
|
||||
}
|
||||
|
||||
return warrens;
|
||||
}
|
||||
|
||||
export async function getWarrenDirectory(
|
||||
path: string
|
||||
): Promise<DirectoryEntry[]> {
|
||||
const entries: DirectoryEntry[] = await $fetch(
|
||||
getApiUrl(`warrens/${path}`),
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
|
||||
return entries;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export default defineNuxtConfig({
|
||||
'@nuxt/test-utils',
|
||||
'shadcn-nuxt',
|
||||
'@nuxtjs/color-mode',
|
||||
'@pinia/nuxt',
|
||||
],
|
||||
|
||||
css: ['~/assets/css/tailwind.css'],
|
||||
@@ -53,4 +54,10 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
ssr: false,
|
||||
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
apiBase: '/api',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev": "nuxt dev --dotenv .env.dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
@@ -16,6 +16,7 @@
|
||||
"@nuxt/icon": "1.15.0",
|
||||
"@nuxt/image": "1.10.0",
|
||||
"@nuxt/test-utils": "3.19.2",
|
||||
"@pinia/nuxt": "^0.11.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -23,6 +24,7 @@
|
||||
"eslint": "^9.0.0",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"nuxt": "^3.17.6",
|
||||
"pinia": "^3.0.3",
|
||||
"reka-ui": "^2.3.2",
|
||||
"shadcn-nuxt": "2.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { getWarrenDirectory } from '~/lib/api/warrens';
|
||||
const route = useRoute();
|
||||
const entries = await getWarrenDirectory(route.path.split('/warrens/')[1]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DirectoryList />
|
||||
<DirectoryList :entries="entries" />
|
||||
</template>
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
|
||||
const warrens = ['Thyr', 'Serc'];
|
||||
const store = useWarrenStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ScrollArea class="w-full h-full">
|
||||
<div class="flex flex-row gap-2">
|
||||
<Button
|
||||
v-for="(warren, i) in warrens"
|
||||
:key="i"
|
||||
class="w-36 h-12"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
as-child
|
||||
<NuxtLink
|
||||
v-for="(warren, uuid) in store.warrens"
|
||||
:key="uuid"
|
||||
:to="`/warrens/${uuid}`"
|
||||
>
|
||||
<NuxtLink
|
||||
class="flex flex-row items-center gap-1.5"
|
||||
:to="`/warrens/${warren}`"
|
||||
>
|
||||
<Button class="w-44 h-12" variant="outline" size="lg">
|
||||
<Icon name="lucide:folder-root" />
|
||||
{{ warren }}
|
||||
</NuxtLink>
|
||||
</Button>
|
||||
<span clas="truncate">{{ warren.name }}</span>
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</template>
|
||||
|
||||
8
frontend/stores/index.ts
Normal file
8
frontend/stores/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { Warren } from '~/types/warrens';
|
||||
|
||||
export const useWarrenStore = defineStore('warrens', {
|
||||
state: () => ({
|
||||
warrens: {} as Record<string, Warren>,
|
||||
}),
|
||||
});
|
||||
@@ -1,6 +1,11 @@
|
||||
export type DirectoryEntryType = 'file' | 'directory';
|
||||
export type FileType = 'file' | 'directory';
|
||||
|
||||
export type BreadcrumbData = {
|
||||
name: string;
|
||||
href: string;
|
||||
};
|
||||
|
||||
export type DirectoryEntry = {
|
||||
name: string;
|
||||
fileType: FileType;
|
||||
};
|
||||
|
||||
4
frontend/types/warrens.ts
Normal file
4
frontend/types/warrens.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type Warren = {
|
||||
id: string,
|
||||
name: string,
|
||||
};
|
||||
5
frontend/utils/api.ts
Normal file
5
frontend/utils/api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function getApiUrl(path: string): string {
|
||||
const API_BASE_URL = useRuntimeConfig().public.apiBase;
|
||||
console.log(API_BASE_URL);
|
||||
return `${API_BASE_URL}/${path}`;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { BreadcrumbData } from '~/types';
|
||||
|
||||
export function getBreadcrumbs(path: string): BreadcrumbData[] {
|
||||
const { warrens } = useWarrenStore();
|
||||
|
||||
const crumbs = path
|
||||
.split('/')
|
||||
.filter((v) => v.length > 0)
|
||||
@@ -21,6 +23,14 @@ export function getBreadcrumbs(path: string): BreadcrumbData[] {
|
||||
.join('/');
|
||||
}
|
||||
|
||||
if (
|
||||
crumbs.length >= 3 &&
|
||||
crumbs[1].href === '/warrens' &&
|
||||
crumbs[2].name in warrens
|
||||
) {
|
||||
crumbs[2].name = warrens[crumbs[2].name].name;
|
||||
}
|
||||
|
||||
return crumbs;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user