From 5d2eda5840d166b744847ef9f07e02317c40d20b Mon Sep 17 00:00:00 2001 From: DecDuck Date: Wed, 2 Jul 2025 15:13:15 +1000 Subject: [PATCH 01/26] feat: small library tweaks + company page --- .github/workflows/ci.yml | 6 ++ components/GameCarousel.vue | 12 +++- components/GameEditor/Metadata.vue | 2 + components/LibraryDirectory.vue | 6 +- pages/admin/library/index.vue | 2 +- pages/store/[id]/index.vue | 35 +++++++++ pages/store/c/[id]/index.vue | 87 +++++++++++++++++++++++ server/api/v1/companies/[id]/index.get.ts | 41 +++++++++++ server/api/v1/games/[id]/index.get.ts | 16 +++++ server/internal/metadata/manual.ts | 1 + 10 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 pages/store/c/[id]/index.vue create mode 100644 server/api/v1/companies/[id]/index.get.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f04182eb..9ed5caf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,12 @@ on: pull_request: branches: - develop +<<<<<<< Updated upstream +======= + +permissions: + contents: read +>>>>>>> Stashed changes permissions: contents: read diff --git a/components/GameCarousel.vue b/components/GameCarousel.vue index bb40069c..0f4787c6 100644 --- a/components/GameCarousel.vue +++ b/components/GameCarousel.vue @@ -35,11 +35,17 @@ diff --git a/pages/store/c/[id]/index.vue b/pages/store/c/[id]/index.vue new file mode 100644 index 00000000..4da4b701 --- /dev/null +++ b/pages/store/c/[id]/index.vue @@ -0,0 +1,87 @@ + + + + diff --git a/server/api/v1/companies/[id]/index.get.ts b/server/api/v1/companies/[id]/index.get.ts new file mode 100644 index 00000000..45c28b5f --- /dev/null +++ b/server/api/v1/companies/[id]/index.get.ts @@ -0,0 +1,41 @@ +import aclManager from "~/server/internal/acls"; +import prisma from "~/server/internal/db/database"; + +export default defineEventHandler(async (h3) => { + const userId = await aclManager.getUserIdACL(h3, ["store:read"]); + if (!userId) throw createError({ statusCode: 403 }); + + const companyId = getRouterParam(h3, "id"); + if (!companyId) + throw createError({ + statusCode: 400, + statusMessage: "Missing gameId in route params (somehow...?)", + }); + + const company = await prisma.company.findUnique({ + where: { id: companyId }, + include: { + published: { + select: { + id: true, + mName: true, + mShortDescription: true, + mCoverObjectId: true, + }, + }, + developed: { + select: { + id: true, + mName: true, + mShortDescription: true, + mCoverObjectId: true, + }, + }, + }, + }); + + if (!company) + throw createError({ statusCode: 404, statusMessage: "Company not found" }); + + return { company }; +}); diff --git a/server/api/v1/games/[id]/index.get.ts b/server/api/v1/games/[id]/index.get.ts index f12e7ba2..2cf0041d 100644 --- a/server/api/v1/games/[id]/index.get.ts +++ b/server/api/v1/games/[id]/index.get.ts @@ -16,6 +16,22 @@ export default defineEventHandler(async (h3) => { where: { id: gameId }, include: { versions: true, + publishers: { + select: { + id: true, + mName: true, + mShortDescription: true, + mLogoObjectId: true, + }, + }, + developers: { + select: { + id: true, + mName: true, + mShortDescription: true, + mLogoObjectId: true, + }, + }, }, }); diff --git a/server/internal/metadata/manual.ts b/server/internal/metadata/manual.ts index a2a52ea8..dd4d6fab 100644 --- a/server/internal/metadata/manual.ts +++ b/server/internal/metadata/manual.ts @@ -7,6 +7,7 @@ import type { CompanyMetadata, } from "./types"; import * as jdenticon from "jdenticon"; +import { randomUUID } from "crypto"; export class ManualMetadataProvider implements MetadataProvider { name() { From 59a9dd034810bb440b9fb3bb923e3d77eebbaf15 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 20 Jul 2025 19:36:12 +1000 Subject: [PATCH 02/26] feat: new store view --- components/AddLibraryButton.vue | 2 +- .../Library.vue} | 0 .../{NewsDirectory.vue => Directory/News.vue} | 0 components/GameEditor/Metadata.vue | 2 +- .../CreateCollection.vue} | 0 .../DeleteCollection.vue} | 0 .../DeleteNews.vue} | 0 .../DeleteUser.vue} | 0 .../UploadFile.vue} | 0 components/StoreView.vue | 441 ++++++++++++++++++ composables/store.ts | 14 + pages/admin/library/index.vue | 2 +- pages/admin/users/index.vue | 3 +- pages/library.vue | 4 +- pages/library/index.vue | 4 +- pages/news.vue | 4 +- pages/news/[id]/index.vue | 2 +- pages/store/c/[id]/index.vue | 46 +- pages/store/index.vue | 39 +- .../migration.sql | 5 + prisma/models/content.prisma | 22 + server/api/v1/store/genres.get.ts | 10 + server/api/v1/store/index.get.ts | 114 +++++ server/api/v1/store/tags.get.ts | 10 + server/internal/library/index.ts | 5 + server/internal/metadata/giantbomb.ts | 10 +- server/internal/metadata/igdb.ts | 5 +- server/internal/metadata/index.ts | 17 +- server/internal/metadata/manual.ts | 2 +- server/internal/metadata/pcgamingwiki.ts | 3 +- server/internal/metadata/types.d.ts | 1 + 31 files changed, 682 insertions(+), 85 deletions(-) rename components/{LibraryDirectory.vue => Directory/Library.vue} (100%) rename components/{NewsDirectory.vue => Directory/News.vue} (100%) rename components/{CreateCollectionModal.vue => Modal/CreateCollection.vue} (100%) rename components/{DeleteCollectionModal.vue => Modal/DeleteCollection.vue} (100%) rename components/{DeleteNewsModal.vue => Modal/DeleteNews.vue} (100%) rename components/{DeleteUserModal.vue => Modal/DeleteUser.vue} (100%) rename components/{UploadFileDialog.vue => Modal/UploadFile.vue} (100%) create mode 100644 components/StoreView.vue create mode 100644 composables/store.ts create mode 100644 prisma/migrations/20250720070939_static_genres/migration.sql create mode 100644 server/api/v1/store/genres.get.ts create mode 100644 server/api/v1/store/index.get.ts create mode 100644 server/api/v1/store/tags.get.ts diff --git a/components/AddLibraryButton.vue b/components/AddLibraryButton.vue index 526e2eb2..226bbc1c 100644 --- a/components/AddLibraryButton.vue +++ b/components/AddLibraryButton.vue @@ -84,7 +84,7 @@ - diff --git a/components/LibraryDirectory.vue b/components/Directory/Library.vue similarity index 100% rename from components/LibraryDirectory.vue rename to components/Directory/Library.vue diff --git a/components/NewsDirectory.vue b/components/Directory/News.vue similarity index 100% rename from components/NewsDirectory.vue rename to components/Directory/News.vue diff --git a/components/GameEditor/Metadata.vue b/components/GameEditor/Metadata.vue index 59210ade..3ea937e6 100644 --- a/components/GameEditor/Metadata.vue +++ b/components/GameEditor/Metadata.vue @@ -268,7 +268,7 @@ - +
+
+ + + + +
+ + +
+ + +
+

Filters

+ +
+ + +
+ +

+ + {{ + section.name + }} + + + +

+ +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+ + Sort + +
+ + + +
+ + + +
+
+
+
+ + + +
+
+ +
+

Games

+ +
+ + + + +
+ + +
+ +
+
+
+ No results +
+
+
+
+
+
+ + + diff --git a/composables/store.ts b/composables/store.ts new file mode 100644 index 00000000..adaffb0a --- /dev/null +++ b/composables/store.ts @@ -0,0 +1,14 @@ +export type StoreFilterOption = { + name: string; + param: string; + options: Array<{ + name: string; + value: string; + }>; + multiple?: boolean; +}; + +export type StoreSortOption = { + name: string; + param: string; +} \ No newline at end of file diff --git a/pages/admin/library/index.vue b/pages/admin/library/index.vue index 807d2ff5..ece59112 100644 --- a/pages/admin/library/index.vue +++ b/pages/admin/library/index.vue @@ -91,7 +91,7 @@ {{ game.mName }} {{ game.metadataSource }}{{ game.library!.name }}
diff --git a/pages/admin/users/index.vue b/pages/admin/users/index.vue index 548bfbaf..b800bb2b 100644 --- a/pages/admin/users/index.vue +++ b/pages/admin/users/index.vue @@ -138,12 +138,11 @@ - + diff --git a/pages/store/index.vue b/pages/store/index.vue index 7eadaadf..53a21aa5 100644 --- a/pages/store/index.vue +++ b/pages/store/index.vue @@ -31,7 +31,7 @@ > {{ game.mName }} -

+

{{ game.mShortDescription }}

@@ -71,7 +71,7 @@ diff --git a/components/GamePanel.vue b/components/GamePanel.vue index d7fd4d8b..97b4651c 100644 --- a/components/GamePanel.vue +++ b/components/GamePanel.vue @@ -6,7 +6,7 @@ 'transition-all duration-300 text-left hover:scale-[1.02] hover:shadow-lg hover:-translate-y-0.5': animate, }" - class="group relative w-48 h-64 rounded-lg overflow-hidden" + class="group relative flex-1 min-w-42 max-w-48 h-64 rounded-lg overflow-hidden" >
+ + {{ item.name }} + + + + No items selected. + +
+ +
+ + + + + + +
  • + + {{ item.name }} + +
  • +
    +
    +
    +
    +
    + + + diff --git a/components/StoreView.vue b/components/StoreView.vue index 00927be4..2dea8bb1 100644 --- a/components/StoreView.vue +++ b/components/StoreView.vue @@ -72,10 +72,13 @@ -
    +
    @@ -85,7 +88,7 @@ :id="`filter-${section.param}-${option}`" v-model=" (optionValues[section.param] as any)[ - option.value + option.param ] " :name="`${section.param}[]`" @@ -101,7 +104,7 @@ class="col-start-1 row-start-1 appearance-none rounded-sm border border-gray-300 bg-white checked:border-blue-600 checked:bg-blue-600 indeterminate:border-blue-600 indeterminate:bg-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 forced-colors:appearance-auto" @update:value=" () => - (optionValues[section.param] = option.value) + (optionValues[section.param] = option.param) " />
    @@ -113,6 +116,11 @@ >
    + @@ -133,7 +141,7 @@ - Sort + Sort
    @@ -335,7 +335,7 @@ class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-1.5 py-0.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition-all duration-200 hover:scale-105 hover:shadow-lg hover:shadow-blue-500/25 active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600" @click="() => insertImageAtCursor(image)" > - {{ $t("insert") }} + {{ $t("common.insert") }} @@ -424,7 +424,7 @@ :class="['inline-flex w-full shadow-sm sm:ml-3 sm:w-auto']" @click="() => coreMetadataUpdate_wrapper()" > - {{ $t("save") }} + {{ $t("common.save") }} @@ -28,7 +28,7 @@ scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-zinc-100 sm:pl-3" > - {{ $t("name") }} + {{ $t("common.name") }} - {{ $t("edit") }} + {{ $t("common.edit") }} @@ -84,7 +84,7 @@ class="text-blue-500 hover:text-blue-400" @click="() => edit(sourceIdx)" > - {{ $t("edit") }} + {{ $t("common.edit") }} {{ $t("chars.srComma", [source.name]) }} @@ -125,7 +125,7 @@ {{ $t("common.name") }}

    {{ $t("library.admin.sources.nameDesc") }} @@ -228,7 +228,7 @@ class="w-full sm:w-fit" @click="() => performActionSource_wrapper()" > - {{ createMode ? $t("create") : $t("save") }} + {{ createMode ? $t("common.create") : $t("common.save") }} @@ -491,7 +491,7 @@ function coreMetadataUploadFiles(e: InputEvent) { { title: t("errors.upload.title"), description: t("errors.upload.description", [t("errors.unknown")]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -530,7 +530,7 @@ function coreMetadataUpdate_wrapper() { description: t("errors.game.metadata.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -589,7 +589,7 @@ watch(descriptionHTML, (_v) => { description: t("errors.game.description.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -633,7 +633,7 @@ async function updateBannerImage(id: string) { description: t("errors.game.banner.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -659,7 +659,7 @@ async function updateCoverImage(id: string) { description: t("errors.game.cover.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -688,7 +688,7 @@ async function deleteImage(id: string) { description: t("errors.game.deleteImage.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -730,7 +730,7 @@ async function updateImageCarousel() { description: t("errors.game.carousel.description", [ (e as H3Error)?.statusMessage ?? t("errors.unknown"), ]), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); diff --git a/components/GameEditor/Version.vue b/components/GameEditor/Version.vue index 98289147..aae4171a 100644 --- a/components/GameEditor/Version.vue +++ b/components/GameEditor/Version.vue @@ -140,7 +140,7 @@ async function updateVersionOrder() { description: t("errors.version.order.desc", { error: (e as H3Error)?.statusMessage ?? t("errors.unknown"), }), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); @@ -168,7 +168,7 @@ async function deleteVersion(versionName: string) { description: t("errors.version.delete.desc", { error: (e as H3Error)?.statusMessage ?? t("errors.unknown"), }), - buttonText: t("close"), + buttonText: t("common.close"), }, (e, c) => c(), ); diff --git a/components/MultiItemSelector.vue b/components/MultiItemSelector.vue index da58fdaf..c6bab47c 100644 --- a/components/MultiItemSelector.vue +++ b/components/MultiItemSelector.vue @@ -12,7 +12,7 @@ class="group relative -mr-1 size-3.5 rounded-xs hover:bg-blue-600/20" @click="() => remove(item.param)" > - Remove + {{ $t("common.remove") }} - No items selected. + {{ $t("common.noSelected") }} diff --git a/components/StoreView.vue b/components/StoreView.vue index 2dea8bb1..7b1e56c2 100644 --- a/components/StoreView.vue +++ b/components/StoreView.vue @@ -33,14 +33,16 @@ class="relative ml-auto flex size-full max-w-sm flex-col overflow-y-auto bg-zinc-900 pt-4 pb-6 shadow-xl" >

    -

    Filters

    +

    + {{ $t("store.view.srFilters") }} +

    @@ -141,7 +143,7 @@ - Sort + {{ $t("store.view.sort") }}