From d1f97db412317f51e7fc4aa1c1cc4d34a1798406 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 3 Aug 2026 07:56:03 +0100 Subject: [PATCH] feat: server play frontend draft --- .../src/pages/hosting/manage/Index.vue | 1 + .../src/pages/hosting/manage/Play.vue | 18 ++ .../src/pages/hosting/manage/index.js | 3 +- apps/app-frontend/src/routes.js | 5 + apps/app-frontend/src/store/theme.ts | 1 + apps/frontend/src/composables/featureFlags.ts | 1 + .../src/pages/hosting/manage/[id].vue | 1 + .../src/pages/hosting/manage/[id]/play.vue | 19 ++ .../manage/[id]/play/ServerPlayCard.vue | 175 +++++++++++++ .../manage/[id]/play/ServerPlayersTable.vue | 200 ++++++++++++++ .../wrapped/hosting/manage/[id]/play/play.vue | 245 ++++++++++++++++++ .../wrapped/hosting/manage/[id]/play/types.ts | 11 + .../layouts/wrapped/hosting/manage/root.vue | 13 + packages/ui/src/layouts/wrapped/index.ts | 1 + 14 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 apps/app-frontend/src/pages/hosting/manage/Play.vue create mode 100644 apps/frontend/src/pages/hosting/manage/[id]/play.vue create mode 100644 packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayCard.vue create mode 100644 packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayersTable.vue create mode 100644 packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/play.vue create mode 100644 packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/types.ts diff --git a/apps/app-frontend/src/pages/hosting/manage/Index.vue b/apps/app-frontend/src/pages/hosting/manage/Index.vue index a90ee87076..c679d86211 100644 --- a/apps/app-frontend/src/pages/hosting/manage/Index.vue +++ b/apps/app-frontend/src/pages/hosting/manage/Index.vue @@ -9,6 +9,7 @@ :reload-page="() => router.go(0)" :resolve-viewer="resolveViewer" :show-copy-id-action="themeStore.devMode" + :show-play-tab="themeStore.getFeatureFlag('ServerPlayFrontend')" :auth-user="authUser" :navigate-to-billing="() => openUrl('https://modrinth.com/settings/billing')" :navigate-to-servers="() => router.push('/hosting/manage')" diff --git a/apps/app-frontend/src/pages/hosting/manage/Play.vue b/apps/app-frontend/src/pages/hosting/manage/Play.vue new file mode 100644 index 0000000000..4cb2573e3c --- /dev/null +++ b/apps/app-frontend/src/pages/hosting/manage/Play.vue @@ -0,0 +1,18 @@ + + + diff --git a/apps/app-frontend/src/pages/hosting/manage/index.js b/apps/app-frontend/src/pages/hosting/manage/index.js index 0c10b07133..073c549296 100644 --- a/apps/app-frontend/src/pages/hosting/manage/index.js +++ b/apps/app-frontend/src/pages/hosting/manage/index.js @@ -4,5 +4,6 @@ import Content from './Content.vue' import Files from './Files.vue' import Index from './Index.vue' import Overview from './Overview.vue' +import Play from './Play.vue' -export { Access, Backups, Content, Files, Index, Overview } +export { Access, Backups, Content, Files, Index, Overview, Play } diff --git a/apps/app-frontend/src/routes.js b/apps/app-frontend/src/routes.js index 70f6e5372b..f80f18f7f6 100644 --- a/apps/app-frontend/src/routes.js +++ b/apps/app-frontend/src/routes.js @@ -27,6 +27,11 @@ export default new createRouter({ name: 'ServerManage', component: Hosting.Index, children: [ + { + path: 'play', + name: 'ServerManagePlay', + component: Hosting.Play, + }, { path: '', name: 'ServerManageOverview', diff --git a/apps/app-frontend/src/store/theme.ts b/apps/app-frontend/src/store/theme.ts index 4b61ebff78..f3dde62bed 100644 --- a/apps/app-frontend/src/store/theme.ts +++ b/apps/app-frontend/src/store/theme.ts @@ -18,6 +18,7 @@ export const DEFAULT_FEATURE_FLAGS = { advanced_filters_collapsed: true, always_show_copy_details: false, hide_installed_modpacks: false, + ServerPlayFrontend: false, } export const THEME_OPTIONS = ['dark', 'light', 'oled', 'retro', 'system'] as const diff --git a/apps/frontend/src/composables/featureFlags.ts b/apps/frontend/src/composables/featureFlags.ts index 3dcc90f86e..919303afaf 100644 --- a/apps/frontend/src/composables/featureFlags.ts +++ b/apps/frontend/src/composables/featureFlags.ts @@ -58,6 +58,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({ versionDevInfoCollapsed: true, alwaysShowVersionDevInfo: false, advancedFiltersCollapsed: true, + ServerPlayFrontend: false, } as const) export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS diff --git a/apps/frontend/src/pages/hosting/manage/[id].vue b/apps/frontend/src/pages/hosting/manage/[id].vue index 5f6d5245f4..9e218386ab 100644 --- a/apps/frontend/src/pages/hosting/manage/[id].vue +++ b/apps/frontend/src/pages/hosting/manage/[id].vue @@ -5,6 +5,7 @@ :resolve-viewer="resolveViewer" :show-copy-id-action="flags.developerMode" :show-advanced-debug-info="flags.advancedDebugInfo" + :show-play-tab="flags.ServerPlayFrontend" :stripe-publishable-key="config.public.stripePublishableKey as string" :site-url="config.public.siteUrl as string" :products="products" diff --git a/apps/frontend/src/pages/hosting/manage/[id]/play.vue b/apps/frontend/src/pages/hosting/manage/[id]/play.vue new file mode 100644 index 0000000000..b4866d98f7 --- /dev/null +++ b/apps/frontend/src/pages/hosting/manage/[id]/play.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayCard.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayCard.vue new file mode 100644 index 0000000000..b515971992 --- /dev/null +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayCard.vue @@ -0,0 +1,175 @@ + + + diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayersTable.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayersTable.vue new file mode 100644 index 0000000000..f235922655 --- /dev/null +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/ServerPlayersTable.vue @@ -0,0 +1,200 @@ + + + diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/play.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/play.vue new file mode 100644 index 0000000000..0c9fd04c2d --- /dev/null +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/play.vue @@ -0,0 +1,245 @@ + + + diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/types.ts b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/types.ts new file mode 100644 index 0000000000..63f2dbb055 --- /dev/null +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/play/types.ts @@ -0,0 +1,11 @@ +export type ServerPlayerMethod = 'direct' | 'link' + +export type ServerPlayerRow = { + id: string + username: string + avatarUrl?: string + lastPlayedAt: Date | null + joinedAt: Date | null + method: ServerPlayerMethod + pending?: boolean +} diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/root.vue b/packages/ui/src/layouts/wrapped/hosting/manage/root.vue index c781570242..417539fec4 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/root.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/root.vue @@ -412,6 +412,7 @@ import { LinkIcon, LoaderCircleIcon, LockIcon, + PlayIcon, MoreVerticalIcon, RightArrowIcon, ServerIcon as ServerAssetIcon, @@ -492,6 +493,7 @@ const props = withDefaults( showCopyIdAction?: boolean showAdvancedDebugInfo?: boolean showUptime?: boolean + showPlayTab?: boolean additionalTabs?: Tab[] stripePublishableKey?: string siteUrl?: string @@ -516,6 +518,7 @@ const props = withDefaults( showCopyIdAction: false, showAdvancedDebugInfo: false, showUptime: true, + showPlayTab: false, additionalTabs: () => [], stripePublishableKey: undefined, siteUrl: undefined, @@ -948,6 +951,16 @@ watch(serverData, (data) => { }) const navLinks = computed(() => [ + ...(props.showPlayTab + ? [ + { + label: 'Play', + href: `/hosting/manage/${props.serverId}/play`, + icon: PlayIcon, + subpages: [], + }, + ] + : []), { label: 'Overview', href: `/hosting/manage/${props.serverId}`, diff --git a/packages/ui/src/layouts/wrapped/index.ts b/packages/ui/src/layouts/wrapped/index.ts index 79bec2d024..1d5b6f52c0 100644 --- a/packages/ui/src/layouts/wrapped/index.ts +++ b/packages/ui/src/layouts/wrapped/index.ts @@ -7,4 +7,5 @@ export { default as ServersManageContentPage } from './hosting/manage/content.vu export { default as ServersManageFilesPage } from './hosting/manage/files.vue' export { default as ServersManagePageIndex } from './hosting/manage/index.vue' export { default as ServersManageOverviewPage } from './hosting/manage/overview.vue' +export { default as ServersManagePlayPage } from './hosting/manage/[id]/play/play.vue' export { default as ServersManageRootLayout } from './hosting/manage/root.vue'