mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-13 16:22:39 +10:00
refactor: rename version tab to configuration
This commit is contained in:
173
components/GameEditor/Configuration.vue
Normal file
173
components/GameEditor/Configuration.vue
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
|
<template>
|
||||||
|
<div v-if="game" class="h-full grow flex flex-row gap-y-8">
|
||||||
|
<div class="grow w-full h-full px-6 py-4 flex flex-col">
|
||||||
|
<!-- version manager -->
|
||||||
|
<div>
|
||||||
|
<!-- version priority -->
|
||||||
|
<div>
|
||||||
|
<div class="border-b border-zinc-800 pb-3">
|
||||||
|
<div
|
||||||
|
class="flex flex-wrap items-center justify-between sm:flex-nowrap"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
class="text-base font-semibold font-display leading-6 text-zinc-100"
|
||||||
|
>
|
||||||
|
{{ $t("library.admin.versionPriority") }}
|
||||||
|
|
||||||
|
<!-- import games button -->
|
||||||
|
|
||||||
|
<NuxtLink
|
||||||
|
v-if="unimportedVersions !== undefined"
|
||||||
|
:href="
|
||||||
|
unimportedVersions.length > 0
|
||||||
|
? `/admin/library/${game.id}/import`
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
type="button"
|
||||||
|
:class="[
|
||||||
|
unimportedVersions.length > 0
|
||||||
|
? 'bg-blue-600 hover:bg-blue-700'
|
||||||
|
: 'bg-blue-800/50',
|
||||||
|
'inline-flex w-fit items-center gap-x-2 rounded-md px-3 py-1 text-sm font-semibold font-display text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
unimportedVersions.length > 0
|
||||||
|
? $t("library.admin.import.version.import")
|
||||||
|
: $t("library.admin.import.version.noVersions")
|
||||||
|
}}
|
||||||
|
</NuxtLink>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 text-center w-full text-sm text-zinc-600">
|
||||||
|
{{ $t("lowest") }}
|
||||||
|
</div>
|
||||||
|
<draggable
|
||||||
|
:list="game.versions"
|
||||||
|
handle=".handle"
|
||||||
|
class="mt-2 space-y-4"
|
||||||
|
@update="() => updateVersionOrder()"
|
||||||
|
>
|
||||||
|
<template #item="{ element: item }: { element: GameVersion }">
|
||||||
|
<div
|
||||||
|
class="w-full inline-flex items-center px-4 py-2 bg-zinc-800 rounded justify-between"
|
||||||
|
>
|
||||||
|
<div class="text-zinc-100 font-semibold">
|
||||||
|
{{ item.versionName }}
|
||||||
|
</div>
|
||||||
|
<div class="text-zinc-400">
|
||||||
|
{{ item.delta ? $t("library.admin.version.delta") : "" }}
|
||||||
|
</div>
|
||||||
|
<div class="inline-flex items-center gap-x-2">
|
||||||
|
<component
|
||||||
|
:is="PLATFORM_ICONS[item.platform]"
|
||||||
|
class="size-6 text-blue-600"
|
||||||
|
/>
|
||||||
|
<Bars3Icon class="cursor-move w-6 h-6 text-zinc-400 handle" />
|
||||||
|
<button @click="() => deleteVersion(item.versionName)">
|
||||||
|
<TrashIcon class="w-5 h-5 text-red-600" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
<div
|
||||||
|
v-if="game.versions.length == 0"
|
||||||
|
class="text-center font-bold text-zinc-400 my-3"
|
||||||
|
>
|
||||||
|
{{ $t("library.admin.version.noVersionsAdded") }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-center w-full text-sm text-zinc-600">
|
||||||
|
{{ $t("highest") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="lg:overflow-y-auto lg:border-l lg:border-zinc-800 lg:block lg:inset-y-0 lg:z-50 lg:w-[30vw] flex flex-col gap-y-8 px-6 py-4"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Game, GameVersion } from "~/prisma/client";
|
||||||
|
import { Bars3Icon, TrashIcon } from "@heroicons/vue/24/solid";
|
||||||
|
import type { SerializeObject } from "nitropack";
|
||||||
|
import type { H3Error } from "h3";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: "admin",
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO implement UI for this page
|
||||||
|
|
||||||
|
defineProps<{ unimportedVersions: string[] }>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
type GameAndVersions = Game & { versions: GameVersion[] };
|
||||||
|
const game = defineModel<SerializeObject<GameAndVersions>>() as Ref<
|
||||||
|
SerializeObject<GameAndVersions>
|
||||||
|
>;
|
||||||
|
if (!game.value)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: "Game not provided to editor component",
|
||||||
|
});
|
||||||
|
|
||||||
|
async function updateVersionOrder() {
|
||||||
|
try {
|
||||||
|
const newVersions = await $dropFetch("/api/v1/admin/game/version", {
|
||||||
|
method: "PATCH",
|
||||||
|
body: {
|
||||||
|
id: game.value.id,
|
||||||
|
versions: game.value.versions.map((e) => e.versionName),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
game.value.versions = newVersions;
|
||||||
|
} catch (e) {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: t("errors.version.order.title"),
|
||||||
|
description: t("errors.version.order.desc", {
|
||||||
|
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
||||||
|
}),
|
||||||
|
buttonText: t("close"),
|
||||||
|
},
|
||||||
|
(e, c) => c(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteVersion(versionName: string) {
|
||||||
|
try {
|
||||||
|
await $dropFetch("/api/v1/admin/game/version", {
|
||||||
|
method: "DELETE",
|
||||||
|
body: {
|
||||||
|
id: game.value.id,
|
||||||
|
versionName: versionName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
game.value.versions.splice(
|
||||||
|
game.value.versions.findIndex((e) => e.versionName === versionName),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: t("errors.version.delete.title"),
|
||||||
|
description: t("errors.version.delete.desc", {
|
||||||
|
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
||||||
|
}),
|
||||||
|
buttonText: t("close"),
|
||||||
|
},
|
||||||
|
(e, c) => c(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -1,177 +0,0 @@
|
|||||||
<!-- eslint-disable vue/no-v-html -->
|
|
||||||
<template>
|
|
||||||
<div v-if="game">
|
|
||||||
<div class="grow flex flex-row gap-y-8">
|
|
||||||
<div class="grow w-full h-full px-6 py-4 flex flex-col"></div>
|
|
||||||
<div
|
|
||||||
class="lg:overflow-y-auto lg:border-l lg:border-zinc-800 lg:block lg:inset-y-0 lg:z-50 lg:w-[30vw] flex flex-col gap-y-8 px-6 py-4"
|
|
||||||
>
|
|
||||||
<!-- version manager -->
|
|
||||||
<div>
|
|
||||||
<!-- version priority -->
|
|
||||||
<div>
|
|
||||||
<div class="border-b border-zinc-800 pb-3">
|
|
||||||
<div
|
|
||||||
class="flex flex-wrap items-center justify-between sm:flex-nowrap"
|
|
||||||
>
|
|
||||||
<h3
|
|
||||||
class="text-base font-semibold font-display leading-6 text-zinc-100"
|
|
||||||
>
|
|
||||||
{{ $t("library.admin.versionPriority") }}
|
|
||||||
|
|
||||||
<!-- import games button -->
|
|
||||||
|
|
||||||
<NuxtLink
|
|
||||||
v-if="unimportedVersions !== undefined"
|
|
||||||
:href="
|
|
||||||
unimportedVersions.length > 0
|
|
||||||
? `/admin/library/${game.id}/import`
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
type="button"
|
|
||||||
:class="[
|
|
||||||
unimportedVersions.length > 0
|
|
||||||
? 'bg-blue-600 hover:bg-blue-700'
|
|
||||||
: 'bg-blue-800/50',
|
|
||||||
'inline-flex w-fit items-center gap-x-2 rounded-md px-3 py-1 text-sm font-semibold font-display text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600',
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
unimportedVersions.length > 0
|
|
||||||
? $t("library.admin.import.version.import")
|
|
||||||
: $t("library.admin.import.version.noVersions")
|
|
||||||
}}
|
|
||||||
</NuxtLink>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 text-center w-full text-sm text-zinc-600">
|
|
||||||
{{ $t("lowest") }}
|
|
||||||
</div>
|
|
||||||
<draggable
|
|
||||||
:list="game.versions"
|
|
||||||
handle=".handle"
|
|
||||||
class="mt-2 space-y-4"
|
|
||||||
@update="() => updateVersionOrder()"
|
|
||||||
>
|
|
||||||
<template #item="{ element: item }: { element: GameVersion }">
|
|
||||||
<div
|
|
||||||
class="w-full inline-flex items-center px-4 py-2 bg-zinc-800 rounded justify-between"
|
|
||||||
>
|
|
||||||
<div class="text-zinc-100 font-semibold">
|
|
||||||
{{ item.versionName }}
|
|
||||||
</div>
|
|
||||||
<div class="text-zinc-400">
|
|
||||||
{{ item.delta ? $t("library.admin.version.delta") : "" }}
|
|
||||||
</div>
|
|
||||||
<div class="inline-flex items-center gap-x-2">
|
|
||||||
<component
|
|
||||||
:is="PLATFORM_ICONS[item.platform]"
|
|
||||||
class="size-6 text-blue-600"
|
|
||||||
/>
|
|
||||||
<Bars3Icon
|
|
||||||
class="cursor-move w-6 h-6 text-zinc-400 handle"
|
|
||||||
/>
|
|
||||||
<button @click="() => deleteVersion(item.versionName)">
|
|
||||||
<TrashIcon class="w-5 h-5 text-red-600" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</draggable>
|
|
||||||
<div
|
|
||||||
v-if="game.versions.length == 0"
|
|
||||||
class="text-center font-bold text-zinc-400 my-3"
|
|
||||||
>
|
|
||||||
{{ $t("library.admin.version.noVersionsAdded") }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-2 text-center w-full text-sm text-zinc-600">
|
|
||||||
{{ $t("highest") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import type { Game, GameVersion } from "~/prisma/client";
|
|
||||||
import { Bars3Icon, TrashIcon } from "@heroicons/vue/24/solid";
|
|
||||||
import type { SerializeObject } from "nitropack";
|
|
||||||
import type { H3Error } from "h3";
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: "admin",
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO implement UI for this page
|
|
||||||
|
|
||||||
defineProps<{ unimportedVersions: string[] }>();
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
type GameAndVersions = Game & { versions: GameVersion[] };
|
|
||||||
const game = defineModel<SerializeObject<GameAndVersions>>() as Ref<
|
|
||||||
SerializeObject<GameAndVersions>
|
|
||||||
>;
|
|
||||||
if (!game.value)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 500,
|
|
||||||
statusMessage: "Game not provided to editor component",
|
|
||||||
});
|
|
||||||
|
|
||||||
async function updateVersionOrder() {
|
|
||||||
try {
|
|
||||||
const newVersions = await $dropFetch("/api/v1/admin/game/version", {
|
|
||||||
method: "PATCH",
|
|
||||||
body: {
|
|
||||||
id: game.value.id,
|
|
||||||
versions: game.value.versions.map((e) => e.versionName),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
game.value.versions = newVersions;
|
|
||||||
} catch (e) {
|
|
||||||
createModal(
|
|
||||||
ModalType.Notification,
|
|
||||||
{
|
|
||||||
title: t("errors.version.order.title"),
|
|
||||||
description: t("errors.version.order.desc", {
|
|
||||||
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
|
||||||
}),
|
|
||||||
buttonText: t("close"),
|
|
||||||
},
|
|
||||||
(e, c) => c(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteVersion(versionName: string) {
|
|
||||||
try {
|
|
||||||
await $dropFetch("/api/v1/admin/game/version", {
|
|
||||||
method: "DELETE",
|
|
||||||
body: {
|
|
||||||
id: game.value.id,
|
|
||||||
versionName: versionName,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
game.value.versions.splice(
|
|
||||||
game.value.versions.findIndex((e) => e.versionName === versionName),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
createModal(
|
|
||||||
ModalType.Notification,
|
|
||||||
{
|
|
||||||
title: t("errors.version.delete.title"),
|
|
||||||
description: t("errors.version.delete.desc", {
|
|
||||||
error: (e as H3Error)?.statusMessage ?? t("errors.unknown"),
|
|
||||||
}),
|
|
||||||
buttonText: t("close"),
|
|
||||||
},
|
|
||||||
(e, c) => c(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -7,67 +7,6 @@
|
|||||||
>
|
>
|
||||||
<!--start-->
|
<!--start-->
|
||||||
<div>
|
<div>
|
||||||
<Listbox v-if="false" v-model="currentMode" as="div">
|
|
||||||
<div class="relative mt-2">
|
|
||||||
<ListboxButton
|
|
||||||
class="min-w-[10vw] w-full cursor-default inline-flex items-center gap-x-2 rounded-md bg-zinc-900 py-1.5 pr-2 pl-3 text-left text-zinc-200 outline-1 -outline-offset-1 outline-zinc-700 focus:outline-2 focus:-outline-offset-2 focus:outline-blue-600 sm:text-sm/6"
|
|
||||||
>
|
|
||||||
<span class="col-start-1 row-start-1 truncate">{{
|
|
||||||
currentMode
|
|
||||||
}}</span>
|
|
||||||
|
|
||||||
<PencilIcon class="ml-auto size-5" />
|
|
||||||
|
|
||||||
<ChevronUpDownIcon
|
|
||||||
class="text-gray-500 size-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</ListboxButton>
|
|
||||||
|
|
||||||
<transition
|
|
||||||
leave-active-class="transition ease-in duration-100"
|
|
||||||
leave-from-class="opacity-100"
|
|
||||||
leave-to-class="opacity-0"
|
|
||||||
>
|
|
||||||
<ListboxOptions
|
|
||||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-white/5 focus:outline-hidden sm:text-sm"
|
|
||||||
>
|
|
||||||
<ListboxOption
|
|
||||||
v-for="[value] in Object.entries(components)"
|
|
||||||
v-slot="{ active, selected }"
|
|
||||||
:key="value"
|
|
||||||
as="template"
|
|
||||||
:value="value"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
:class="[
|
|
||||||
active
|
|
||||||
? 'bg-blue-600 text-white outline-hidden'
|
|
||||||
: 'text-zinc-100',
|
|
||||||
'relative cursor-default py-2 pr-9 pl-3 select-none',
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
:class="[
|
|
||||||
selected ? 'font-semibold' : 'font-normal',
|
|
||||||
'block truncate',
|
|
||||||
]"
|
|
||||||
>{{ value }}</span
|
|
||||||
>
|
|
||||||
|
|
||||||
<span
|
|
||||||
v-if="selected"
|
|
||||||
class="text-white absolute inset-y-0 right-0 flex items-center pr-4"
|
|
||||||
>
|
|
||||||
<PencilIcon class="size-5" aria-hidden="true" />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ListboxOption>
|
|
||||||
</ListboxOptions>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</Listbox>
|
|
||||||
|
|
||||||
<div class="pt-4 inline-flex gap-x-2">
|
<div class="pt-4 inline-flex gap-x-2">
|
||||||
<div
|
<div
|
||||||
v-for="[value, { icon }] in Object.entries(components)"
|
v-for="[value, { icon }] in Object.entries(components)"
|
||||||
@ -112,19 +51,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { GameEditorConfiguration, GameEditorMetadata } from "#components";
|
||||||
Listbox,
|
|
||||||
ListboxButton,
|
|
||||||
ListboxOption,
|
|
||||||
ListboxOptions,
|
|
||||||
} from "@headlessui/vue";
|
|
||||||
import { ChevronUpDownIcon } from "@heroicons/vue/16/solid";
|
|
||||||
import { GameEditorMetadata, GameEditorVersion } from "#components";
|
|
||||||
import {
|
import {
|
||||||
ArrowTopRightOnSquareIcon,
|
ArrowTopRightOnSquareIcon,
|
||||||
DocumentIcon,
|
DocumentIcon,
|
||||||
PencilIcon,
|
WrenchIcon,
|
||||||
ServerStackIcon,
|
|
||||||
} from "@heroicons/vue/24/outline";
|
} from "@heroicons/vue/24/outline";
|
||||||
import type { Component } from "vue";
|
import type { Component } from "vue";
|
||||||
|
|
||||||
@ -146,16 +77,16 @@ useHead({
|
|||||||
|
|
||||||
enum GameEditorMode {
|
enum GameEditorMode {
|
||||||
Metadata = "Metadata",
|
Metadata = "Metadata",
|
||||||
Versions = "Versions",
|
Configuration = "Configuration",
|
||||||
}
|
}
|
||||||
|
|
||||||
const components: {
|
const components: {
|
||||||
[key in GameEditorMode]: { editor: Component; icon: Component };
|
[key in GameEditorMode]: { editor: Component; icon: Component };
|
||||||
} = {
|
} = {
|
||||||
[GameEditorMode.Metadata]: { editor: GameEditorMetadata, icon: DocumentIcon },
|
[GameEditorMode.Metadata]: { editor: GameEditorMetadata, icon: DocumentIcon },
|
||||||
[GameEditorMode.Versions]: {
|
[GameEditorMode.Configuration]: {
|
||||||
editor: GameEditorVersion,
|
editor: GameEditorConfiguration,
|
||||||
icon: ServerStackIcon,
|
icon: WrenchIcon,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export default defineDropTask({
|
|||||||
progress(currentProgress);
|
progress(currentProgress);
|
||||||
|
|
||||||
const entries = Object.entries(manifest);
|
const entries = Object.entries(manifest);
|
||||||
const increment = (1 / entries.length) * 0.9;
|
const increment = 90 / entries.length;
|
||||||
for (const [name, data] of entries) {
|
for (const [name, data] of entries) {
|
||||||
if (!data.files && !data.registry) continue;
|
if (!data.files && !data.registry) continue;
|
||||||
|
|
||||||
@ -137,6 +137,7 @@ export default defineDropTask({
|
|||||||
});
|
});
|
||||||
|
|
||||||
currentProgress += increment;
|
currentProgress += increment;
|
||||||
|
progress(currentProgress);
|
||||||
log(`Imported game "${name}"`);
|
log(`Imported game "${name}"`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user