Skip to content

Commit 364006f

Browse files
committed
implement file explorer
1 parent f02c4de commit 364006f

4 files changed

Lines changed: 128 additions & 41 deletions

File tree

src/router/index.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ const router = createRouter({
5656
component: () => import('../views/DesktopShellView.vue'),
5757
meta: { requiresAuth: true, hideNavbar: true },
5858
},
59+
{
60+
path: '/desktops/:realm/files',
61+
name: 'desktop-files',
62+
component: () => import('../views/DesktopFileExplorerView.vue'),
63+
meta: { requiresAuth: true, hideNavbar: true },
64+
},
5965
],
6066
})
6167

@@ -71,4 +77,32 @@ router.beforeEach((to, from, next) => {
7177
}
7278
})
7379

80+
export function openDesktop(realm: string) {
81+
openDesktopWindow(`/desktops/${realm}`)
82+
}
83+
84+
export function openFileExplorer(realm: string, desktopName?: string) {
85+
router.push({
86+
name: 'desktop-files',
87+
params: { realm },
88+
query: desktopName ? { name: desktopName } : {},
89+
})
90+
}
91+
92+
function openDesktopWindow(path: string) {
93+
const url = router.resolve(path).href
94+
95+
const width = 1000
96+
const height = 700
97+
98+
const left = window.screenX + (window.outerWidth - width) / 2
99+
const top = window.screenY + (window.outerHeight - height) / 2
100+
101+
window.open(
102+
url,
103+
'_blank',
104+
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`,
105+
)
106+
}
107+
74108
export default router

src/types/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,30 @@ export interface Desktop {
3333
realm: string
3434
icon: string
3535
}
36+
37+
export interface FileEntry {
38+
name: string
39+
path: string
40+
type: string
41+
mode: string
42+
size: number
43+
hidden: boolean
44+
mod_time: string
45+
is_dir: boolean
46+
is_symlink: boolean
47+
link_target?: string
48+
}
49+
50+
export interface FileBrowseResult {
51+
path: string
52+
home_path: string
53+
parent_path?: string
54+
type: string
55+
mode: string
56+
size: number
57+
mod_time: string
58+
is_dir: boolean
59+
is_symlink: boolean
60+
link_target?: string
61+
entries?: FileEntry[]
62+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue'
3+
import { useRoute, useRouter } from 'vue-router'
4+
5+
import EmbeddedDesktopFileExplorer from '@/components/EmbeddedDesktopFileExplorer.vue'
6+
7+
const route = useRoute()
8+
const router = useRouter()
9+
10+
const realm = computed(() => String(route.params.realm || ''))
11+
const desktopName = computed(() => {
12+
const queryName = route.query.name
13+
return typeof queryName === 'string' ? queryName : undefined
14+
})
15+
16+
function closeExplorer() {
17+
router.push('/')
18+
}
19+
</script>
20+
21+
<template>
22+
<div class="container py-5 fade-in-up">
23+
<div class="row justify-content-center">
24+
<div class="col-lg-10">
25+
<div class="mb-4">
26+
<button class="btn btn-link text-decoration-none px-0 back-link" @click="closeExplorer">
27+
<i class="bi bi-arrow-left me-2"></i>Back to dashboard
28+
</button>
29+
</div>
30+
31+
<EmbeddedDesktopFileExplorer
32+
:realm="realm"
33+
:desktop-name="desktopName"
34+
@close="closeExplorer"
35+
/>
36+
</div>
37+
</div>
38+
</div>
39+
</template>
40+
41+
<style scoped>
42+
.back-link {
43+
color: #617182;
44+
font-weight: 600;
45+
}
46+
47+
.back-link:hover {
48+
color: #1f2a37;
49+
}
50+
</style>

src/views/HomeView.vue

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
11
<script setup lang="ts">
2-
import { ref, onMounted, watch } from 'vue'
3-
import { useRouter } from 'vue-router'
2+
import { ref, watch } from 'vue'
43
4+
import { openDesktop, openFileExplorer } from '../router'
55
import { useAuthStore } from '../stores/auth'
66
import { authService } from '../services/authService'
77
import type { Organization, Desktop } from '../types'
88
99
const authStore = useAuthStore()
10-
const router = useRouter()
11-
12-
function openDesktop(realm: string) {
13-
const url = router.resolve(`/desktops/${realm}`).href
14-
15-
const width = 1000
16-
const height = 700
17-
18-
const left = window.screenX + (window.outerWidth - width) / 2
19-
const top = window.screenY + (window.outerHeight - height) / 2
20-
21-
window.open(
22-
url,
23-
'_blank',
24-
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`,
25-
)
26-
}
2710
2811
// State
2912
const organizations = ref<Organization[]>([])
@@ -78,7 +61,6 @@ const fetchDesktops = async () => {
7861
}
7962
}
8063
81-
// Watch for session changes (e.g., after auto-login)
8264
watch(
8365
() => authStore.session,
8466
(newSession) => {
@@ -93,18 +75,6 @@ watch(
9375
{ immediate: true },
9476
)
9577
96-
onMounted(() => {
97-
// If session is already present, fetch immediately
98-
if (authStore.session) {
99-
fetchOrganizations()
100-
fetchDesktops()
101-
} else {
102-
// If not, the watcher will handle it, but let's make sure we don't hang forever
103-
// if auto-login is not even running or fails.
104-
// authStore.autoLogin is called in App.vue, so we wait for its result indirectly.
105-
}
106-
})
107-
10878
const handleOpenModal = () => {
10979
showModal.value = true
11080
}
@@ -181,7 +151,10 @@ const handleCreateOrg = async () => {
181151

182152
<div v-else class="row g-4">
183153
<div v-for="desktop in desktops" :key="desktop.realm" class="col-md-4">
184-
<div class="card h-100 border-0 shadow-sm">
154+
<div
155+
class="card h-100 border-0 shadow-sm card-hover desktop-card"
156+
@click="openFileExplorer(desktop.realm, desktop.name)"
157+
>
185158
<div class="card-body p-4 d-flex flex-column">
186159
<div class="d-flex align-items-center mb-3">
187160
<span class="fs-2 me-3">{{ desktop.icon }}</span>
@@ -191,23 +164,22 @@ const handleCreateOrg = async () => {
191164
</div>
192165

193166
<div class="mt-auto d-flex justify-content-between align-items-center">
194-
<span class="badge bg-light text-secondary rounded-pill"> Desktop </span>
167+
<span class="badge bg-light text-secondary rounded-pill"> Open Explorer </span>
195168
<button
196169
class="btn btn-sm btn-outline-primary"
197-
@click="openDesktop(desktop.realm)"
170+
@click.stop="openDesktop(desktop.realm)"
198171
title="Open Terminal">
199172
<i class="bi bi-terminal"></i>
200173
</button>
201174
</div>
202175
</div>
203176
</div>
177+
</div>
178+
</div>
204179

205-
<!-- Empty State -->
206-
<div v-if="desktops.length === 0" class="col-12">
207-
<div class="card border-dashed p-5 text-center bg-transparent">
208-
<p class="text-muted mb-0">No desktops found</p>
209-
</div>
210-
</div>
180+
<div v-if="desktops.length === 0" class="col-12">
181+
<div class="card border-dashed p-5 text-center bg-transparent">
182+
<p class="text-muted mb-0">No desktops found</p>
211183
</div>
212184
</div>
213185
</div>
@@ -327,6 +299,10 @@ const handleCreateOrg = async () => {
327299
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1) !important;
328300
}
329301
302+
.desktop-card {
303+
cursor: pointer;
304+
}
305+
330306
/* Theme Primary Button Override for consistency */
331307
.btn-theme-primary {
332308
background-color: var(--theme-yellow) !important;

0 commit comments

Comments
 (0)