You've already forked AstralRinth
* fix Signed-off-by: Evan Song <theevansong@gmail.com> * fix Signed-off-by: Evan Song <theevansong@gmail.com> * refactor(fileitem): optimize Signed-off-by: Evan Song <theevansong@gmail.com> * chore(fileitem): fixed width timestamp Signed-off-by: Evan Song <theevansong@gmail.com> * fix(fileitem): allow editing json5/jsonc Signed-off-by: Evan Song <theevansong@gmail.com> * feat: motd pt 1, auto backups scaffolding, editing navbar changes * feat: fancy sidebar animations * fix: files * fix: files pt2 * fix: faulty name validation disallowing spaces in file names Signed-off-by: Evan Song <theevansong@gmail.com> * refactor: fileitem props Signed-off-by: Evan Song <theevansong@gmail.com> * fix: upload files not refreshing files list Signed-off-by: Evan Song <theevansong@gmail.com> * fix(imgviewer): handle invalid/empty images Signed-off-by: Evan Song <theevansong@gmail.com> * fix: return of the sticky files header Signed-off-by: Evan Song <theevansong@gmail.com> * chore: prevent servericon from shrinking Signed-off-by: Evan Song <theevansong@gmail.com> * fix: wtf were we thinking with this anyway Signed-off-by: Evan Song <theevansong@gmail.com> * fix: further mobile optimization Signed-off-by: Evan Song <theevansong@gmail.com> * chore: propagate margin Signed-off-by: Evan Song <theevansong@gmail.com> * chore: truncation fixes Signed-off-by: Evan Song <theevansong@gmail.com> * fix: track navbar with sentinel Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * fix(files): a11y Signed-off-by: Evan Song <theevansong@gmail.com> * chore: improve inspector styles Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * feat: console preformance improvements, decrease blur * feat(mobile): new server header * fix: linting * fix: useless z indeces Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust file filter names Signed-off-by: Evan Song <theevansong@gmail.com> * feat(files): true breadcrumbs Signed-off-by: Evan Song <theevansong@gmail.com> * fix(marketing): make custom responsive * fix(marketing): mobile file manager card * feat: trackable navtabs Signed-off-by: Evan Song <theevansong@gmail.com> * fix: oh no Signed-off-by: Evan Song <theevansong@gmail.com> * fix: smartly truncate Signed-off-by: Evan Song <theevansong@gmail.com> * fix(terminal): z-indexes * fix: autofocus more inputs Signed-off-by: Evan Song <theevansong@gmail.com> * fix: color Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust copy Signed-off-by: Evan Song <theevansong@gmail.com> * chore: backup modal usability improvements Signed-off-by: Evan Song <theevansong@gmail.com> * fix: padding Signed-off-by: Evan Song <theevansong@gmail.com> * chore: title Signed-off-by: Evan Song <theevansong@gmail.com> * fix(content): update banner mobile support * fix: server listing icons Signed-off-by: Evan Song <theevansong@gmail.com> * fix: ignore clicks in server listing for labels Signed-off-by: Evan Song <theevansong@gmail.com> * feat(mobile): backup card * fix(backups): make plural conditional * fix: debounce file item selectitem Signed-off-by: Evan Song <theevansong@gmail.com> * fix: lint Signed-off-by: Evan Song <theevansong@gmail.com> * stuff Signed-off-by: Evan Song <theevansong@gmail.com> * fix: temp sidebar fix until i can be smart * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * chore: explictly set button type in file modals Signed-off-by: Evan Song <theevansong@gmail.com> * fix: properly sort backups Signed-off-by: Evan Song <theevansong@gmail.com> * feat: add getautobackup method to pyroservers Signed-off-by: Evan Song <theevansong@gmail.com> * choer: update autobackup params Signed-off-by: Evan Song <theevansong@gmail.com> * chore: update autobackup methods (REALLY GUYS) Signed-off-by: Evan Song <theevansong@gmail.com> * feat: implement autobackups Signed-off-by: Evan Song <theevansong@gmail.com> * feat: implement backup-while-running preference Signed-off-by: Evan Song <theevansong@gmail.com> * feat: make server labels a component * feat: implement 'All details' modal * fix(mobile): server manage page * feat(files): mobile compatible * fix(info labels): wrap * chore(inspector): clean Signed-off-by: Evan Song <theevansong@gmail.com> * fix(backup settings): swap + and - * fix(manage): new -> plans instead of modal * feat: more small mobile fixes * fix(auto backup modal): manual input validation * fix(file browse navbar): home margin * feat(purchase modal): mobile support * fix(marketing): faded line alignments * feat: add servers to mobile nav * feat(network): dns record fixes * feat: make all settings work on mobile * fix(loader settings): modpack mobile * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * feat(marketing): add 'Manage your servers' button * fix(marketing): only check servers if logged in * fix(network): allocation edit & delete button * fix(backups): use UiServersTeleportOverflowMenu * chore: linting * chore: but here comes the sentence case Signed-off-by: Evan Song <theevansong@gmail.com> * feat(marketing): make buttons consistent * lint Signed-off-by: Evan Song <theevansong@gmail.com> * fix(loader): prevent multiline version names in dropdown Signed-off-by: Evan Song <theevansong@gmail.com> * lint Signed-off-by: Evan Song <theevansong@gmail.com> * fix: copy Signed-off-by: Evan Song <theevansong@gmail.com> * fix: sentence case Signed-off-by: Evan Song <theevansong@gmail.com> * fix: linting * chore: rename dumbass preference key Signed-off-by: Evan Song <theevansong@gmail.com> * refactor: rewrite power action buttons Signed-off-by: Evan Song <theevansong@gmail.com> * fix: robust download logic Signed-off-by: Evan Song <theevansong@gmail.com> * fix(loader mobile): modpack dropdown width * fix: sentence case * fix(save & 'working on it'): look good on mobile * fix(TeleportDropdown): width * fix(inspecting error): mobile * fix: show action button dropdown when installing * fix(navtabs): temp fix for mobile scrolling issue * fix(install error): mobile compatible * chore: just remove tracking Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * fix: cleanup * fix: broken svg clr in checkbox when using experimental styles Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust vanilla icon Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust loader props Signed-off-by: Evan Song <theevansong@gmail.com> * revert changes to serversidebar Signed-off-by: Evan Song <theevansong@gmail.com> * fix: server properties flicker Signed-off-by: Evan Song <theevansong@gmail.com> * fix(backups): plural * fix: cases where the telepoverflow would clash with viewport edge Signed-off-by: Evan Song <theevansong@gmail.com> * feat(backups): auto-backups label * fix(network): titlecase * feat(fileitem): new rename icon * fix(properties): wiki proper noun * fix: disable motd for the time being * chore: adjust wording for power conifmration Signed-off-by: Evan Song <theevansong@gmail.com> * feat: "external" to billing Signed-off-by: Evan Song <theevansong@gmail.com> * fix: icon Signed-off-by: Evan Song <theevansong@gmail.com> * fix: add EULA checkbox * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * me and bro deciding which case rules to enforce Signed-off-by: Evan Song <theevansong@gmail.com> * feat(sftp): copy address & username, launch tooltip * feat(files): better move * chore: attempt to mitigate excessive stack depth type Signed-off-by: Evan Song <theevansong@gmail.com> * fix(loader): prevent versions 1.2.4 and below * feat(dns table): placeholder improvements * feat(pyroServer): error handling * fix: intrinsic size on loader icon Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust wording Signed-off-by: Evan Song <theevansong@gmail.com> * fix: sentence case Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust wording Signed-off-by: Evan Song <theevansong@gmail.com> * fix: types Signed-off-by: Evan Song <theevansong@gmail.com> * fix: "implemented" key in preference Signed-off-by: Evan Song <theevansong@gmail.com> * feat(connection lost): redesign * feat(connection error): make icon orange * fix: cleanup * chore(connection lost): redesign pt 2 Signed-off-by: Evan Song <theevansong@gmail.com> * fix: OOOOHHH MY GOD Signed-off-by: Evan Song <theevansong@gmail.com> * feat: implement capacity api on marketing Signed-off-by: Evan Song <theevansong@gmail.com> * chore: update createdat backup type Signed-off-by: Evan Song <theevansong@gmail.com> * refactor: all of backups Signed-off-by: Evan Song <theevansong@gmail.com> * chore: update backup types Signed-off-by: Evan Song <theevansong@gmail.com> * refactor: backups pt 2 Signed-off-by: Evan Song <theevansong@gmail.com> * fix: comically small icons Signed-off-by: Evan Song <theevansong@gmail.com> * chore: align designs Signed-off-by: Evan Song <theevansong@gmail.com> * chore: hide ram graph if ram as bytes enabled Signed-off-by: Evan Song <theevansong@gmail.com> * base add content page * Fix conflict * feat(content): mobile-compatible header, sticky * fix(marketing): md instead of sm for custom * fix: compiler macro warning Signed-off-by: Evan Song <theevansong@gmail.com> * again Signed-off-by: Evan Song <theevansong@gmail.com> * fix: loader type error Signed-off-by: Evan Song <theevansong@gmail.com> * fix: default uptime seconds prop Signed-off-by: Evan Song <theevansong@gmail.com> * fix: hydration errors on server listing Signed-off-by: Evan Song <theevansong@gmail.com> * feat: move custom URL to general Signed-off-by: Evan Song <theevansong@gmail.com> * feat: indiviudally checkj capacities Signed-off-by: Evan Song <theevansong@gmail.com> * fix: falsey Signed-off-by: Evan Song <theevansong@gmail.com> * fix: missing prop Signed-off-by: Evan Song <theevansong@gmail.com> * chore: Derive On That Thang Signed-off-by: Evan Song <theevansong@gmail.com> * chore: adjust gap Signed-off-by: Evan Song <theevansong@gmail.com> * fix: add default name for backups * fix: the backup number should PROBABLY be computed lol * fix(backups): truncate text, mobile fixes * fix(loader): modpack mobile fix * feat(plans): add vcpus * fix(backup modal): blank by default, maxlength * fix(subdomain): separate length & valid chars * feat: mrpack installs functionality (untested), forbidden handling, backups grammar * feat(content): make responsive on mobile * fix: disable plan buttons separately * fix(backup modal): update name max length * fix(purchase): wrapping on eula, eula link * fix: move skeleton * fix(server mobile header): truncation * fix(server header): proper alignment * Finish content page fixes * fix: who up rinthing Signed-off-by: Evan Song <theevansong@gmail.com> * wip Signed-off-by: Evan Song <theevansong@gmail.com> * fix(staging & email banner): z-index * feat: make eula tickbox more visible * fix: move "powered by pyro" below buttons on hero * fix: oops sorry ellie, also updated the main screenshot * feat: update content screenshot * fix: content page card should hide image on lg * feat: hide total storage for now * fix: terminal card now uses terminal icon * fix(marketing): make medium plan card border solid * feat: modloader card, move pyro BACK below buttons, beta release pill * fix: spinning logo should be behind hero * feat: surgically remove the hero's massive forehead * feat(marketing): mobile UI screenshot * fix(hero): z-index goes over mobile nav * fix: consistent borders, files breakpoints * chore: update turbo * chore: adjust hero sizing Signed-off-by: Evan Song <theevansong@gmail.com> * chore: mention region restrictions * chore: double check if we are at capcity Signed-off-by: Evan Song <theevansong@gmail.com> * fix: measure twice cut once Signed-off-by: Evan Song <theevansong@gmail.com> * chore: bro cut twice and measured once 💀 Signed-off-by: Evan Song <theevansong@gmail.com> * fix(marketing): login first * fix: out of capacity text when logged out * fix(slider): reset some values for frontend * feat: wip hero section Signed-off-by: Evan Song <theevansong@gmail.com> * New navigation to support the new products (#2879) * Nav * oops extra file * feat: mrpack uploading with existing modpack, fix: choose modpack duplicate * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * feat: update features section Signed-off-by: Evan Song <theevansong@gmail.com> * Nav adjustments * fix: server manager empty state clashing with loading state Signed-off-by: Evan Song <theevansong@gmail.com> * chore: query param hard Signed-off-by: Evan Song <theevansong@gmail.com> * fix: do not count uptime if crashed Signed-off-by: Evan Song <theevansong@gmail.com> * fix: grammar Signed-off-by: Evan Song <theevansong@gmail.com> * hide hero img on lg breakpoints * Make plugins a plug * chore: prep for buffered text selection terminal Signed-off-by: Evan Song <theevansong@gmail.com> * fix: marketing responsive stuff, n fixes * fix hoverable prop * fix: edit mod spacing * fix: type error for display name in dropdown Signed-off-by: Evan Song <theevansong@gmail.com> * feat: custom plans * fix: no more console.log * fix: properly linked prop label Signed-off-by: Evan Song <theevansong@gmail.com> * fix(install hero mobile): padding * fix: prevent x overflow on servers page Signed-off-by: Evan Song <theevansong@gmail.com> * fix lint oh ym fucking god yal Signed-off-by: Evan Song <theevansong@gmail.com> * Migrate modpack install to search * fix(custom plan): warning icon variable * fix: loading probally and modal loader things * fix(marketing): login icon colours * fix(marketing): responsiveness * fix(marketing): responsiveness v2 * fix: sync button for icon tm * fix(marketing): responsiveness v3 * fix: hero image Signed-off-by: Evan Song <theevansong@gmail.com> * chore: clean Signed-off-by: Evan Song <theevansong@gmail.com> * chore: switch to cdn links Signed-off-by: Evan Song <theevansong@gmail.com> * chore: switch to cdn links Signed-off-by: Evan Song <theevansong@gmail.com> * chore: switch to cdn links Signed-off-by: Evan Song <theevansong@gmail.com> * chore: switch to cdn links Signed-off-by: Evan Song <theevansong@gmail.com> * Remove prod override --------- Signed-off-by: Evan Song <theevansong@gmail.com> Co-authored-by: Evan Song <theevansong@gmail.com> Co-authored-by: TheWander02 <48934424+thewander02@users.noreply.github.com> Co-authored-by: he3als <65787561+he3als@users.noreply.github.com> Co-authored-by: Evan Song <52982404+ferothefox@users.noreply.github.com> Co-authored-by: Lio <git@lio.cat> Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: not-nullptr <needhelpwithrift@gmail.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Prospector <prospectordev@gmail.com> Co-authored-by: sticks <tanner@teamhydra.dev>
940 lines
33 KiB
Vue
940 lines
33 KiB
Vue
<template>
|
|
<div
|
|
ref="scrollListener"
|
|
data-pyro
|
|
class="servers-hero relative isolate -mt-44 h-full min-h-screen pt-8"
|
|
>
|
|
<PurchaseModal
|
|
v-if="showModal && selectedProduct && customer"
|
|
:key="selectedProduct.id"
|
|
ref="purchaseModal"
|
|
:product="selectedProduct"
|
|
:country="country"
|
|
:custom-server="customServer"
|
|
:publishable-key="config.public.stripePublishableKey"
|
|
:send-billing-request="
|
|
async (body) =>
|
|
await useBaseFetch('billing/payment', { internal: true, method: 'POST', body })
|
|
"
|
|
:fetch-payment-data="fetchPaymentData"
|
|
:on-error="handleError"
|
|
:customer="customer"
|
|
:payment-methods="paymentMethods"
|
|
:return-url="`${config.public.siteUrl}/servers/manage`"
|
|
:server-name="`${auth?.user?.username}'s server`"
|
|
@hidden="handleModalHidden"
|
|
/>
|
|
|
|
<section
|
|
class="mx-auto mt-32 flex min-h-[calc(80vh-0px)] max-w-7xl flex-col justify-center px-5 sm:mt-20 sm:min-h-[calc(100vh-0px)] sm:pl-10 lg:pl-3"
|
|
>
|
|
<div class="z-[5] flex w-full flex-col gap-8">
|
|
<div class="flex flex-col gap-4">
|
|
<div
|
|
class="relative h-fit w-fit rounded-full bg-highlight-green px-3 py-1 text-sm font-bold text-brand backdrop-blur-lg"
|
|
>
|
|
Beta Release
|
|
</div>
|
|
<h1 class="relative m-0 max-w-3xl text-3xl font-bold !leading-[110%] md:text-6xl">
|
|
Host your next server with Modrinth Servers
|
|
</h1>
|
|
</div>
|
|
<h2
|
|
class="relative m-0 max-w-2xl text-base font-normal leading-[155%] text-secondary md:text-[1.2rem]"
|
|
>
|
|
Modrinth Servers is the easiest way to host your own Minecraft server. Seamlessly install
|
|
and play your favorite mods and modpacks, all within the Modrinth platform.
|
|
</h2>
|
|
<div class="relative flex w-full flex-wrap items-center gap-8 align-middle sm:w-fit">
|
|
<div
|
|
class="flex w-full flex-col items-center gap-5 text-center align-middle sm:w-fit sm:flex-row"
|
|
>
|
|
<ButtonStyled color="brand" size="large">
|
|
<nuxt-link class="w-fit" to="#plan">
|
|
<GameIcon aria-hidden="true" />
|
|
{{ hasServers ? "Start a new server" : "Start your server" }}
|
|
</nuxt-link>
|
|
</ButtonStyled>
|
|
<ButtonStyled v-if="hasServers" type="outlined" size="large">
|
|
<nuxt-link class="w-fit" to="/servers/manage">
|
|
<BoxIcon aria-hidden="true" /> Manage your servers
|
|
</nuxt-link>
|
|
</ButtonStyled>
|
|
<UiServersPoweredByPyro class="mx-0 !mt-0" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="absolute left-[55%] top-56 z-[5] hidden h-full max-h-[calc(100vh-10rem)] w-full rotate-1 xl:block"
|
|
>
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/panel-right-dark.webp"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="pointer-events-none h-full w-fit select-none"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
class="top-26 pointer-events-none absolute left-0 z-[4] flex h-screen w-full flex-row items-end gap-24 sm:-right-1/4 sm:top-14"
|
|
>
|
|
<div
|
|
class="pointer-events-none absolute left-0 right-0 top-8 max-h-[90%] overflow-hidden sm:top-28 sm:mt-0"
|
|
style="mask-image: linear-gradient(black, transparent 80%)"
|
|
>
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/bigrinth.webp"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="pointer-events-none w-full animate-spin select-none p-4 opacity-50"
|
|
style="
|
|
animation-duration: 172s !important;
|
|
animation-timing-function: linear;
|
|
animation-iteration-count: infinite;
|
|
"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
class="relative flex flex-col bg-[radial-gradient(65%_50%_at_50%_-10%,var(--color-brand-highlight)_0%,var(--color-accent-contrast)_100%)] px-3 pt-24 md:pt-48"
|
|
>
|
|
<div class="faded-brand-line absolute left-0 top-0 h-[1px] w-full"></div>
|
|
<div class="relative mx-auto flex w-full max-w-7xl flex-col gap-8">
|
|
<div
|
|
class="relative w-fit rounded-full bg-highlight-green px-3 py-1 text-sm font-bold text-brand backdrop-blur-lg"
|
|
>
|
|
Why Modrinth Servers?
|
|
</div>
|
|
<h1 class="relative m-0 max-w-2xl text-4xl leading-[120%] md:text-7xl">
|
|
Find a modpack. Now it's a server.
|
|
</h1>
|
|
<h2
|
|
class="relative m-0 max-w-2xl text-base font-normal leading-[155%] text-secondary md:text-[18px]"
|
|
>
|
|
Choose from the thousands of modpacks on Modrinth or create your own. Invite your friends
|
|
when you're ready to play.
|
|
</h2>
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/excitement.webp"
|
|
alt=""
|
|
class="absolute right-14 top-0 hidden max-w-[360px] lg:block"
|
|
/>
|
|
<div class="relative grid w-full grid-cols-1 gap-8 lg:grid-cols-2">
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<path
|
|
d="M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z"
|
|
/>
|
|
<rect x="3" y="14" width="7" height="7" rx="1" />
|
|
<circle cx="17.5" cy="17.5" r="3.5" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">Play where your mods are</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Modrinth Servers seamlessly integrates the mod and modpack installation process into
|
|
your server.
|
|
</h3>
|
|
</div>
|
|
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<LoaderIcon loader="fabric" class="size-8 text-brand" />
|
|
<h2 class="m-0 text-lg font-bold">All your favorite mods</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Choose between Vanilla, Fabric, Forge, Quilt and NeoForge. If it's on Modrinth, it can
|
|
run on your server.
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div class="relative">
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/installation-dark.webp"
|
|
alt=""
|
|
class="hidden w-full rounded-2xl sm:block"
|
|
/>
|
|
</div>
|
|
<div class="grid w-full grid-cols-1 gap-8 lg:grid-cols-2">
|
|
<div class="flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<rect width="20" height="16" x="2" y="4" rx="2" />
|
|
<path d="M6 8h.01" />
|
|
<path d="M10 8h.01" />
|
|
<path d="M14 8h.01" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">Manage it all on Modrinth</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Your server, mods, players, and more are all on Modrinth. No need to switch between
|
|
platforms.
|
|
</h3>
|
|
</div>
|
|
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<polygon points="13 19 22 12 13 5 13 19" />
|
|
<polygon points="2 19 11 12 2 5 2 19" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">
|
|
Experience modern, reliable hosting powered by Pyro
|
|
</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Modrinth Servers are hosted on super-fast servers, with custom-built sofware to ensure
|
|
your server runs smoothly.
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
class="relative mt-24 flex flex-col bg-[radial-gradient(65%_50%_at_50%_-10%,var(--color-brand-highlight)_0%,var(--color-accent-contrast)_100%)] px-3 pt-24 md:mt-48 md:pt-48"
|
|
>
|
|
<div class="faded-brand-line absolute left-0 top-0 h-[1px] w-full"></div>
|
|
<div class="relative mx-auto flex w-full max-w-7xl flex-col gap-8">
|
|
<div
|
|
class="relative w-fit rounded-full bg-highlight-green px-3 py-1 text-sm font-bold text-brand backdrop-blur-lg"
|
|
>
|
|
Included with your server
|
|
</div>
|
|
<h1 class="relative m-0 max-w-2xl text-4xl leading-[120%] md:text-7xl">
|
|
Comes with all the features you need.
|
|
</h1>
|
|
<h2
|
|
class="relative m-0 max-w-xl text-base font-normal leading-[155%] text-secondary md:text-[18px]"
|
|
>
|
|
Included with every server is a suite of features designed to provide a hosting experience
|
|
that only Modrinth can offer.
|
|
</h2>
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/waving.webp"
|
|
alt=""
|
|
class="absolute right-8 top-40 hidden max-w-[480px] lg:block"
|
|
/>
|
|
<div class="grid grid-cols-1 gap-9 lg:grid-cols-2">
|
|
<div class="grid w-full grid-cols-1 gap-8">
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<circle cx="12" cy="12" r="10" />
|
|
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
|
|
<path d="M2 12h20" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">Custom URL</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Share your server with a custom
|
|
<span class="text-contrast">modrinth.gg</span> URL.
|
|
</h3>
|
|
<div
|
|
aria-hidden="true"
|
|
class="ooh-shiny absolute right-4 top-4 flex items-center justify-center rounded-full bg-bg-raised p-4"
|
|
>
|
|
<span class="font-bold text-contrast">{{ currentText }}</span
|
|
>.modrinth.gg
|
|
</div>
|
|
</div>
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<path d="M12 13v8" />
|
|
<path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" />
|
|
<path d="m8 17 4-4 4 4" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">Backups included</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Every server comes with 15 backups stored off-site with Backblaze.
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div
|
|
style="
|
|
background: radial-gradient(
|
|
86.12% 101.64% at 95.97% 94.07%,
|
|
rgba(27, 217, 106, 0.23) 0%,
|
|
rgba(14, 115, 56, 0.2) 100%
|
|
);
|
|
border: 1px solid rgba(12, 107, 52, 0.55);
|
|
box-shadow: 0px 12px 38.1px rgba(27, 217, 106, 0.13);
|
|
"
|
|
class="relative flex flex-col gap-4 overflow-hidden rounded-2xl p-6 text-left sm:backdrop-blur-xl md:p-12"
|
|
>
|
|
<h2 class="m-0 text-lg font-bold">Easy to use file manager</h2>
|
|
<h3 class="m-0 text-base font-normal">
|
|
Search, manage, and upload files directly to your server with ease.
|
|
</h3>
|
|
|
|
<img
|
|
src="https://cdn.modrinth.com/servers/content-dark.webp"
|
|
alt=""
|
|
class="absolute -bottom-12 -right-[15%] hidden max-w-2xl rounded-2xl bg-brand p-4 lg:block"
|
|
/>
|
|
<div class="flex flex-row items-center gap-3">
|
|
<div
|
|
aria-hidden="true"
|
|
class="max-w-fit rounded-full bg-brand p-4 text-sm font-bold text-[var(--color-accent-contrast)] lg:absolute lg:bottom-8 lg:right-8 lg:block"
|
|
>
|
|
8.49 GB used
|
|
</div>
|
|
<div
|
|
aria-hidden="true"
|
|
class="flex w-fit items-center gap-2 rounded-full bg-button-bg p-3 lg:hidden"
|
|
>
|
|
<SortAscendingIcon class="h-6 w-6" />
|
|
Sort
|
|
</div>
|
|
<div
|
|
aria-hidden="true"
|
|
class="flex w-fit items-center rounded-full bg-button-bg p-3 lg:hidden"
|
|
>
|
|
<SearchIcon class="h-6 w-6" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid w-full grid-cols-1 gap-8 lg:grid-cols-2">
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<!-- <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="size-8 text-brand"
|
|
>
|
|
<path
|
|
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
|
|
/>
|
|
<path
|
|
d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"
|
|
/>
|
|
<path d="m18 15-2-2" />
|
|
<path d="m15 18-2-2" />
|
|
</svg> -->
|
|
<TerminalSquareIcon class="size-8 text-brand" />
|
|
<h2 class="m-0 text-lg font-bold">
|
|
An easy console, server properties manager, and more
|
|
</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Super powerful features with super simple access.
|
|
</h3>
|
|
</div>
|
|
<div class="relative flex flex-col gap-4 rounded-2xl bg-bg p-6 text-left md:p-12">
|
|
<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="size-8 text-brand"
|
|
>
|
|
<path
|
|
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
|
|
/>
|
|
<path
|
|
d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"
|
|
/>
|
|
<path d="m18 15-2-2" />
|
|
<path d="m15 18-2-2" />
|
|
</svg>
|
|
<h2 class="m-0 text-lg font-bold">Help when you need it</h2>
|
|
<h3 class="m-0 text-base font-normal text-secondary">
|
|
Reach out to the Modrinth team for help with your server at any time.
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
id="plan"
|
|
class="relative mt-24 flex flex-col bg-[radial-gradient(65%_50%_at_50%_-10%,var(--color-brand-highlight)_0%,var(--color-accent-contrast)_100%)] px-3 pt-24 md:mt-48 md:pt-48"
|
|
>
|
|
<div class="faded-brand-line absolute left-0 top-0 h-[1px] w-full"></div>
|
|
<div class="mx-auto flex w-full max-w-7xl flex-col items-center gap-8 text-center">
|
|
<h1 class="relative m-0 text-4xl leading-[120%] md:text-7xl">
|
|
Start your server on Modrinth
|
|
</h1>
|
|
<h2
|
|
class="relative m-0 max-w-xl text-base font-normal leading-[155%] text-secondary md:text-[18px]"
|
|
>
|
|
{{
|
|
isAtCapacity && !loggedOut
|
|
? "We are currently at capacity. Please try again later."
|
|
: "There's a plan for everyone! Choose the one that fits your needs."
|
|
}}
|
|
<span class="font-bold"> Servers are currently US only. More regions coming soon!</span>
|
|
</h2>
|
|
|
|
<ul class="m-0 flex w-full flex-col gap-8 p-0 lg:flex-row">
|
|
<li class="flex w-full flex-col gap-4 rounded-2xl bg-bg p-8 text-left lg:w-1/3">
|
|
<div class="flex flex-row items-center justify-between">
|
|
<h1 class="m-0">Small</h1>
|
|
<div
|
|
class="grid size-8 place-content-center rounded-full bg-highlight-blue text-xs font-bold text-blue"
|
|
>
|
|
S
|
|
</div>
|
|
</div>
|
|
<p class="m-0">
|
|
Perfect for vanilla multiplayer, small friend groups, SMPs, and light modding.
|
|
</p>
|
|
<div class="flex flex-row flex-wrap items-center gap-3 text-nowrap">
|
|
<p class="m-0">4 GB RAM</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">32 GB Storage</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">2 vCPUs</p>
|
|
</div>
|
|
<h2 class="m-0 text-3xl text-contrast">
|
|
$12<span class="text-sm font-normal text-secondary">/month</span>
|
|
</h2>
|
|
<ButtonStyled color="blue" size="large">
|
|
<button
|
|
v-if="!isSmallAtCapacity"
|
|
class="!bg-highlight-blue !font-medium !text-blue"
|
|
@click="selectProduct(pyroPlanProducts[0])"
|
|
>
|
|
Get Started
|
|
<RightArrowIcon class="!min-h-4 !min-w-4 !text-blue" />
|
|
</button>
|
|
<NuxtLink
|
|
v-else
|
|
:to="loggedOut ? redirectUrl : 'https://support.modrinth.com'"
|
|
:target="loggedOut ? '_self' : '_blank'"
|
|
class="!bg-highlight-blue !font-medium !text-blue"
|
|
>
|
|
<template v-if="loggedOut">
|
|
Login
|
|
<UserIcon class="!min-h-4 !min-w-4 !text-blue" />
|
|
</template>
|
|
<template v-else>
|
|
Out of Stock
|
|
<ExternalIcon class="!min-h-4 !min-w-4 !text-blue" />
|
|
</template>
|
|
</NuxtLink>
|
|
</ButtonStyled>
|
|
</li>
|
|
|
|
<li
|
|
style="
|
|
background: radial-gradient(
|
|
86.12% 101.64% at 95.97% 94.07%,
|
|
rgba(27, 217, 106, 0.23) 0%,
|
|
rgba(14, 115, 56, 0.2) 100%
|
|
);
|
|
border: 1px solid rgba(12, 107, 52, 0.55);
|
|
box-shadow: 0px 12px 38.1px rgba(27, 217, 106, 0.13);
|
|
"
|
|
class="flex w-full flex-col gap-4 rounded-2xl bg-bg p-8 text-left lg:w-1/3"
|
|
>
|
|
<div class="flex flex-row items-center justify-between">
|
|
<h1 class="m-0">Medium</h1>
|
|
<div
|
|
class="grid size-8 place-content-center rounded-full bg-highlight-green text-xs font-bold text-brand"
|
|
>
|
|
M
|
|
</div>
|
|
</div>
|
|
<p class="m-0">Great for modded multiplayer and small communities.</p>
|
|
<div class="flex flex-row flex-wrap items-center gap-3 text-nowrap">
|
|
<p class="m-0">6 GB RAM</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">48 GB Storage</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">3 vCPUs</p>
|
|
</div>
|
|
<h2 class="m-0 text-3xl text-contrast">
|
|
$18<span class="text-sm font-normal text-secondary">/month</span>
|
|
</h2>
|
|
<ButtonStyled color="brand" size="large">
|
|
<button
|
|
v-if="!isMediumAtCapacity"
|
|
class="shadow-xl"
|
|
@click="selectProduct(pyroPlanProducts[1])"
|
|
>
|
|
Get Started
|
|
<RightArrowIcon class="!min-h-4 !min-w-4" />
|
|
</button>
|
|
<NuxtLink
|
|
v-else
|
|
:to="loggedOut ? redirectUrl : 'https://support.modrinth.com'"
|
|
:target="loggedOut ? '_self' : '_blank'"
|
|
class="!bg-highlight-green !font-medium !text-green"
|
|
>
|
|
<template v-if="loggedOut">
|
|
Login
|
|
<UserIcon class="!min-h-4 !min-w-4 !text-green" />
|
|
</template>
|
|
<template v-else>
|
|
Out of Stock
|
|
<ExternalIcon class="!min-h-4 !min-w-4 !text-green" />
|
|
</template>
|
|
</NuxtLink>
|
|
</ButtonStyled>
|
|
</li>
|
|
|
|
<li class="flex w-full flex-col gap-4 rounded-2xl bg-bg p-8 text-left lg:w-1/3">
|
|
<div class="flex flex-row items-center justify-between">
|
|
<h1 class="m-0">Large</h1>
|
|
<div
|
|
class="grid size-8 place-content-center rounded-full bg-highlight-purple text-xs font-bold text-purple"
|
|
>
|
|
L
|
|
</div>
|
|
</div>
|
|
<p class="m-0">Ideal for larger communities, modpacks, and heavy modding.</p>
|
|
<div class="flex flex-row flex-wrap items-center gap-3 text-nowrap">
|
|
<p class="m-0">8 GB RAM</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">64 GB Storage</p>
|
|
<div class="size-1.5 rounded-full bg-secondary opacity-25"></div>
|
|
<p class="m-0">4 vCPUs</p>
|
|
</div>
|
|
<h2 class="m-0 text-3xl text-contrast">
|
|
$24<span class="text-sm font-normal text-secondary">/month</span>
|
|
</h2>
|
|
<ButtonStyled color="purple" size="large">
|
|
<button
|
|
v-if="!isLargeAtCapacity"
|
|
class="!bg-highlight-purple !font-medium !text-purple"
|
|
@click="selectProduct(pyroPlanProducts[2])"
|
|
>
|
|
Get Started
|
|
<RightArrowIcon class="!min-h-4 !min-w-4 !text-purple" />
|
|
</button>
|
|
<NuxtLink
|
|
v-else
|
|
:to="loggedOut ? redirectUrl : 'https://support.modrinth.com'"
|
|
:target="loggedOut ? '_self' : '_blank'"
|
|
class="!bg-highlight-purple !font-medium !text-purple"
|
|
>
|
|
<template v-if="loggedOut">
|
|
Login
|
|
<UserIcon class="!min-h-4 !min-w-4 !text-purple" />
|
|
</template>
|
|
<template v-else>
|
|
Out of Stock
|
|
<ExternalIcon class="!min-h-4 !min-w-4 !text-purple" />
|
|
</template>
|
|
</NuxtLink>
|
|
</ButtonStyled>
|
|
</li>
|
|
</ul>
|
|
|
|
<div
|
|
class="flex w-full flex-col items-start justify-between gap-4 rounded-2xl bg-bg p-8 text-left md:flex-row md:gap-0"
|
|
>
|
|
<div class="flex flex-col gap-4">
|
|
<h1 class="m-0">Build your own</h1>
|
|
<h2 class="m-0 text-base font-normal">
|
|
If you're a more technical server administrator, you can pick your own RAM and storage
|
|
options.
|
|
</h2>
|
|
</div>
|
|
|
|
<div class="flex w-full flex-col-reverse gap-2 md:w-auto md:flex-col md:items-center">
|
|
<ButtonStyled color="standard" size="large">
|
|
<button
|
|
v-if="!isLargeAtCapacity"
|
|
class="w-full md:w-fit"
|
|
@click="selectProduct(pyroProducts, true)"
|
|
>
|
|
Build your own
|
|
<RightArrowIcon class="!min-h-4 !min-w-4" />
|
|
</button>
|
|
<NuxtLink
|
|
v-else
|
|
:to="loggedOut ? redirectUrl : 'https://support.modrinth.com'"
|
|
:target="loggedOut ? '_self' : '_blank'"
|
|
class="w-full md:w-fit"
|
|
>
|
|
<template v-if="loggedOut">
|
|
Login
|
|
<UserIcon class="!min-h-4 !min-w-4" />
|
|
</template>
|
|
<template v-else>
|
|
Out of Stock
|
|
<ExternalIcon class="!min-h-4 !min-w-4" />
|
|
</template>
|
|
</NuxtLink>
|
|
</ButtonStyled>
|
|
<p class="m-0 text-sm">Starting at $3/GB RAM</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ButtonStyled, PurchaseModal } from "@modrinth/ui";
|
|
import {
|
|
BoxIcon,
|
|
GameIcon,
|
|
RightArrowIcon,
|
|
SearchIcon,
|
|
SortAscendingIcon,
|
|
ExternalIcon,
|
|
TerminalSquareIcon,
|
|
UserIcon,
|
|
} from "@modrinth/assets";
|
|
import { products } from "~/generated/state.json";
|
|
import LoaderIcon from "~/components/ui/servers/icons/LoaderIcon.vue";
|
|
|
|
const pyroProducts = products.filter((p) => p.metadata.type === "pyro");
|
|
const pyroPlanProducts = pyroProducts.filter(
|
|
(p) => p.metadata.ram === 4096 || p.metadata.ram === 6144 || p.metadata.ram === 8192,
|
|
);
|
|
pyroPlanProducts.sort((a, b) => a.metadata.ram - b.metadata.ram);
|
|
// yep. this is a thing.
|
|
if (!pyroProducts.metadata) {
|
|
pyroProducts.metadata = {};
|
|
}
|
|
pyroProducts.metadata.type = "pyro";
|
|
|
|
const title = "Modrinth Servers";
|
|
const description =
|
|
"Start your own Minecraft server directly on Modrinth. Play your favorite mods, plugins, and datapacks — without the hassle of setup.";
|
|
|
|
useSeoMeta({
|
|
title,
|
|
description,
|
|
ogTitle: title,
|
|
ogDescription: description,
|
|
});
|
|
|
|
useHead({
|
|
script: [
|
|
{
|
|
src: "https://js.stripe.com/v3/",
|
|
defer: true,
|
|
async: true,
|
|
},
|
|
],
|
|
});
|
|
|
|
const auth = await useAuth();
|
|
const data = useNuxtApp();
|
|
const config = useRuntimeConfig();
|
|
const purchaseModal = ref(null);
|
|
const country = useUserCountry();
|
|
const customer = ref(null);
|
|
const paymentMethods = ref([]);
|
|
const selectedProduct = ref(null);
|
|
const customServer = ref(false);
|
|
const showModal = ref(false);
|
|
const modalKey = ref(0);
|
|
|
|
const words = ["my-smp", "medieval-masters", "create-server", "mega-smp", "spookypack"];
|
|
const currentWordIndex = ref(0);
|
|
const currentText = ref("");
|
|
const isDeleting = ref(false);
|
|
const typingSpeed = 75;
|
|
const deletingSpeed = 25;
|
|
const pauseTime = 2000;
|
|
|
|
const loggedOut = computed(() => !auth.value.user);
|
|
const redirectUrl = `/auth/sign-in?redirect=${encodeURIComponent("/servers#plan")}`;
|
|
|
|
const { data: hasServers } = await useAsyncData("ServerListCountCheck", async () => {
|
|
try {
|
|
if (!auth.value.user) return false;
|
|
const response = await usePyroFetch("servers");
|
|
return response.servers && response.servers.length > 0;
|
|
} catch {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
const { data: capacityStatuses, refresh: refreshCapacity } = await useAsyncData(
|
|
"ServerCapacityAll",
|
|
async () => {
|
|
try {
|
|
const capacityChecks = pyroPlanProducts.map((product) =>
|
|
usePyroFetch("capacity", {
|
|
method: "POST",
|
|
body: {
|
|
cpu: product.metadata.cpu,
|
|
memory_mb: product.metadata.ram,
|
|
swap_mb: product.metadata.swap,
|
|
storage_mb: product.metadata.storage,
|
|
},
|
|
}),
|
|
);
|
|
|
|
const results = await Promise.all(capacityChecks);
|
|
return {
|
|
small: results[0],
|
|
medium: results[1],
|
|
large: results[2],
|
|
};
|
|
} catch (error) {
|
|
console.error("Error checking server capacities:", error);
|
|
return {
|
|
small: { available: 0 },
|
|
medium: { available: 0 },
|
|
large: { available: 0 },
|
|
};
|
|
}
|
|
},
|
|
);
|
|
|
|
const isSmallAtCapacity = computed(() => capacityStatuses.value?.small?.available === 0);
|
|
const isMediumAtCapacity = computed(() => capacityStatuses.value?.medium?.available === 0);
|
|
const isLargeAtCapacity = computed(() => capacityStatuses.value?.large?.available === 0);
|
|
|
|
const startTyping = () => {
|
|
const currentWord = words[currentWordIndex.value];
|
|
if (isDeleting.value) {
|
|
if (currentText.value.length > 0) {
|
|
currentText.value = currentText.value.slice(0, -1);
|
|
setTimeout(startTyping, deletingSpeed);
|
|
} else {
|
|
isDeleting.value = false;
|
|
currentWordIndex.value = (currentWordIndex.value + 1) % words.length;
|
|
setTimeout(startTyping, typingSpeed);
|
|
}
|
|
} else if (currentText.value.length < currentWord.length) {
|
|
currentText.value = currentWord.slice(0, currentText.value.length + 1);
|
|
setTimeout(startTyping, typingSpeed);
|
|
} else {
|
|
isDeleting.value = true;
|
|
setTimeout(startTyping, pauseTime);
|
|
}
|
|
};
|
|
|
|
const handleError = (err) => {
|
|
addNotification({
|
|
group: "main",
|
|
title: "An error occurred",
|
|
type: "error",
|
|
text: err.message ?? (err.data ? err.data.description : err),
|
|
});
|
|
};
|
|
|
|
const handleModalHidden = () => {
|
|
showModal.value = false;
|
|
};
|
|
|
|
watch(selectedProduct, async (newProduct) => {
|
|
if (newProduct) {
|
|
showModal.value = false;
|
|
await nextTick();
|
|
showModal.value = true;
|
|
modalKey.value++;
|
|
await nextTick();
|
|
if (purchaseModal.value && purchaseModal.value.show) {
|
|
purchaseModal.value.show();
|
|
}
|
|
}
|
|
});
|
|
|
|
async function fetchPaymentData() {
|
|
if (!auth.value.user) return;
|
|
try {
|
|
const [customerData, paymentMethodsData] = await Promise.all([
|
|
useBaseFetch("billing/customer", { internal: true }),
|
|
useBaseFetch("billing/payment_methods", { internal: true }),
|
|
]);
|
|
customer.value = customerData;
|
|
paymentMethods.value = paymentMethodsData;
|
|
} catch (error) {
|
|
console.error("Error fetching payment data:", error);
|
|
addNotification({
|
|
group: "main",
|
|
title: "Error fetching payment data",
|
|
type: "error",
|
|
text: error.message || "An unexpected error occurred",
|
|
});
|
|
}
|
|
}
|
|
|
|
const route = useRoute();
|
|
const isAtCapacity = computed(
|
|
() => isSmallAtCapacity.value && isMediumAtCapacity.value && isLargeAtCapacity.value,
|
|
);
|
|
|
|
const selectProduct = async (product, custom) => {
|
|
if (isAtCapacity.value) {
|
|
addNotification({
|
|
group: "main",
|
|
title: "Server Capacity Full",
|
|
type: "error",
|
|
text: "We are currently at capacity. Please try again later.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await refreshCapacity();
|
|
|
|
if (isAtCapacity.value) {
|
|
addNotification({
|
|
group: "main",
|
|
title: "Server Capacity Full",
|
|
type: "error",
|
|
text: "We are currently at capacity. Please try again later.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!auth.value.user) {
|
|
data.$router.push(redirectUrl);
|
|
return;
|
|
}
|
|
|
|
customServer.value = !!custom;
|
|
selectedProduct.value = product;
|
|
showModal.value = true;
|
|
modalKey.value++;
|
|
await nextTick();
|
|
if (purchaseModal.value && purchaseModal.value.show) {
|
|
purchaseModal.value.show();
|
|
}
|
|
};
|
|
|
|
const openPurchaseModal = () => {
|
|
if (isAtCapacity.value) {
|
|
addNotification({
|
|
group: "main",
|
|
title: "Server Capacity Full",
|
|
type: "error",
|
|
text: "We are currently at capacity. Please try again later.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
refreshCapacity();
|
|
|
|
if (isAtCapacity.value) {
|
|
addNotification({
|
|
group: "main",
|
|
title: "Server Capacity Full",
|
|
type: "error",
|
|
text: "We are currently at capacity. Please try again later.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
customServer.value = false;
|
|
selectedProduct.value = pyroPlanProducts[0];
|
|
showModal.value = true;
|
|
modalKey.value++;
|
|
nextTick(() => {
|
|
if (purchaseModal.value && purchaseModal.value.show) {
|
|
purchaseModal.value.show();
|
|
}
|
|
});
|
|
};
|
|
|
|
onMounted(() => {
|
|
startTyping();
|
|
if (route.query.showModal) {
|
|
openPurchaseModal();
|
|
}
|
|
});
|
|
|
|
watch(customer, (newCustomer) => {
|
|
if (newCustomer) {
|
|
if (route.query.showModal) {
|
|
openPurchaseModal();
|
|
}
|
|
}
|
|
});
|
|
|
|
onMounted(() => {
|
|
document.body.style.background = "var(--color-accent-contrast)";
|
|
document.body.style.overflowX = "hidden !important";
|
|
const layoutDiv = document.querySelector(".layout");
|
|
if (layoutDiv) {
|
|
layoutDiv.style.background = "var(--color-accent-contrast)";
|
|
}
|
|
fetchPaymentData();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
document.body.style.background = "";
|
|
document.body.style.overflowX = "";
|
|
const layoutDiv = document.querySelector(".layout");
|
|
if (layoutDiv) {
|
|
layoutDiv.style.background = "";
|
|
}
|
|
if (window.Stripe) {
|
|
window.Stripe = null;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.servers-hero {
|
|
background: radial-gradient(
|
|
65% 30% at 50% -10%,
|
|
var(--color-brand-highlight) 0%,
|
|
var(--color-accent-contrast) 100%
|
|
);
|
|
}
|
|
|
|
.faded-brand-line {
|
|
background: linear-gradient(to right, var(--color-brand-highlight), transparent);
|
|
}
|
|
</style>
|