From ef4c2e31613b50da97d946e299e7a50f6003e502 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 27 Apr 2026 14:10:10 +0100 Subject: [PATCH 01/18] feat: reshuffle layout for worlds --- apps/app-frontend/src/App.vue | 49 ++++ .../src/pages/hosting/manage/Backups.vue | 2 +- .../src/pages/hosting/manage/Content.vue | 2 +- .../src/pages/hosting/manage/Index.vue | 2 +- .../src/pages/hosting/manage/World.vue | 16 ++ .../src/pages/hosting/manage/index.js | 3 +- .../providers/setup/server-install-content.ts | 9 +- apps/app-frontend/src/routes.js | 34 +++ .../composables/use-server-install-content.ts | 14 +- .../middleware/server-world-tabs.global.ts | 40 ++++ .../hosting/manage/[id]/worlds/[world_id].vue | 11 + .../[id]/{ => worlds/[world_id]}/backups.vue | 2 +- .../[id]/{ => worlds/[world_id]}/files.vue | 0 .../[world_id]/index.vue} | 2 +- .../components/servers/ServerManageStats.vue | 6 +- .../servers/ServerSettingsModal.vue | 2 +- .../admonitions/ServerPanelAdmonitions.vue | 6 +- .../servers/backups/BackupCreateModal.vue | 4 +- .../servers/backups/BackupRenameModal.vue | 4 +- .../servers/backups/BackupRestoreModal.vue | 6 +- .../server-header/PanelServerActionButton.vue | 21 +- .../server-header/ServerManageHeader.vue | 144 +++++------- .../src/composables/server-backups-queue.ts | 2 +- .../server-settings/pages/installation.vue | 11 +- .../server-settings/pages/properties.vue | 6 +- .../hosting/manage/[id]/onboarding.vue | 6 +- .../wrapped/hosting/manage/backups.vue | 6 +- .../wrapped/hosting/manage/content.vue | 8 +- .../layouts/wrapped/hosting/manage/root.vue | 83 ++++--- .../wrapped/hosting/manage/world-root.vue | 213 ++++++++++++++++++ .../layouts/wrapped/hosting/manage/worlds.vue | 12 +- packages/ui/src/layouts/wrapped/index.ts | 1 + .../servers/ServerPanelAdmonitions.stories.ts | 2 +- 33 files changed, 550 insertions(+), 179 deletions(-) create mode 100644 apps/app-frontend/src/pages/hosting/manage/World.vue create mode 100644 apps/frontend/src/middleware/server-world-tabs.global.ts create mode 100644 apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id].vue rename apps/frontend/src/pages/hosting/manage/[id]/{ => worlds/[world_id]}/backups.vue (93%) rename apps/frontend/src/pages/hosting/manage/[id]/{ => worlds/[world_id]}/files.vue (100%) rename apps/frontend/src/pages/hosting/manage/[id]/{content.vue => worlds/[world_id]/index.vue} (96%) create mode 100644 packages/ui/src/layouts/wrapped/hosting/manage/world-root.vue diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index c841f5fc9c..f7f0fbccd2 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -482,6 +482,11 @@ const sidebarOverlayScrollbarsOptions = Object.freeze({ }, }) +router.beforeEach(async (to) => { + const redirect = await resolveLegacyServerWorldTabRedirect(to) + if (redirect) return redirect +}) + router.beforeEach(() => { suspensePending = false if (routerToken) loading.end(routerToken) @@ -513,6 +518,50 @@ function onSuspensePending() { suspenseToken = loading.begin() } +async function resolveLegacyServerWorldTabRedirect(to) { + if (!['ServerManageContent', 'ServerManageFiles', 'ServerManageBackups'].includes(to.name)) { + return null + } + + const serverId = getRouteParam(to.params.id) + if (!serverId) return null + + const tabPath = + to.name === 'ServerManageFiles' ? '/files' : to.name === 'ServerManageBackups' ? '/backups' : '' + const worldsPath = `/hosting/manage/${encodeURIComponent(serverId)}/worlds` + + try { + const serverFull = await tauriApiClient.archon.servers_v1.get(serverId) + const world = serverFull.worlds.find((item) => item.is_active) ?? serverFull.worlds[0] + if (world) { + return { + path: `${worldsPath}/${encodeURIComponent(world.id)}${tabPath}`, + query: to.query, + hash: to.hash, + replace: true, + } + } + } catch { + return { + path: worldsPath, + query: to.query, + hash: to.hash, + replace: true, + } + } + + return { + path: worldsPath, + query: to.query, + hash: to.hash, + replace: true, + } +} + +function getRouteParam(param) { + return Array.isArray(param) ? param[0] : param +} + function onSuspenseResolve() { if (suspenseToken) { loading.end(suspenseToken) diff --git a/apps/app-frontend/src/pages/hosting/manage/Backups.vue b/apps/app-frontend/src/pages/hosting/manage/Backups.vue index 0ebb7bd800..b62de2ba6d 100644 --- a/apps/app-frontend/src/pages/hosting/manage/Backups.vue +++ b/apps/app-frontend/src/pages/hosting/manage/Backups.vue @@ -13,7 +13,7 @@ const queryClient = useQueryClient() if (worldId.value) { try { await queryClient.ensureQueryData({ - queryKey: ['backups', 'list', serverId], + queryKey: ['backups', 'list', serverId, worldId.value], queryFn: () => client.archon.backups_v1.list(serverId, worldId.value!), staleTime: 30_000, }) diff --git a/apps/app-frontend/src/pages/hosting/manage/Content.vue b/apps/app-frontend/src/pages/hosting/manage/Content.vue index ab7d242452..1306234cc3 100644 --- a/apps/app-frontend/src/pages/hosting/manage/Content.vue +++ b/apps/app-frontend/src/pages/hosting/manage/Content.vue @@ -13,7 +13,7 @@ const queryClient = useQueryClient() if (worldId.value) { try { await queryClient.ensureQueryData({ - queryKey: ['content', 'list', 'v1', serverId], + queryKey: ['content', 'list', 'v1', serverId, worldId.value], queryFn: () => client.archon.content_v1.getAddons(serverId, worldId.value!, { from_modpack: false }), staleTime: 30_000, diff --git a/apps/app-frontend/src/pages/hosting/manage/Index.vue b/apps/app-frontend/src/pages/hosting/manage/Index.vue index f1e4388755..030f74a7e3 100644 --- a/apps/app-frontend/src/pages/hosting/manage/Index.vue +++ b/apps/app-frontend/src/pages/hosting/manage/Index.vue @@ -93,7 +93,7 @@ watch( breadcrumbs.setName('Server', server.name) breadcrumbs.setContext({ name: server.name, - link: `/hosting/manage/${serverId.value}/content`, + link: `/hosting/manage/${serverId.value}/worlds`, }) } }, diff --git a/apps/app-frontend/src/pages/hosting/manage/World.vue b/apps/app-frontend/src/pages/hosting/manage/World.vue new file mode 100644 index 0000000000..fe52492c50 --- /dev/null +++ b/apps/app-frontend/src/pages/hosting/manage/World.vue @@ -0,0 +1,16 @@ + + + diff --git a/apps/app-frontend/src/pages/hosting/manage/index.js b/apps/app-frontend/src/pages/hosting/manage/index.js index e06f46e2a2..6e77f9e638 100644 --- a/apps/app-frontend/src/pages/hosting/manage/index.js +++ b/apps/app-frontend/src/pages/hosting/manage/index.js @@ -3,6 +3,7 @@ import Content from './Content.vue' import Files from './Files.vue' import Index from './Index.vue' import Overview from './Overview.vue' +import World from './World.vue' import Worlds from './Worlds.vue' -export { Backups, Content, Files, Index, Overview, Worlds } +export { Backups, Content, Files, Index, Overview, World, Worlds } diff --git a/apps/app-frontend/src/providers/setup/server-install-content.ts b/apps/app-frontend/src/providers/setup/server-install-content.ts index eea327e639..7b82793a85 100644 --- a/apps/app-frontend/src/providers/setup/server-install-content.ts +++ b/apps/app-frontend/src/providers/setup/server-install-content.ts @@ -252,7 +252,7 @@ export function createServerInstallContent(opts: { if (serverFlowFrom.value === 'reset-server') { return `/hosting/manage/${sid}?openSettings=installation` } - return `/hosting/manage/${sid}/content` + return getServerWorldContentPath(sid, effectiveServerWorldId.value) }) const serverBackLabel = computed(() => { if (serverFlowFrom.value === 'onboarding') return 'Back to setup' @@ -556,7 +556,7 @@ export function createServerInstallContent(opts: { if (serverFlowFrom.value === 'onboarding') { await client.archon.servers_v1.endIntro(sid) - await router.push(`/hosting/manage/${sid}/content`) + await router.push(getServerWorldContentPath(sid, wid)) return } @@ -571,6 +571,11 @@ export function createServerInstallContent(opts: { serverContentProjectIds.value = new Set([...serverContentProjectIds.value, id]) } + function getServerWorldContentPath(serverId: string, worldId: string | null) { + const base = `/hosting/manage/${encodeURIComponent(serverId)}/worlds` + return worldId ? `${base}/${encodeURIComponent(worldId)}` : base + } + return { serverIdQuery, worldIdQuery, diff --git a/apps/app-frontend/src/routes.js b/apps/app-frontend/src/routes.js index 410869c46a..e0b131f3d7 100644 --- a/apps/app-frontend/src/routes.js +++ b/apps/app-frontend/src/routes.js @@ -65,6 +65,40 @@ export default new createRouter({ breadcrumb: [{ name: '?Server' }], }, }, + { + path: 'worlds/:world_id', + name: 'ServerManageWorld', + component: Hosting.World, + meta: { + breadcrumb: [{ name: '?Server' }], + }, + children: [ + { + path: '', + name: 'ServerManageWorldContent', + component: Hosting.Content, + meta: { + breadcrumb: [{ name: '?Server' }], + }, + }, + { + path: 'files', + name: 'ServerManageWorldFiles', + component: Hosting.Files, + meta: { + breadcrumb: [{ name: '?Server' }], + }, + }, + { + path: 'backups', + name: 'ServerManageWorldBackups', + component: Hosting.Backups, + meta: { + breadcrumb: [{ name: '?Server' }], + }, + }, + ], + }, { path: 'files', name: 'ServerManageFiles', diff --git a/apps/frontend/src/composables/use-server-install-content.ts b/apps/frontend/src/composables/use-server-install-content.ts index 75b3c0d232..871e709fbe 100644 --- a/apps/frontend/src/composables/use-server-install-content.ts +++ b/apps/frontend/src/composables/use-server-install-content.ts @@ -187,7 +187,10 @@ export function useServerInstallContent({ }, } - const contentQueryKey = computed(() => ['content', 'list', currentServerId.value ?? ''] as const) + const contentQueryKey = computed( + () => + ['content', 'list', 'v1', currentServerId.value ?? '', currentWorldId.value ?? null] as const, + ) const { data: serverContentData, error: serverContentError } = useQuery({ queryKey: contentQueryKey, queryFn: () => @@ -625,7 +628,7 @@ export function useServerInstallContent({ if (fromContext.value === 'onboarding') { await client.archon.servers_v1.endIntro(currentServerId.value) queryClient.invalidateQueries({ queryKey: ['servers', 'detail', currentServerId.value] }) - navigateTo(`/hosting/manage/${currentServerId.value}/content`) + navigateTo(getServerWorldContentPath(currentServerId.value, currentWorldId.value ?? null)) } else { navigateTo(`/hosting/manage/${currentServerId.value}?openSettings=installation`) } @@ -641,9 +644,14 @@ export function useServerInstallContent({ if (fromContext.value === 'onboarding') return `/hosting/manage/${id}?resumeModal=setup-type` if (fromContext.value === 'reset-server') return `/hosting/manage/${id}?openSettings=installation` - return `/hosting/manage/${id}/content` + return getServerWorldContentPath(id, currentWorldId.value) }) + function getServerWorldContentPath(serverId: string, worldId: string | null) { + const base = `/hosting/manage/${encodeURIComponent(serverId)}/worlds` + return worldId ? `${base}/${encodeURIComponent(worldId)}` : base + } + const serverBackLabel = computed(() => { if (fromContext.value === 'onboarding') return formatMessage(messages.backToSetup) if (fromContext.value === 'reset-server') return formatMessage(messages.cancelReset) diff --git a/apps/frontend/src/middleware/server-world-tabs.global.ts b/apps/frontend/src/middleware/server-world-tabs.global.ts new file mode 100644 index 0000000000..2a740b7dea --- /dev/null +++ b/apps/frontend/src/middleware/server-world-tabs.global.ts @@ -0,0 +1,40 @@ +import { createModrinthClient } from '~/helpers/api.ts' + +export default defineNuxtRouteMiddleware(async (to) => { + const match = to.path.match(/^\/hosting\/manage\/([^/]+)\/(content|files|backups)\/?$/) + if (!match) return + + const serverId = decodeURIComponent(match[1]) + const tab = match[2] + const worldsPath = `/hosting/manage/${encodeURIComponent(serverId)}/worlds` + const tabPath = tab === 'content' ? '' : `/${tab}` + const auth = await useAuth() + + if (auth.value.token) { + try { + const config = useRuntimeConfig() + const client = createModrinthClient(auth, { + apiBaseUrl: config.public.apiBaseUrl.replace('/v2/', '/'), + archonBaseUrl: config.public.pyroBaseUrl.replace('/v2/', '/'), + rateLimitKey: config.rateLimitKey, + }) + const serverFull = await client.archon.servers_v1.get(serverId) + const world = serverFull.worlds.find((item) => item.is_active) ?? serverFull.worlds[0] + + if (world) { + return navigateTo( + { + path: `${worldsPath}/${encodeURIComponent(world.id)}${tabPath}`, + query: to.query, + hash: to.hash, + }, + { replace: true }, + ) + } + } catch { + return navigateTo({ path: worldsPath, query: to.query, hash: to.hash }, { replace: true }) + } + } + + return navigateTo({ path: worldsPath, query: to.query, hash: to.hash }, { replace: true }) +}) diff --git a/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id].vue b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id].vue new file mode 100644 index 0000000000..357dc14f4a --- /dev/null +++ b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id].vue @@ -0,0 +1,11 @@ + + + diff --git a/apps/frontend/src/pages/hosting/manage/[id]/backups.vue b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/backups.vue similarity index 93% rename from apps/frontend/src/pages/hosting/manage/[id]/backups.vue rename to apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/backups.vue index 467662653f..ab36852f6a 100644 --- a/apps/frontend/src/pages/hosting/manage/[id]/backups.vue +++ b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/backups.vue @@ -14,7 +14,7 @@ const flags = useFeatureFlags() if (worldId.value) { try { await queryClient.ensureQueryData({ - queryKey: ['backups', 'list', serverId], + queryKey: ['backups', 'list', serverId, worldId.value], queryFn: () => client.archon.backups_v1.list(serverId, worldId.value!), staleTime: 30_000, }) diff --git a/apps/frontend/src/pages/hosting/manage/[id]/files.vue b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/files.vue similarity index 100% rename from apps/frontend/src/pages/hosting/manage/[id]/files.vue rename to apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/files.vue diff --git a/apps/frontend/src/pages/hosting/manage/[id]/content.vue b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/index.vue similarity index 96% rename from apps/frontend/src/pages/hosting/manage/[id]/content.vue rename to apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/index.vue index 8530076bff..e5e8e9e517 100644 --- a/apps/frontend/src/pages/hosting/manage/[id]/content.vue +++ b/apps/frontend/src/pages/hosting/manage/[id]/worlds/[world_id]/index.vue @@ -38,7 +38,7 @@ const contentWorldId = await getContentWorldId() if (contentWorldId) { try { const content = await queryClient.ensureQueryData({ - queryKey: ['content', 'list', 'v1', serverId], + queryKey: ['content', 'list', 'v1', serverId, contentWorldId], queryFn: () => client.archon.content_v1.getAddons(serverId, contentWorldId, { from_modpack: false }), staleTime: 30_000, diff --git a/packages/ui/src/components/servers/ServerManageStats.vue b/packages/ui/src/components/servers/ServerManageStats.vue index b817cab303..77effe85ad 100644 --- a/packages/ui/src/components/servers/ServerManageStats.vue +++ b/packages/ui/src/components/servers/ServerManageStats.vue @@ -77,7 +77,7 @@ onMounted(() => { isClient.value = true }) -const { serverId } = injectModrinthServerContext() +const { serverId, worldId } = injectModrinthServerContext() const { featureFlags } = injectPageContext() const props = withDefaults( @@ -190,7 +190,9 @@ const metrics = computed(() => { showGraph: false, chartOptions: null as ReturnType | null, series: null as { name: string; data: number[] }[] | null, - link: `/hosting/manage/${encodeURIComponent(serverId)}/files`, + link: worldId.value + ? `/hosting/manage/${encodeURIComponent(serverId)}/worlds/${encodeURIComponent(worldId.value)}/files` + : `/hosting/manage/${encodeURIComponent(serverId)}/worlds`, } if (props.loading) { diff --git a/packages/ui/src/components/servers/ServerSettingsModal.vue b/packages/ui/src/components/servers/ServerSettingsModal.vue index a72001c73b..6575ad2afa 100644 --- a/packages/ui/src/components/servers/ServerSettingsModal.vue +++ b/packages/ui/src/components/servers/ServerSettingsModal.vue @@ -186,7 +186,7 @@ async function show({ serverId, tabIndex, tabId }: ShowOptions) { queryFn: () => client.archon.properties_v1.getProperties(targetServerId, worldId.value!), }) queryClient.prefetchQuery({ - queryKey: ['content', 'list', 'v1', targetServerId], + queryKey: ['content', 'list', 'v1', targetServerId, worldId.value], queryFn: () => client.archon.content_v1.getAddons(targetServerId, worldId.value!, { from_modpack: false, diff --git a/packages/ui/src/components/servers/admonitions/ServerPanelAdmonitions.vue b/packages/ui/src/components/servers/admonitions/ServerPanelAdmonitions.vue index c157327f99..c54fcb7b8d 100644 --- a/packages/ui/src/components/servers/admonitions/ServerPanelAdmonitions.vue +++ b/packages/ui/src/components/servers/admonitions/ServerPanelAdmonitions.vue @@ -53,8 +53,12 @@ const messages = defineMessages({ }, }) -const isOnContentTab = computed(() => route.path.includes('/content')) const isOnFilesTab = computed(() => route.path.includes('/files')) +const isOnContentTab = computed( + () => + route.path.includes('/content') || + (!!route.params.world_id && !isOnFilesTab.value && !route.path.includes('/backups')), +) const bannerCoversInstalling = computed( () => diff --git a/packages/ui/src/components/servers/backups/BackupCreateModal.vue b/packages/ui/src/components/servers/backups/BackupCreateModal.vue index bd9e53c4a1..c6aaf0f994 100644 --- a/packages/ui/src/components/servers/backups/BackupCreateModal.vue +++ b/packages/ui/src/components/servers/backups/BackupCreateModal.vue @@ -87,12 +87,12 @@ const props = defineProps<{ backups?: Archon.BackupsQueue.v1.BackupQueueBackup[] }>() -const backupsQueryKey = ['backups', 'queue', ctx.serverId] +const backupsQueryKey = computed(() => ['backups', 'queue', ctx.serverId, ctx.worldId.value]) const createMutation = useMutation({ mutationFn: (name: string) => client.archon.backups_queue_v1.create(ctx.serverId, ctx.worldId.value!, { name }), - onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey }), + onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey.value }), }) const modal = ref>() diff --git a/packages/ui/src/components/servers/backups/BackupRenameModal.vue b/packages/ui/src/components/servers/backups/BackupRenameModal.vue index eaed166ea1..b9c65c8605 100644 --- a/packages/ui/src/components/servers/backups/BackupRenameModal.vue +++ b/packages/ui/src/components/servers/backups/BackupRenameModal.vue @@ -69,12 +69,12 @@ const props = defineProps<{ backups?: Archon.BackupsQueue.v1.BackupQueueBackup[] }>() -const backupsQueryKey = ['backups', 'queue', ctx.serverId] +const backupsQueryKey = computed(() => ['backups', 'queue', ctx.serverId, ctx.worldId.value]) const renameMutation = useMutation({ mutationFn: ({ backupId, name }: { backupId: string; name: string }) => client.archon.backups_v1.rename(ctx.serverId, ctx.worldId.value!, backupId, { name }), - onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey }), + onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey.value }), }) const modal = ref>() diff --git a/packages/ui/src/components/servers/backups/BackupRestoreModal.vue b/packages/ui/src/components/servers/backups/BackupRestoreModal.vue index 975c27f654..3743774a06 100644 --- a/packages/ui/src/components/servers/backups/BackupRestoreModal.vue +++ b/packages/ui/src/components/servers/backups/BackupRestoreModal.vue @@ -39,7 +39,7 @@ import type { Archon } from '@modrinth/api-client' import { RotateCounterClockwiseIcon, SpinnerIcon, XIcon } from '@modrinth/assets' import { useMutation, useQueryClient } from '@tanstack/vue-query' -import { ref } from 'vue' +import { computed, ref } from 'vue' import { injectModrinthClient, @@ -56,7 +56,7 @@ const client = injectModrinthClient() const queryClient = useQueryClient() const ctx = injectModrinthServerContext() -const backupsQueryKey = ['backups', 'queue', ctx.serverId] +const backupsQueryKey = computed(() => ['backups', 'queue', ctx.serverId, ctx.worldId.value]) function safetyBackupName(backupName: string) { const base = `Before restoring "${backupName}"` @@ -66,7 +66,7 @@ function safetyBackupName(backupName: string) { const restoreMutation = useMutation({ mutationFn: ({ backupId, name }: { backupId: string; name: string }) => client.archon.backups_queue_v1.restore(ctx.serverId, ctx.worldId.value!, backupId, { name }), - onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey }), + onSuccess: () => queryClient.invalidateQueries({ queryKey: backupsQueryKey.value }), }) const modal = ref>() diff --git a/packages/ui/src/components/servers/server-header/PanelServerActionButton.vue b/packages/ui/src/components/servers/server-header/PanelServerActionButton.vue index fd6cd6930d..256f98bf37 100644 --- a/packages/ui/src/components/servers/server-header/PanelServerActionButton.vue +++ b/packages/ui/src/components/servers/server-header/PanelServerActionButton.vue @@ -1,14 +1,14 @@ @@ -348,6 +342,7 @@ import { CheckIcon, CopyIcon, FileIcon, + GlobeIcon, IssuesIcon, LayoutTemplateIcon, LoaderCircleIcon, @@ -356,7 +351,6 @@ import { SettingsIcon, TransferIcon, TriangleAlertIcon, - WorldIcon, XIcon, } from '@modrinth/assets' import type { Stats } from '@modrinth/utils' @@ -374,11 +368,7 @@ import ServerNotice from '#ui/components/base/ServerNotice.vue' import ConfirmLeaveModal from '#ui/components/modal/ConfirmLeaveModal.vue' import ServerPanelAdmonitions from '#ui/components/servers/admonitions/ServerPanelAdmonitions.vue' import MedalServerCountdown from '#ui/components/servers/marketing/MedalServerCountdown.vue' -import { - PanelServerActionButton, - PanelServerOverflowMenu, - ServerManageHeader, -} from '#ui/components/servers/server-header' +import { PanelServerActionButton, ServerManageHeader } from '#ui/components/servers/server-header' import ServerSettingsModal from '#ui/components/servers/ServerSettingsModal.vue' import { useDebugLogger, @@ -419,7 +409,6 @@ const props = withDefaults( serverId: string reloadPage: () => void resolveViewer: () => Promise<{ userId: string | null; userRole: string | null }> - showCopyIdAction?: boolean showAdvancedDebugInfo?: boolean showUptime?: boolean additionalTabs?: Tab[] @@ -441,7 +430,6 @@ const props = withDefaults( }) => void | Promise }>(), { - showCopyIdAction: false, showAdvancedDebugInfo: false, showUptime: true, additionalTabs: () => [], @@ -1037,7 +1025,7 @@ const navLinks = computed(() => [ { label: formatMessage(messages.instancesNav), href: `/hosting/manage/${encodeURIComponent(props.serverId)}/instances`, - icon: WorldIcon, + icon: GlobeIcon, subpages: [], }, ...props.additionalTabs, From 443e17f9d551273d32cdceeaa17322e371894517 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 18 May 2026 10:43:46 +0100 Subject: [PATCH 09/18] fix: lint --- .../composables/use-server-install-content.ts | 4 +- packages/ui/src/layouts/wrapped/index.ts | 4 +- packages/ui/src/locales/en-US/index.json | 96 +++++++++---------- 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/apps/frontend/src/composables/use-server-install-content.ts b/apps/frontend/src/composables/use-server-install-content.ts index f9bf933d04..4531601bb2 100644 --- a/apps/frontend/src/composables/use-server-install-content.ts +++ b/apps/frontend/src/composables/use-server-install-content.ts @@ -628,7 +628,9 @@ export function useServerInstallContent({ if (fromContext.value === 'onboarding') { await client.archon.servers_v1.endIntro(currentServerId.value) queryClient.invalidateQueries({ queryKey: ['servers', 'detail', currentServerId.value] }) - navigateTo(getServerInstanceContentPath(currentServerId.value, currentWorldId.value ?? null)) + navigateTo( + getServerInstanceContentPath(currentServerId.value, currentWorldId.value ?? null), + ) } else { navigateTo(`/hosting/manage/${currentServerId.value}?openSettings=installation`) } diff --git a/packages/ui/src/layouts/wrapped/index.ts b/packages/ui/src/layouts/wrapped/index.ts index dad1837365..b6e7993a30 100644 --- a/packages/ui/src/layouts/wrapped/index.ts +++ b/packages/ui/src/layouts/wrapped/index.ts @@ -1,9 +1,9 @@ export { default as ServersManageRootLayout } from './hosting/manage/[id]/index.vue' -export { default as ServerOnboardingPanelPage } from './hosting/manage/[id]/onboarding.vue' -export { default as ServersManageOverviewPage } from './hosting/manage/[id]/overview.vue' export { default as ServersManageBackupsPage } from './hosting/manage/[id]/instances/[instance-id]/backups.vue' export { default as ServersManageContentPage } from './hosting/manage/[id]/instances/[instance-id]/content.vue' export { default as ServersManageFilesPage } from './hosting/manage/[id]/instances/[instance-id]/files.vue' export { default as ServersManageInstanceRootLayout } from './hosting/manage/[id]/instances/[instance-id]/index.vue' export { default as ServersManageInstancesPage } from './hosting/manage/[id]/instances/index.vue' +export { default as ServerOnboardingPanelPage } from './hosting/manage/[id]/onboarding.vue' +export { default as ServersManageOverviewPage } from './hosting/manage/[id]/overview.vue' export { default as ServersManagePageIndex } from './hosting/manage/index.vue' diff --git a/packages/ui/src/locales/en-US/index.json b/packages/ui/src/locales/en-US/index.json index 98e5b30454..0a9f52c290 100644 --- a/packages/ui/src/locales/en-US/index.json +++ b/packages/ui/src/locales/en-US/index.json @@ -3650,6 +3650,51 @@ "servers.manage.install.retry-error": { "defaultMessage": "Failed to retry installation" }, + "servers.manage.instance.all-instances": { + "defaultMessage": "All instances" + }, + "servers.manage.instance.fallback-name": { + "defaultMessage": "Instance" + }, + "servers.manage.instance.last-active": { + "defaultMessage": "Last active {time}" + }, + "servers.manage.instance.settings": { + "defaultMessage": "Instance settings" + }, + "servers.manage.instances.card.active": { + "defaultMessage": "Active" + }, + "servers.manage.instances.card.create": { + "defaultMessage": "Create instance" + }, + "servers.manage.instances.card.created": { + "defaultMessage": "Created" + }, + "servers.manage.instances.card.edit": { + "defaultMessage": "Edit instance" + }, + "servers.manage.instances.card.empty-description": { + "defaultMessage": "New instance" + }, + "servers.manage.instances.card.installed-content": { + "defaultMessage": "Installed content" + }, + "servers.manage.instances.card.last-active": { + "defaultMessage": "Last active" + }, + "servers.manage.instances.card.none": { + "defaultMessage": "None" + }, + "servers.manage.instances.card.not-tracked-yet": { + "defaultMessage": "Not tracked yet" + }, + "servers.manage.instances.card.settings": { + "defaultMessage": "Instance settings" + }, + "servers.manage.instances.slot-name": { + "defaultMessage": "Instance #{index}" + }, "servers.manage.nav.backups": { "defaultMessage": "Backups" }, @@ -3659,12 +3704,12 @@ "servers.manage.nav.files": { "defaultMessage": "Files" }, - "servers.manage.nav.overview": { - "defaultMessage": "Overview" - }, "servers.manage.nav.instances": { "defaultMessage": "Instances" }, + "servers.manage.nav.overview": { + "defaultMessage": "Overview" + }, "servers.manage.new-server-button": { "defaultMessage": "New server" }, @@ -3749,51 +3794,6 @@ "servers.manage.websocket.reconnecting": { "defaultMessage": "Hang on, we're reconnecting to your server." }, - "servers.manage.instance.all-instances": { - "defaultMessage": "All instances" - }, - "servers.manage.instance.fallback-name": { - "defaultMessage": "Instance" - }, - "servers.manage.instance.last-active": { - "defaultMessage": "Last active {time}" - }, - "servers.manage.instance.settings": { - "defaultMessage": "Instance settings" - }, - "servers.manage.instances.card.active": { - "defaultMessage": "Active" - }, - "servers.manage.instances.card.create": { - "defaultMessage": "Create instance" - }, - "servers.manage.instances.card.created": { - "defaultMessage": "Created" - }, - "servers.manage.instances.card.edit": { - "defaultMessage": "Edit instance" - }, - "servers.manage.instances.card.empty-description": { - "defaultMessage": "New instance" - }, - "servers.manage.instances.card.installed-content": { - "defaultMessage": "Installed content" - }, - "servers.manage.instances.card.last-active": { - "defaultMessage": "Last active" - }, - "servers.manage.instances.card.none": { - "defaultMessage": "None" - }, - "servers.manage.instances.card.not-tracked-yet": { - "defaultMessage": "Not tracked yet" - }, - "servers.manage.instances.card.settings": { - "defaultMessage": "Instance settings" - }, - "servers.manage.instances.slot-name": { - "defaultMessage": "Instance #{index}" - }, "servers.medal-listing.countdown.remaining": { "defaultMessage": "{days} {days, plural, one {day} other {days}} {hours} {hours, plural, one {hour} other {hours}} {minutes} {minutes, plural, one {minute} other {minutes}} {seconds} {seconds, plural, one {second} other {seconds}} remaining..." }, From c5f1fa1154279166875a80ca37788d842c162cf7 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 18 May 2026 11:12:49 +0100 Subject: [PATCH 10/18] fix: header issues --- .../app-frontend/src/pages/instance/Index.vue | 49 +++++-- .../server-header/ServerManageHeader.vue | 2 +- .../server-header/WorldManageHeader.vue | 127 ++++++++++++++++++ .../components/servers/server-header/index.ts | 1 + .../[id]/instances/[instance-id]/index.vue | 99 +++++--------- .../servers/WorldManageHeader.stories.ts | 51 +++++++ 6 files changed, 256 insertions(+), 73 deletions(-) create mode 100644 packages/ui/src/components/servers/server-header/WorldManageHeader.vue create mode 100644 packages/ui/src/stories/servers/WorldManageHeader.stories.ts diff --git a/apps/app-frontend/src/pages/instance/Index.vue b/apps/app-frontend/src/pages/instance/Index.vue index 0090900296..549d706196 100644 --- a/apps/app-frontend/src/pages/instance/Index.vue +++ b/apps/app-frontend/src/pages/instance/Index.vue @@ -28,17 +28,30 @@