feat: manage page
This commit is contained in:
parent
6c452f86b6
commit
5d98b16270
11
apps/frontend/src/pages/servers_new/manage/index.vue
Normal file
11
apps/frontend/src/pages/servers_new/manage/index.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
class="experimental-styles-within relative mx-auto mb-6 flex min-h-screen w-full max-w-[1280px] flex-col px-6"
|
||||
>
|
||||
<ServersManagePage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ServersManagePage } from "@modrinth/ui";
|
||||
</script>
|
||||
@ -49,6 +49,7 @@ import _CubeIcon from './icons/cube.svg?component'
|
||||
import _CurrencyIcon from './icons/currency.svg?component'
|
||||
import _DashboardIcon from './icons/dashboard.svg?component'
|
||||
import _DatabaseIcon from './icons/database.svg?component'
|
||||
import _DotIcon from './icons/dot.svg?component'
|
||||
import _DownloadIcon from './icons/download.svg?component'
|
||||
import _DropdownIcon from './icons/dropdown.svg?component'
|
||||
import _EditIcon from './icons/edit.svg?component'
|
||||
@ -240,6 +241,7 @@ export const CubeIcon = _CubeIcon
|
||||
export const CurrencyIcon = _CurrencyIcon
|
||||
export const DashboardIcon = _DashboardIcon
|
||||
export const DatabaseIcon = _DatabaseIcon
|
||||
export const DotIcon = _DotIcon
|
||||
export const DownloadIcon = _DownloadIcon
|
||||
export const DropdownIcon = _DropdownIcon
|
||||
export const EditIcon = _EditIcon
|
||||
|
||||
1
packages/assets/icons/dot.svg
Normal file
1
packages/assets/icons/dot.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-dot-icon lucide-dot"><circle cx="12.1" cy="12.1" r="1"/></svg>
|
||||
|
After Width: | Height: | Size: 264 B |
@ -1,3 +1,4 @@
|
||||
export * from './src/components'
|
||||
export * from './src/utils'
|
||||
export * from './src/composables'
|
||||
export * from './src/servers'
|
||||
|
||||
5
packages/ui/src/components/base/RaisedBadge.vue
Normal file
5
packages/ui/src/components/base/RaisedBadge.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-2 w-fit px-3 py-1 bg-button-bg rounded-full text-sm">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@ -35,6 +35,7 @@ export { default as ProgressBar } from './base/ProgressBar.vue'
|
||||
export { default as ProjectCard } from './base/ProjectCard.vue'
|
||||
export { default as RadialHeader } from './base/RadialHeader.vue'
|
||||
export { default as RadioButtons } from './base/RadioButtons.vue'
|
||||
export { default as RaisedBadge } from './base/RaisedBadge.vue'
|
||||
export { default as ScrollablePanel } from './base/ScrollablePanel.vue'
|
||||
export { default as ServerNotice } from './base/ServerNotice.vue'
|
||||
export { default as SimpleBadge } from './base/SimpleBadge.vue'
|
||||
|
||||
0
packages/ui/src/servers/components/.gitkeep
Normal file
0
packages/ui/src/servers/components/.gitkeep
Normal file
141
packages/ui/src/servers/components/management/ServerCard.vue
Normal file
141
packages/ui/src/servers/components/management/ServerCard.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<Card>
|
||||
<div class="server-card-grid">
|
||||
<div class="header-section flex gap-4 items-center mb-4">
|
||||
<Avatar size="4rem" />
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-xl text-contrast font-bold">{{ server_name }}</span>
|
||||
<span class="text-md text-secondary" v-tooltip="server_created.toLocaleString()">
|
||||
Created {{ formatRelativeTime(server_created) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="badges-section flex gap-2 items-center mb-4">
|
||||
<RaisedBadge>{{ server_plan }}</RaisedBadge>
|
||||
<RaisedBadge class="text-lg" :color="serverStatusColor">
|
||||
• {{ formattedServerStatus }}
|
||||
</RaisedBadge>
|
||||
</div>
|
||||
|
||||
<div class="content-section flex flex-col gap-2 mb-4">
|
||||
<div class="flex flex-row gap-2">
|
||||
<UsersIcon class="size-4 my-auto" />
|
||||
<span class="text-secondary">
|
||||
{{ players_online }} / {{ max_players_online }} players
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<GlobeIcon class="size-4 my-auto" />
|
||||
<span class="text-secondary">{{ world_name }}</span>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<LinkIcon class="size-4 my-auto" />
|
||||
<CopyCode :text="ip" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-section flex gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<RouterLink :to="`/servers/manage/${id}`">
|
||||
<EditIcon class="size-4" />
|
||||
Manage
|
||||
</RouterLink>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<RouterLink :to="`/servers/manage/${id}`">
|
||||
<CurrencyIcon class="size-4" />
|
||||
Billing
|
||||
</RouterLink>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CurrencyIcon, EditIcon, GlobeIcon, LinkIcon, UsersIcon } from '@modrinth/assets'
|
||||
import { Avatar, Card, RaisedBadge, useRelativeTime, CopyCode, ButtonStyled } from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const props = defineProps<{
|
||||
server_name: string
|
||||
server_created: Date
|
||||
server_plan: string
|
||||
server_status: string
|
||||
players_online: number
|
||||
max_players_online: number
|
||||
world_name: string
|
||||
ip: string
|
||||
id: string
|
||||
}>()
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
|
||||
const serverStatusColor = computed(() => {
|
||||
switch (props.server_status) {
|
||||
case 'online':
|
||||
return 'green'
|
||||
case 'restarting':
|
||||
return 'orange'
|
||||
case 'offline':
|
||||
return undefined
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
|
||||
const formattedServerStatus = computed(() => {
|
||||
return props.server_status.slice(0, 1).toUpperCase() + props.server_status.slice(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.server-card-grid {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'header badges'
|
||||
'content content'
|
||||
'actions actions';
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.server-card-grid {
|
||||
grid-template-areas:
|
||||
'header'
|
||||
'badges'
|
||||
'content'
|
||||
'actions';
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.badges-section {
|
||||
justify-self: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.badges-section {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
.header-section {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.badges-section {
|
||||
grid-area: badges;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
grid-area: content;
|
||||
}
|
||||
|
||||
.actions-section {
|
||||
grid-area: actions;
|
||||
}
|
||||
</style>
|
||||
0
packages/ui/src/servers/composables/.gitkeep
Normal file
0
packages/ui/src/servers/composables/.gitkeep
Normal file
1
packages/ui/src/servers/index.ts
Normal file
1
packages/ui/src/servers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as ServersManagePage } from './pages/manage.vue'
|
||||
0
packages/ui/src/servers/pages/.gitkeep
Normal file
0
packages/ui/src/servers/pages/.gitkeep
Normal file
143
packages/ui/src/servers/pages/manage.vue
Normal file
143
packages/ui/src/servers/pages/manage.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="flex flex-col sm:flex-row gap-4 sm:gap-0 my-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-2xl sm:text-3xl text-contrast font-bold">Servers</span>
|
||||
<span class="text-sm sm:text-base text-secondary">View and manage all your servers</span>
|
||||
</div>
|
||||
<div class="sm:ml-auto">
|
||||
<ButtonStyled color="green" :size="isMobile ? 'standard' : 'large'" class="w-full sm:w-auto">
|
||||
<button class="flex items-center justify-center gap-2">
|
||||
<PlusIcon class="size-4" />
|
||||
<span>New server</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="text-md sm:text-lg text-contrast font-bold mb-4 flex flex-row gap-2">
|
||||
Your servers
|
||||
</span>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<ServerCard
|
||||
v-for="server in servers"
|
||||
:key="server.id"
|
||||
:server_name="server.server_name"
|
||||
:server_created="server.server_created"
|
||||
:server_plan="server.server_plan"
|
||||
:server_status="server.server_status"
|
||||
:players_online="server.players_online"
|
||||
:max_players_online="server.max_players_online"
|
||||
:world_name="server.world_name"
|
||||
:ip="server.ip"
|
||||
:id="server.id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span class="text-md sm:text-lg text-contrast font-bold mb-4 flex flex-row gap-2">
|
||||
Servers shared with you
|
||||
</span>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<ServerCard
|
||||
v-for="server in servers"
|
||||
:key="server.id"
|
||||
:server_name="server.server_name"
|
||||
:server_created="server.server_created"
|
||||
:server_plan="server.server_plan"
|
||||
:server_status="server.server_status"
|
||||
:players_online="server.players_online"
|
||||
:max_players_online="server.max_players_online"
|
||||
:world_name="server.world_name"
|
||||
:ip="server.ip"
|
||||
:id="server.id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="servers.length === 0" class="text-center py-12">
|
||||
<ServerIcon class="size-12 mx-auto text-gray-400 mb-4" />
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">No servers found</h3>
|
||||
<p class="text-gray-500 mb-6 px-4">Get started by creating your first server</p>
|
||||
<ButtonStyled color="green" class="w-full sm:w-auto">
|
||||
<button class="flex items-center justify-center gap-2">
|
||||
<PlusIcon class="size-4" /> Create your first server
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ButtonStyled, RaisedBadge } from '@modrinth/ui'
|
||||
import ServerCard from '../components/management/ServerCard.vue'
|
||||
import { PlusIcon, ServerIcon } from '@modrinth/assets'
|
||||
|
||||
const isMobile = ref(false)
|
||||
const isRefreshing = ref(false)
|
||||
|
||||
const servers = ref([
|
||||
{
|
||||
id: 'server-1',
|
||||
server_name: 'Rinth SMP',
|
||||
server_created: new Date('2023-10-01T12:00:00Z'),
|
||||
server_plan: 'Large',
|
||||
server_status: 'online',
|
||||
players_online: 5,
|
||||
max_players_online: 20,
|
||||
world_name: 'Example World',
|
||||
ip: 'valiant-apple.modrinth.gg',
|
||||
},
|
||||
{
|
||||
id: 'server-1',
|
||||
server_name: 'Rinth SMP',
|
||||
server_created: new Date('2023-10-01T12:00:00Z'),
|
||||
server_plan: 'Large',
|
||||
server_status: 'online',
|
||||
players_online: 5,
|
||||
max_players_online: 20,
|
||||
world_name: 'Example World',
|
||||
ip: 'valiant-apple.modrinth.gg',
|
||||
},
|
||||
{
|
||||
id: 'server-1',
|
||||
server_name: 'Rinth SMP',
|
||||
server_created: new Date('2023-10-01T12:00:00Z'),
|
||||
server_plan: 'Large',
|
||||
server_status: 'online',
|
||||
players_online: 5,
|
||||
max_players_online: 20,
|
||||
world_name: 'Example World',
|
||||
ip: 'valiant-apple.modrinth.gg',
|
||||
},
|
||||
{
|
||||
id: 'server-1',
|
||||
server_name: 'Rinth SMP',
|
||||
server_created: new Date('2023-10-01T12:00:00Z'),
|
||||
server_plan: 'Large',
|
||||
server_status: 'online',
|
||||
players_online: 5,
|
||||
max_players_online: 20,
|
||||
world_name: 'Example World',
|
||||
ip: 'valiant-apple.modrinth.gg',
|
||||
},
|
||||
])
|
||||
|
||||
const checkMobile = () => {
|
||||
isMobile.value = window.innerWidth < 640
|
||||
}
|
||||
|
||||
const handlePullToRefresh = () => {
|
||||
if (window.scrollY === 0) {
|
||||
isRefreshing.value = true
|
||||
setTimeout(() => {
|
||||
isRefreshing.value = false
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkMobile()
|
||||
window.addEventListener('resize', checkMobile)
|
||||
window.addEventListener('scroll', handlePullToRefresh)
|
||||
})
|
||||
</script>
|
||||
Loading…
x
Reference in New Issue
Block a user