diff --git a/frontend/src/components/layout/DashboardLayout.vue b/frontend/src/components/layout/DashboardLayout.vue index 6be1976..44d7191 100644 --- a/frontend/src/components/layout/DashboardLayout.vue +++ b/frontend/src/components/layout/DashboardLayout.vue @@ -28,6 +28,7 @@ import { FileText, FolderOpen, Crosshair, + Navigation2, Menu, X, } from 'lucide-vue-next' @@ -46,6 +47,7 @@ const navItems = [ { name: 'Plugins', path: '/plugins', icon: Puzzle, permission: 'plugins.view' }, { name: 'File Manager', path: '/files', icon: FolderOpen, permission: 'files.view' }, { name: 'Loot Builder', path: '/loot-builder', icon: Crosshair, permission: 'loot.view' }, + { name: 'Teleport Config', path: '/teleport-config', icon: Navigation2, permission: 'teleport.view' }, { name: 'Auto-Wiper', path: '/wipes', icon: RefreshCw, permission: 'wipes.view' }, { name: 'Maps', path: '/maps', icon: Map, permission: 'maps.view' }, { name: 'Chat Log', path: '/chat', icon: MessageSquare, permission: 'chat.view' }, diff --git a/frontend/src/components/teleport/PermissionGroupEditor.vue b/frontend/src/components/teleport/PermissionGroupEditor.vue new file mode 100644 index 0000000..3554c39 --- /dev/null +++ b/frontend/src/components/teleport/PermissionGroupEditor.vue @@ -0,0 +1,195 @@ + + + diff --git a/frontend/src/components/teleport/WarpEditor.vue b/frontend/src/components/teleport/WarpEditor.vue new file mode 100644 index 0000000..1da6e27 --- /dev/null +++ b/frontend/src/components/teleport/WarpEditor.vue @@ -0,0 +1,76 @@ + + + diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 17c1dfd..8c130e9 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -115,6 +115,11 @@ const panelRoutes: RouteRecordRaw[] = [ name: 'loot-builder', component: () => import('@/views/admin/LootBuilderView.vue'), }, + { + path: 'teleport-config', + name: 'teleport-config', + component: () => import('@/views/admin/TeleportConfigView.vue'), + }, { path: 'wipes', name: 'wipes', diff --git a/frontend/src/stores/teleport.ts b/frontend/src/stores/teleport.ts new file mode 100644 index 0000000..6a0a89a --- /dev/null +++ b/frontend/src/stores/teleport.ts @@ -0,0 +1,145 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { useApi } from '@/composables/useApi' +import { useToastStore } from '@/stores/toast' +import type { TeleportConfigSummary, TeleportConfigFull, TeleportApplyResult } from '@/types' + +export const useTeleportStore = defineStore('teleport', () => { + const configs = ref([]) + const currentConfig = ref(null) + const isLoading = ref(false) + const isSaving = ref(false) + const isApplying = ref(false) + const isDirty = ref(false) + const api = useApi() + const toast = useToastStore() + + async function fetchConfigs() { + isLoading.value = true + try { + const res = await api.get<{ configs: TeleportConfigSummary[] }>('/teleport/configs') + configs.value = res.configs + } catch (err) { + toast.error((err as Error).message) + } finally { + isLoading.value = false + } + } + + async function loadConfig(id: string) { + isLoading.value = true + try { + const res = await api.get<{ config: TeleportConfigFull }>(`/teleport/configs/${id}`) + currentConfig.value = res.config + isDirty.value = false + } catch (err) { + toast.error((err as Error).message) + } finally { + isLoading.value = false + } + } + + async function createConfig(name: string, description?: string) { + try { + const res = await api.post<{ config: TeleportConfigFull }>('/teleport/configs', { + config_name: name, + description, + }) + await fetchConfigs() + currentConfig.value = res.config + isDirty.value = false + toast.success(`Config "${name}" created`) + return res.config + } catch (err) { + toast.error((err as Error).message) + return null + } + } + + async function saveCurrentConfig() { + if (!currentConfig.value) return + isSaving.value = true + try { + await api.put(`/teleport/configs/${currentConfig.value.id}`, { + config_name: currentConfig.value.config_name, + description: currentConfig.value.description, + config_data: currentConfig.value.config_data, + }) + isDirty.value = false + await fetchConfigs() + toast.success('Config saved') + } catch (err) { + toast.error((err as Error).message) + } finally { + isSaving.value = false + } + } + + async function deleteConfig(id: string) { + try { + await api.del(`/teleport/configs/${id}`) + if (currentConfig.value?.id === id) { + currentConfig.value = null + } + await fetchConfigs() + toast.success('Config deleted') + } catch (err) { + toast.error((err as Error).message) + } + } + + async function applyToServer(id: string) { + isApplying.value = true + try { + const res = await api.post(`/teleport/configs/${id}/apply`) + await fetchConfigs() + toast.success(res.message) + return res + } catch (err) { + toast.error((err as Error).message) + return null + } finally { + isApplying.value = false + } + } + + async function importFromServer(configName: string) { + isLoading.value = true + try { + const res = await api.post<{ config: TeleportConfigFull }>('/teleport/import-from-server', { + config_name: configName, + }) + await fetchConfigs() + currentConfig.value = res.config + isDirty.value = false + toast.success(`Config imported from server as "${configName}"`) + return res.config + } catch (err) { + toast.error((err as Error).message) + return null + } finally { + isLoading.value = false + } + } + + function markDirty() { + isDirty.value = true + } + + return { + configs, + currentConfig, + isLoading, + isSaving, + isApplying, + isDirty, + fetchConfigs, + loadConfig, + createConfig, + saveCurrentConfig, + deleteConfig, + applyToServer, + importFromServer, + markDirty, + } +}) diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 4691e83..5b3c1be 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -519,3 +519,30 @@ export interface LootApplyResult { profile_name: string multiplier: number } + +// Teleport Config types — NTeleportation integration +export interface TeleportConfigSummary { + id: string + config_name: string + description: string | null + is_active: boolean + created_at: string + updated_at: string +} + +export interface TeleportConfigFull { + id: string + license_id: string + config_name: string + description: string | null + config_data: Record + is_active: boolean + created_at: string + updated_at: string +} + +export interface TeleportApplyResult { + success: boolean + message: string + config_name: string +} diff --git a/frontend/src/views/admin/TeleportConfigView.vue b/frontend/src/views/admin/TeleportConfigView.vue new file mode 100644 index 0000000..b53b786 --- /dev/null +++ b/frontend/src/views/admin/TeleportConfigView.vue @@ -0,0 +1,694 @@ + + +