fix(guides) : make ordering predictable
CI / Verify (push) Successful in 1m23s
CI / Build immutable images and deploy (push) Successful in 1m49s

This commit is contained in:
2026-09-09 23:40:30 +07:00 Unverified
parent 289c17808f
commit 00f59e6e85
4 changed files with 25 additions and 5 deletions
+5
View File
@@ -77,12 +77,17 @@ export async function createGuide(input: unknown) {
const slug = slugCollision ? `${baseSlug}-${character.key.toLowerCase()}` : baseSlug;
return db.transaction(async (tx) => {
await tx.execute(sql`select pg_advisory_xact_lock(hashtext('guides-sort-order'))`);
const [order] = await tx
.select({ maximum: sql<number>`coalesce(max(${guides.sortOrder}), 0)` })
.from(guides);
const [guide] = await tx.insert(guides).values({
characterKey: character.key,
slug,
name: character.name,
overview: parsed.overview,
coverMediaId: parsed.coverMediaId,
sortOrder: order.maximum + 10,
}).returning();
await tx.insert(guideSections).values(DEFAULT_SECTIONS.map((section) => ({ ...section, guideId: guide.id })));
await tx.insert(artifactGroups).values([
+4 -4
View File
@@ -1,6 +1,6 @@
import "server-only";
import { and, asc, eq, inArray, isNotNull, isNull, ne } from "drizzle-orm";
import { and, asc, desc, eq, inArray, isNotNull, isNull, ne } from "drizzle-orm";
import * as z from "zod";
import { getDb } from "@/db";
@@ -127,7 +127,7 @@ export async function listGuideCards(includeTrash = false) {
.from(guides)
.innerJoin(media, eq(media.id, guides.coverMediaId))
.where(includeTrash ? isNotNull(guides.trashedAt) : isNull(guides.trashedAt))
.orderBy(asc(guides.sortOrder), asc(guides.name));
.orderBy(desc(guides.sortOrder), desc(guides.createdAt), asc(guides.name));
return rows.map(({ coverObjectKey, ...guide }) => ({
...guide,
coverUrl: publicMediaUrl(coverObjectKey),
@@ -146,7 +146,7 @@ export async function listAdminTeamImportGuides(excludeGuideId: string) {
.from(guides)
.innerJoin(media, eq(media.id, guides.coverMediaId))
.where(and(isNull(guides.trashedAt), ne(guides.id, excludeGuideId)))
.orderBy(asc(guides.sortOrder), asc(guides.name));
.orderBy(desc(guides.sortOrder), desc(guides.createdAt), asc(guides.name));
return rows.map(({ coverObjectKey, ...guide }) => ({
...guide,
@@ -221,7 +221,7 @@ export async function listPublicGuideCards() {
.from(guides)
.innerJoin(media, eq(media.id, guides.coverMediaId))
.where(and(eq(guides.isPublic, true), isNull(guides.trashedAt)))
.orderBy(asc(guides.sortOrder), asc(guides.name)), getDb().select().from(glossaryAliases)]);
.orderBy(desc(guides.sortOrder), desc(guides.createdAt), asc(guides.name)), getDb().select().from(glossaryAliases)]);
const grouped = aliasesByItem(aliases);
return rows.map(({ coverObjectKey, ...guide }) => ({
...guide,