diff --git a/packages/backend/src/graphql/resolvers/articles.ts b/packages/backend/src/graphql/resolvers/articles.ts index 3fd2380..5c048bd 100644 --- a/packages/backend/src/graphql/resolvers/articles.ts +++ b/packages/backend/src/graphql/resolvers/articles.ts @@ -1,10 +1,11 @@ import { builder } from "../builder"; import { ArticleType, ArticleListType, AdminArticleListType } from "../types/index"; import { articles, users } from "../../db/schema/index"; -import { eq, and, lte, desc, asc, ilike, or, count, arrayContains } from "drizzle-orm"; +import { eq, and, lte, desc, asc, ilike, or, count, arrayContains, type SQL } from "drizzle-orm"; import { requireAdmin } from "../../lib/acl"; +import type { DB } from "../../db/connection"; -async function enrichArticle(db: any, article: any) { +async function enrichArticle(db: DB, article: typeof articles.$inferSelect) { let author = null; if (article.author) { const authorUser = await db @@ -39,7 +40,7 @@ builder.queryField("articles", (t) => const pageSize = args.limit ?? 24; const offset = args.offset ?? 0; - const conditions: any[] = [lte(articles.publish_date, new Date())]; + const conditions: SQL[] = [lte(articles.publish_date, new Date())]; if (args.featured !== null && args.featured !== undefined) { conditions.push(eq(articles.featured, args.featured)); } @@ -50,28 +51,24 @@ builder.queryField("articles", (t) => or( ilike(articles.title, `%${args.search}%`), ilike(articles.excerpt, `%${args.search}%`), - ), + ) as SQL, ); } - const orderArgs = - args.sortBy === "name" - ? [asc(articles.title)] - : args.sortBy === "featured" - ? [desc(articles.featured), desc(articles.publish_date)] - : [desc(articles.publish_date)]; - const where = and(...conditions); + const baseQuery = ctx.db.select().from(articles).where(where); + const ordered = + args.sortBy === "name" + ? baseQuery.orderBy(asc(articles.title)) + : args.sortBy === "featured" + ? baseQuery.orderBy(desc(articles.featured), desc(articles.publish_date)) + : baseQuery.orderBy(desc(articles.publish_date)); + const [articleList, totalRows] = await Promise.all([ - (ctx.db.select().from(articles).where(where) as any) - .orderBy(...orderArgs) - .limit(pageSize) - .offset(offset), + ordered.limit(pageSize).offset(offset), ctx.db.select({ total: count() }).from(articles).where(where), ]); - const items = await Promise.all( - articleList.map((article: any) => enrichArticle(ctx.db, article)), - ); + const items = await Promise.all(articleList.map((article) => enrichArticle(ctx.db, article))); return { items, total: totalRows[0]?.total ?? 0 }; }, }), @@ -130,13 +127,13 @@ builder.queryField("adminListArticles", (t) => const limit = args.limit ?? 50; const offset = args.offset ?? 0; - const conditions: any[] = []; + const conditions: SQL[] = []; if (args.search) { conditions.push( or( ilike(articles.title, `%${args.search}%`), ilike(articles.excerpt, `%${args.search}%`), - ), + ) as SQL, ); } if (args.category) conditions.push(eq(articles.category, args.category)); @@ -154,9 +151,7 @@ builder.queryField("adminListArticles", (t) => .offset(offset), ctx.db.select({ total: count() }).from(articles).where(where), ]); - const items = await Promise.all( - articleList.map((article: any) => enrichArticle(ctx.db, article)), - ); + const items = await Promise.all(articleList.map((article) => enrichArticle(ctx.db, article))); return { items, total: totalRows[0]?.total ?? 0 }; }, }), @@ -232,7 +227,7 @@ builder.mutationField("updateArticle", (t) => const updated = await ctx.db .update(articles) - .set(updates as any) + .set(updates as Partial) .where(eq(articles.id, args.id)) .returning(); if (!updated[0]) return null; diff --git a/packages/backend/src/graphql/resolvers/auth.ts b/packages/backend/src/graphql/resolvers/auth.ts index 25eb578..fff3b62 100644 --- a/packages/backend/src/graphql/resolvers/auth.ts +++ b/packages/backend/src/graphql/resolvers/auth.ts @@ -3,6 +3,10 @@ import { builder } from "../builder"; import { CurrentUserType } from "../types/index"; import { users } from "../../db/schema/index"; import { eq } from "drizzle-orm"; + +interface ReplyLike { + header?: (name: string, value: string) => void; +} import { hash, verify as verifyArgon } from "../../lib/argon"; import { setSession, deleteSession } from "../../lib/auth"; import { sendVerification, sendPasswordReset } from "../../lib/email"; @@ -46,12 +50,7 @@ builder.mutationField("login", (t) => // Set session cookie const isProduction = process.env.NODE_ENV === "production"; const cookieValue = `session_token=${token}; HttpOnly; Path=/; SameSite=Strict; Max-Age=86400${isProduction ? "; Secure" : ""}`; - (ctx.reply as any).header?.("Set-Cookie", cookieValue); - - // For graphql-yoga response - if ((ctx as any).serverResponse) { - (ctx as any).serverResponse.setHeader("Set-Cookie", cookieValue); - } + (ctx.reply as ReplyLike).header?.("Set-Cookie", cookieValue); return user[0]; }, @@ -76,7 +75,7 @@ builder.mutationField("logout", (t) => // Clear cookie const isProduction = process.env.NODE_ENV === "production"; const cookieValue = `session_token=; HttpOnly; Path=/; SameSite=Strict; Max-Age=0${isProduction ? "; Secure" : ""}`; - (ctx.reply as any).header?.("Set-Cookie", cookieValue); + (ctx.reply as ReplyLike).header?.("Set-Cookie", cookieValue); return true; }, }), diff --git a/packages/backend/src/graphql/resolvers/comments.ts b/packages/backend/src/graphql/resolvers/comments.ts index 702db70..89f7733 100644 --- a/packages/backend/src/graphql/resolvers/comments.ts +++ b/packages/backend/src/graphql/resolvers/comments.ts @@ -20,7 +20,7 @@ builder.queryField("commentsForVideo", (t) => .orderBy(desc(comments.date_created)); return Promise.all( - commentList.map(async (c: any) => { + commentList.map(async (c) => { const user = await ctx.db .select({ id: users.id, @@ -125,7 +125,7 @@ builder.queryField("adminListComments", (t) => ]); const items = await Promise.all( - commentList.map(async (c: any) => { + commentList.map(async (c) => { const user = await ctx.db .select({ id: users.id, diff --git a/packages/backend/src/graphql/resolvers/gamification.ts b/packages/backend/src/graphql/resolvers/gamification.ts index 810f078..9dad1a4 100644 --- a/packages/backend/src/graphql/resolvers/gamification.ts +++ b/packages/backend/src/graphql/resolvers/gamification.ts @@ -37,7 +37,7 @@ builder.queryField("leaderboard", (t) => .limit(limit) .offset(offset); - return entries.map((e: any, i: number) => ({ ...e, rank: offset + i + 1 })); + return entries.map((e, i) => ({ ...e, rank: offset + i + 1 })); }, }), ); @@ -101,7 +101,7 @@ builder.queryField("userGamification", (t) => return { stats: stats[0] ? { ...stats[0], rank } : null, - achievements: userAchievements.map((a: any) => ({ + achievements: userAchievements.map((a) => ({ ...a, date_unlocked: a.date_unlocked!, })), diff --git a/packages/backend/src/graphql/resolvers/models.ts b/packages/backend/src/graphql/resolvers/models.ts index 9b4d56d..56ac49f 100644 --- a/packages/backend/src/graphql/resolvers/models.ts +++ b/packages/backend/src/graphql/resolvers/models.ts @@ -1,9 +1,10 @@ import { builder } from "../builder"; import { ModelType, ModelListType } from "../types/index"; import { users, user_photos, files } from "../../db/schema/index"; -import { eq, and, desc, asc, ilike, count, arrayContains } from "drizzle-orm"; +import { eq, and, desc, asc, ilike, count, arrayContains, type SQL } from "drizzle-orm"; +import type { DB } from "../../db/connection"; -async function enrichModel(db: any, user: any) { +async function enrichModel(db: DB, user: typeof users.$inferSelect) { // Fetch photos const photoRows = await db .select({ id: files.id, filename: files.filename }) @@ -14,8 +15,8 @@ async function enrichModel(db: any, user: any) { const seen = new Set(); const photos = photoRows - .filter((p: any) => p.id && !seen.has(p.id) && seen.add(p.id)) - .map((p: any) => ({ id: p.id, filename: p.filename })); + .filter((p) => p.id && !seen.has(p.id) && seen.add(p.id)) + .map((p) => ({ id: p.id, filename: p.filename })); return { ...user, photos }; } @@ -35,7 +36,7 @@ builder.queryField("models", (t) => const pageSize = args.limit ?? 24; const offset = args.offset ?? 0; - const conditions: any[] = [eq(users.role, "model")]; + const conditions: SQL[] = [eq(users.role, "model")]; if (args.search) conditions.push(ilike(users.artist_name, `%${args.search}%`)); if (args.tag) conditions.push(arrayContains(users.tags, [args.tag])); @@ -46,7 +47,7 @@ builder.queryField("models", (t) => ctx.db.select().from(users).where(where).orderBy(order).limit(pageSize).offset(offset), ctx.db.select({ total: count() }).from(users).where(where), ]); - const items = await Promise.all(modelList.map((m: any) => enrichModel(ctx.db, m))); + const items = await Promise.all(modelList.map((m) => enrichModel(ctx.db, m))); return { items, total: totalRows[0]?.total ?? 0 }; }, }), diff --git a/packages/backend/src/graphql/resolvers/recordings.ts b/packages/backend/src/graphql/resolvers/recordings.ts index d942cbe..deeeb0f 100644 --- a/packages/backend/src/graphql/resolvers/recordings.ts +++ b/packages/backend/src/graphql/resolvers/recordings.ts @@ -2,7 +2,7 @@ import { GraphQLError } from "graphql"; import { builder } from "../builder"; import { RecordingType, AdminRecordingListType } from "../types/index"; import { recordings, recording_plays } from "../../db/schema/index"; -import { eq, and, desc, ilike, count } from "drizzle-orm"; +import { eq, and, desc, ilike, count, type SQL } from "drizzle-orm"; import { slugify } from "../../lib/slugify"; import { awardPoints, checkAchievements } from "../../lib/gamification"; import { requireAdmin } from "../../lib/acl"; @@ -21,7 +21,7 @@ builder.queryField("recordings", (t) => if (!ctx.currentUser) throw new GraphQLError("Unauthorized"); const conditions = [eq(recordings.user_id, ctx.currentUser.id)]; - if (args.status) conditions.push(eq(recordings.status, args.status as any)); + if (args.status) conditions.push(eq(recordings.status, args.status as "draft" | "published")); if (args.linkedVideoId) conditions.push(eq(recordings.linked_video, args.linkedVideoId)); const limit = args.limit || 50; @@ -115,7 +115,7 @@ builder.mutationField("createRecording", (t) => user_id: ctx.currentUser.id, tags: args.tags || [], linked_video: args.linkedVideoId || null, - status: (args.status as any) || "draft", + status: (args.status as "draft" | "published") || "draft", public: false, }) .returning(); @@ -174,7 +174,7 @@ builder.mutationField("updateRecording", (t) => const updated = await ctx.db .update(recordings) - .set(updates as any) + .set(updates as Partial) .where(eq(recordings.id, args.id)) .returning(); @@ -355,9 +355,9 @@ builder.queryField("adminListRecordings", (t) => const limit = args.limit ?? 50; const offset = args.offset ?? 0; - const conditions: any[] = []; + const conditions: SQL[] = []; if (args.search) conditions.push(ilike(recordings.title, `%${args.search}%`)); - if (args.status) conditions.push(eq(recordings.status, args.status as any)); + if (args.status) conditions.push(eq(recordings.status, args.status as "draft" | "published")); const where = conditions.length > 0 ? and(...conditions) : undefined; const [rows, totalRows] = await Promise.all([ diff --git a/packages/backend/src/graphql/resolvers/users.ts b/packages/backend/src/graphql/resolvers/users.ts index a1e2c9e..a72baf1 100644 --- a/packages/backend/src/graphql/resolvers/users.ts +++ b/packages/backend/src/graphql/resolvers/users.ts @@ -2,7 +2,7 @@ import { GraphQLError } from "graphql"; import { builder } from "../builder"; import { CurrentUserType, UserType, AdminUserListType, AdminUserDetailType } from "../types/index"; import { users, user_photos, files } from "../../db/schema/index"; -import { eq, ilike, or, count, and } from "drizzle-orm"; +import { eq, ilike, or, count, and, asc, type SQL } from "drizzle-orm"; import { requireAdmin } from "../../lib/acl"; builder.queryField("me", (t) => @@ -63,7 +63,7 @@ builder.mutationField("updateProfile", (t) => await ctx.db .update(users) - .set(updates as any) + .set(updates as Partial) .where(eq(users.id, ctx.currentUser.id)); const updated = await ctx.db @@ -93,27 +93,27 @@ builder.queryField("adminListUsers", (t) => const limit = args.limit ?? 50; const offset = args.offset ?? 0; - let query = ctx.db.select().from(users); - let countQuery = ctx.db.select({ total: count() }).from(users); - - const conditions: any[] = []; + const conditions: SQL[] = []; if (args.role) { - conditions.push(eq(users.role, args.role as any)); + conditions.push(eq(users.role, args.role as "model" | "viewer" | "admin")); } if (args.search) { const pattern = `%${args.search}%`; - conditions.push(or(ilike(users.email, pattern), ilike(users.artist_name, pattern))); - } - - if (conditions.length > 0) { - const where = conditions.length === 1 ? conditions[0] : and(...conditions); - query = (query as any).where(where); - countQuery = (countQuery as any).where(where); + conditions.push( + or(ilike(users.email, pattern), ilike(users.artist_name, pattern)) as SQL, + ); } + const where = conditions.length > 0 ? and(...conditions) : undefined; const [items, totalRows] = await Promise.all([ - (query as any).limit(limit).offset(offset), - countQuery, + ctx.db + .select() + .from(users) + .where(where) + .orderBy(asc(users.artist_name)) + .limit(limit) + .offset(offset), + ctx.db.select({ total: count() }).from(users).where(where), ]); return { items, total: totalRows[0]?.total ?? 0 }; @@ -139,7 +139,8 @@ builder.mutationField("adminUpdateUser", (t) => requireAdmin(ctx); const updates: Record = { date_updated: new Date() }; - if (args.role !== undefined && args.role !== null) updates.role = args.role as any; + if (args.role !== undefined && args.role !== null) + updates.role = args.role as "model" | "viewer" | "admin"; if (args.isAdmin !== undefined && args.isAdmin !== null) updates.is_admin = args.isAdmin; if (args.firstName !== undefined && args.firstName !== null) updates.first_name = args.firstName; @@ -151,7 +152,7 @@ builder.mutationField("adminUpdateUser", (t) => const updated = await ctx.db .update(users) - .set(updates as any) + .set(updates as Partial) .where(eq(users.id, args.userId)) .returning(); @@ -194,8 +195,8 @@ builder.queryField("adminGetUser", (t) => .orderBy(user_photos.sort); const seen = new Set(); const photos = photoRows - .filter((p: any) => p.id && !seen.has(p.id) && seen.add(p.id)) - .map((p: any) => ({ id: p.id, filename: p.filename })); + .filter((p) => p.id && !seen.has(p.id) && seen.add(p.id)) + .map((p) => ({ id: p.id, filename: p.filename })); return { ...user[0], photos }; }, }), diff --git a/packages/backend/src/graphql/resolvers/videos.ts b/packages/backend/src/graphql/resolvers/videos.ts index 22b88fc..453857d 100644 --- a/packages/backend/src/graphql/resolvers/videos.ts +++ b/packages/backend/src/graphql/resolvers/videos.ts @@ -28,10 +28,12 @@ import { lt, gte, arrayContains, + type SQL, } from "drizzle-orm"; import { requireAdmin } from "../../lib/acl"; +import type { DB } from "../../db/connection"; -async function enrichVideo(db: any, video: any) { +async function enrichVideo(db: DB, video: typeof videos.$inferSelect) { // Fetch models const modelRows = await db .select({ @@ -87,7 +89,7 @@ builder.queryField("videos", (t) => const pageSize = args.limit ?? 24; const offset = args.offset ?? 0; - const conditions: any[] = [lte(videos.upload_date, new Date())]; + const conditions: SQL[] = [lte(videos.upload_date, new Date())]; if (!ctx.currentUser) conditions.push(eq(videos.premium, false)); if (args.featured !== null && args.featured !== undefined) { conditions.push(eq(videos.featured, args.featured)); @@ -107,7 +109,7 @@ builder.queryField("videos", (t) => conditions.push( inArray( videos.id, - videoIds.map((v: any) => v.video_id), + videoIds.map((v) => v.video_id), ), ); } @@ -148,8 +150,8 @@ builder.queryField("videos", (t) => .leftJoin(files, eq(videos.movie, files.id)) .where(fullWhere), ]); - const videoList = rows.map((r: any) => r.v || r); - const items = await Promise.all(videoList.map((v: any) => enrichVideo(ctx.db, v))); + const videoList = rows.map((r) => r.v); + const items = await Promise.all(videoList.map((v) => enrichVideo(ctx.db, v))); return { items, total: totalRows[0]?.total ?? 0 }; } @@ -157,7 +159,7 @@ builder.queryField("videos", (t) => ctx.db.select().from(videos).where(where).orderBy(order).limit(pageSize).offset(offset), ctx.db.select({ total: count() }).from(videos).where(where), ]); - const items = await Promise.all(rows.map((v: any) => enrichVideo(ctx.db, v))); + const items = await Promise.all(rows.map((v) => enrichVideo(ctx.db, v))); return { items, total: totalRows[0]?.total ?? 0 }; }, }), @@ -421,7 +423,7 @@ builder.queryField("analytics", (t) => }; } - const videoIds = modelVideoIds.map((v: any) => v.video_id); + const videoIds = modelVideoIds.map((v) => v.video_id); const videoList = await ctx.db.select().from(videos).where(inArray(videos.id, videoIds)); const plays = await ctx.db .select() @@ -435,14 +437,14 @@ builder.queryField("analytics", (t) => const totalLikes = videoList.reduce((sum, v) => sum + (v.likes_count || 0), 0); const totalPlays = videoList.reduce((sum, v) => sum + (v.plays_count || 0), 0); - const playsByDate = plays.reduce((acc: any, play) => { + const playsByDate = plays.reduce((acc: Record, play) => { const date = new Date(play.date_created).toISOString().split("T")[0]; if (!acc[date]) acc[date] = 0; acc[date]++; return acc; }, {}); - const likesByDate = likes.reduce((acc: any, like) => { + const likesByDate = likes.reduce((acc: Record, like) => { const date = new Date(like.date_created).toISOString().split("T")[0]; if (!acc[date]) acc[date] = 0; acc[date]++; @@ -499,7 +501,7 @@ builder.queryField("adminListVideos", (t) => const limit = args.limit ?? 50; const offset = args.offset ?? 0; - const conditions: any[] = []; + const conditions: SQL[] = []; if (args.search) conditions.push(ilike(videos.title, `%${args.search}%`)); if (args.premium !== null && args.premium !== undefined) conditions.push(eq(videos.premium, args.premium)); @@ -517,7 +519,7 @@ builder.queryField("adminListVideos", (t) => .offset(offset), ctx.db.select({ total: count() }).from(videos).where(where), ]); - const items = await Promise.all(rows.map((v: any) => enrichVideo(ctx.db, v))); + const items = await Promise.all(rows.map((v) => enrichVideo(ctx.db, v))); return { items, total: totalRows[0]?.total ?? 0 }; }, }), @@ -590,7 +592,7 @@ builder.mutationField("updateVideo", (t) => const updated = await ctx.db .update(videos) - .set(updates as any) + .set(updates as Partial) .where(eq(videos.id, args.id)) .returning(); if (!updated[0]) return null; diff --git a/packages/backend/src/lib/gamification.ts b/packages/backend/src/lib/gamification.ts index 50649d2..107a64f 100644 --- a/packages/backend/src/lib/gamification.ts +++ b/packages/backend/src/lib/gamification.ts @@ -47,7 +47,7 @@ export async function calculateWeightedScore(db: DB, userId: string): Promise { @@ -84,7 +84,7 @@ export async function updateUserStats(db: DB, userId: string): Promise { sql`, `, )}) `); - playbacksCount = parseInt((playbacksResult.rows[0] as any)?.count || "0"); + playbacksCount = parseInt((playbacksResult.rows[0] as { count?: string })?.count || "0"); } else { const playbacksResult = await db .select({ count: count() }) @@ -242,7 +242,7 @@ async function getAchievementProgress( WHERE rp.user_id = ${userId} AND r.user_id != ${userId} `); - return parseInt((result.rows[0] as any)?.count || "0"); + return parseInt((result.rows[0] as { count?: string })?.count || "0"); } if (["completionist_10", "completionist_100"].includes(code)) { @@ -293,7 +293,7 @@ async function getAchievementProgress( WHERE rp.user_id = ${userId} AND r.user_id != ${userId} `); const rc = recordingsResult[0]?.count || 0; - const pc = parseInt((playsResult.rows[0] as any)?.count || "0"); + const pc = parseInt((playsResult.rows[0] as { count?: string })?.count || "0"); return rc >= 50 && pc >= 100 ? 1 : 0; } diff --git a/packages/frontend/src/lib/components/header/header.svelte b/packages/frontend/src/lib/components/header/header.svelte index 657e9ed..d5969cb 100644 --- a/packages/frontend/src/lib/components/header/header.svelte +++ b/packages/frontend/src/lib/components/header/header.svelte @@ -38,7 +38,7 @@ isMobileMenuOpen = false; } - function isActiveLink(link: any) { + function isActiveLink(link: { name: string; href: string }) { return ( (page.url.pathname === "/" && link === navLinks[0]) || (page.url.pathname.startsWith(link.href) && link !== navLinks[0]) diff --git a/packages/frontend/src/lib/components/page-hero/page-hero.svelte b/packages/frontend/src/lib/components/page-hero/page-hero.svelte index 9127da0..b3264c3 100644 --- a/packages/frontend/src/lib/components/page-hero/page-hero.svelte +++ b/packages/frontend/src/lib/components/page-hero/page-hero.svelte @@ -20,7 +20,9 @@ {title} {#if description} -

+

{description}

{/if} diff --git a/packages/frontend/src/lib/components/recording-card/recording-card.svelte b/packages/frontend/src/lib/components/recording-card/recording-card.svelte index ccd8108..e4aa691 100644 --- a/packages/frontend/src/lib/components/recording-card/recording-card.svelte +++ b/packages/frontend/src/lib/components/recording-card/recording-card.svelte @@ -26,7 +26,7 @@ return "text-green-400 bg-green-400/20"; case "draft": return "text-yellow-400 bg-yellow-400/20"; -default: + default: return "text-gray-400 bg-gray-400/20"; } } diff --git a/packages/frontend/src/lib/services.ts b/packages/frontend/src/lib/services.ts index dfbc7a5..dbbcb24 100644 --- a/packages/frontend/src/lib/services.ts +++ b/packages/frontend/src/lib/services.ts @@ -1245,7 +1245,9 @@ export async function adminGetUser(userId: string, token?: string) { "adminGetUser", async () => { const client = token ? getAuthClient(token) : getGraphQLClient(); - const data = await client.request<{ adminGetUser: any }>(ADMIN_GET_USER_QUERY, { userId }); + const data = await client.request<{ + adminGetUser: User & { photos: Array<{ id: string; filename: string }> }; + }>(ADMIN_GET_USER_QUERY, { userId }); return data.adminGetUser; }, { userId }, diff --git a/packages/frontend/src/routes/+page.svelte b/packages/frontend/src/routes/+page.svelte index 2651243..b959c9a 100644 --- a/packages/frontend/src/routes/+page.svelte +++ b/packages/frontend/src/routes/+page.svelte @@ -75,7 +75,9 @@ class="w-24 h-24 rounded-full mx-auto object-cover ring-4 ring-primary/20 group-hover:ring-primary/40 transition-all bg-muted" /> -

{model.artist_name}

+

+ {model.artist_name} +

@@ -110,7 +112,9 @@ class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent group-hover:scale-105 transition-transform duration-300" >
- {#if video.movie_file?.duration}{formatVideoDuration(video.movie_file.duration)}{/if} + {#if video.movie_file?.duration}{formatVideoDuration( + video.movie_file.duration, + )}{/if}
}; message?: string }; + const raw = e.response?.errors?.[0]?.message ?? e.message; error = raw === "Invalid credentials" ? $_("auth.login.error_invalid_credentials") : raw; isError = true; } finally { diff --git a/packages/frontend/src/routes/magazine/+page.svelte b/packages/frontend/src/routes/magazine/+page.svelte index 89f380b..bb25b52 100644 --- a/packages/frontend/src/routes/magazine/+page.svelte +++ b/packages/frontend/src/routes/magazine/+page.svelte @@ -315,8 +315,8 @@ size="sm" disabled={data.page <= 1} onclick={() => goToPage(data.page - 1)} - class="border-primary/20 hover:bg-primary/10" - >{$_("common.previous")} + class="border-primary/20 hover:bg-primary/10">{$_("common.previous")} {#each pageNumbers() as p, i (i)} {#if p === -1} @@ -327,8 +327,8 @@ onclick={() => goToPage(p)} class={p === data.page ? "bg-gradient-to-r from-primary to-accent min-w-9" - : "border-primary/20 hover:bg-primary/10 min-w-9"} - >{p} + : "border-primary/20 hover:bg-primary/10 min-w-9"}>{p} {/if} {/each} + class="border-primary/20 hover:bg-primary/10">{$_("common.next")}

{$_("common.total_results", { values: { total: data.total } })} diff --git a/packages/frontend/src/routes/magazine/[slug]/+page.svelte b/packages/frontend/src/routes/magazine/[slug]/+page.svelte index 68c800a..e382fd2 100644 --- a/packages/frontend/src/routes/magazine/[slug]/+page.svelte +++ b/packages/frontend/src/routes/magazine/[slug]/+page.svelte @@ -153,10 +153,15 @@

About {author.artist_name}

{#if author.description} -

{author.description}

+

+ {author.description} +

{/if} {#if author.slug} - + View profile diff --git a/packages/frontend/src/routes/me/+page.svelte b/packages/frontend/src/routes/me/+page.svelte index 20a0f40..c2bf66e 100644 --- a/packages/frontend/src/routes/me/+page.svelte +++ b/packages/frontend/src/routes/me/+page.svelte @@ -90,8 +90,9 @@ }); toast.success($_("me.settings.toast_update")); invalidateAll(); - } catch (err: any) { - profileError = err.response?.errors?.[0]?.message ?? err.message; + } catch (err) { + const e = err as { response?: { errors?: Array<{ message: string }> }; message?: string }; + profileError = e.response?.errors?.[0]?.message ?? e.message ?? "Unknown error"; isProfileError = true; } finally { isProfileLoading = false; @@ -114,8 +115,9 @@ toast.success($_("me.settings.toast_update")); invalidateAll(); password = confirmPassword = ""; - } catch (err: any) { - securityError = err.response?.errors?.[0]?.message ?? err.message; + } catch (err) { + const e = err as { response?: { errors?: Array<{ message: string }> }; message?: string }; + securityError = e.response?.errors?.[0]?.message ?? e.message ?? "Unknown error"; isSecurityError = true; } finally { isSecurityLoading = false; @@ -290,9 +292,7 @@
-

- JPG, PNG · max 2 MB -

+

JPG, PNG · max 2 MB

Click or drop to {avatar ? "change" : "upload"}

diff --git a/packages/frontend/src/routes/models/+page.svelte b/packages/frontend/src/routes/models/+page.svelte index d3c9117..7d5333c 100644 --- a/packages/frontend/src/routes/models/+page.svelte +++ b/packages/frontend/src/routes/models/+page.svelte @@ -70,34 +70,34 @@
- -
- - { - searchValue = (e.target as HTMLInputElement).value; - debounceSearch(searchValue); - }} - class="pl-10 bg-background/50 border-primary/20 focus:border-primary" - /> -
+ +
+ + { + searchValue = (e.target as HTMLInputElement).value; + debounceSearch(searchValue); + }} + class="pl-10 bg-background/50 border-primary/20 focus:border-primary" + /> +
- - + +
@@ -105,18 +105,18 @@
{#each data.items as model (model.slug)} - -
- {model.artist_name} + +
+ {model.artist_name} - - + - - + - - - -
-
-

- {model.artist_name} -

- +
-
- -
+ +
+ {#each model.tags as tag (tag)} + + {tag} + + {/each} +
- -
- - -
- - - + +
+ + +
+ + {/each}
@@ -205,8 +204,8 @@ size="sm" disabled={data.page <= 1} onclick={() => goToPage(data.page - 1)} - class="border-primary/20 hover:bg-primary/10" - >{$_("common.previous")} + class="border-primary/20 hover:bg-primary/10">{$_("common.previous")} {#each pageNumbers() as p, i (i)} {#if p === -1} @@ -217,8 +216,8 @@ onclick={() => goToPage(p)} class={p === data.page ? "bg-gradient-to-r from-primary to-accent min-w-9" - : "border-primary/20 hover:bg-primary/10 min-w-9"} - >{p} + : "border-primary/20 hover:bg-primary/10 min-w-9"}>{p} {/if} {/each} + class="border-primary/20 hover:bg-primary/10">{$_("common.next")}

{$_("common.total_results", { values: { total: data.total } })} diff --git a/packages/frontend/src/routes/password/+page.svelte b/packages/frontend/src/routes/password/+page.svelte index 8dfdcc9..a70e870 100644 --- a/packages/frontend/src/routes/password/+page.svelte +++ b/packages/frontend/src/routes/password/+page.svelte @@ -30,8 +30,9 @@ await requestPassword(email); toast.success($_("auth.password_request.toast_request", { values: { email } })); goto("/login"); - } catch (err: any) { - error = err.response?.errors?.[0]?.message ?? err.message; + } catch (err) { + const e = err as { response?: { errors?: Array<{ message: string }> }; message?: string }; + error = e.response?.errors?.[0]?.message ?? e.message ?? "Unknown error"; isError = true; } finally { isLoading = false; diff --git a/packages/frontend/src/routes/password/reset/+page.svelte b/packages/frontend/src/routes/password/reset/+page.svelte index 25407d4..f344503 100644 --- a/packages/frontend/src/routes/password/reset/+page.svelte +++ b/packages/frontend/src/routes/password/reset/+page.svelte @@ -39,8 +39,9 @@ await resetPassword(data.token, password); toast.success($_("auth.password_reset.toast_reset")); goto("/login"); - } catch (err: any) { - const raw = err.response?.errors?.[0]?.message ?? err.message; + } catch (err) { + const e = err as { response?: { errors?: Array<{ message: string }> }; message?: string }; + const raw = e.response?.errors?.[0]?.message ?? e.message; const tokenErrors = ["Invalid or expired reset token", "Reset token expired"]; error = tokenErrors.includes(raw) ? $_("auth.password_reset.error_invalid_token") : raw; isError = true; diff --git a/packages/frontend/src/routes/signup/+page.svelte b/packages/frontend/src/routes/signup/+page.svelte index a9a6e6a..01e6063 100644 --- a/packages/frontend/src/routes/signup/+page.svelte +++ b/packages/frontend/src/routes/signup/+page.svelte @@ -47,8 +47,9 @@ await register(email, password, firstName, lastName); toast.success($_("auth.signup.toast_register", { values: { email } })); goto("/login"); - } catch (err: any) { - const raw = err.response?.errors?.[0]?.message ?? err.message; + } catch (err) { + const e = err as { response?: { errors?: Array<{ message: string }> }; message?: string }; + const raw = e.response?.errors?.[0]?.message ?? e.message; error = raw === "Email already registered" ? $_("auth.signup.error_email_taken") : raw; isError = true; } finally { diff --git a/packages/frontend/src/routes/tags/[tag]/+page.svelte b/packages/frontend/src/routes/tags/[tag]/+page.svelte index 73f5e69..8997409 100644 --- a/packages/frontend/src/routes/tags/[tag]/+page.svelte +++ b/packages/frontend/src/routes/tags/[tag]/+page.svelte @@ -27,7 +27,7 @@ const filteredItems = $derived(() => { return data.items - .filter((item: any) => { + .filter((item) => { const matchesSearch = searchQuery === "" || item.title.toLowerCase().includes(searchQuery.toLowerCase()); const matchesCategory = categoryFilter === "all" || item.category === categoryFilter; @@ -72,7 +72,9 @@ />

{ - searchValue = (e.target as HTMLInputElement).value; - debounceSearch(searchValue); - }} - class="pl-10 bg-background/50 border-primary/20 focus:border-primary" - /> - + +
+ + { + searchValue = (e.target as HTMLInputElement).value; + debounceSearch(searchValue); + }} + class="pl-10 bg-background/50 border-primary/20 focus:border-primary" + /> +
- - + + - - + + @@ -142,61 +142,63 @@ @@ -263,8 +264,8 @@ size="sm" disabled={data.page <= 1} onclick={() => goToPage(data.page - 1)} - class="border-primary/20 hover:bg-primary/10" - >{$_("common.previous")} + class="border-primary/20 hover:bg-primary/10">{$_("common.previous")} {#each pageNumbers() as p, i (i)} {#if p === -1} @@ -275,8 +276,8 @@ onclick={() => goToPage(p)} class={p === data.page ? "bg-gradient-to-r from-primary to-accent min-w-9" - : "border-primary/20 hover:bg-primary/10 min-w-9"} - >{p} + : "border-primary/20 hover:bg-primary/10 min-w-9"}>{p} {/if} {/each} + class="border-primary/20 hover:bg-primary/10">{$_("common.next")}

{$_("common.total_results", { values: { total: data.total } })} diff --git a/packages/frontend/src/routes/videos/[slug]/+page.svelte b/packages/frontend/src/routes/videos/[slug]/+page.svelte index 3f48654..9ea4952 100644 --- a/packages/frontend/src/routes/videos/[slug]/+page.svelte +++ b/packages/frontend/src/routes/videos/[slug]/+page.svelte @@ -58,8 +58,8 @@ isLiked = true; toast.success("Added to liked videos"); } - } catch (error: any) { - toast.error(error.message || "Failed to update like"); + } catch (error) { + toast.error((error instanceof Error ? error.message : null) || "Failed to update like"); } finally { isLikeLoading = false; } @@ -86,8 +86,8 @@ invalidateAll(); newComment = ""; showComments = true; - } catch (err: any) { - commentError = err.message; + } catch (err) { + commentError = err instanceof Error ? err.message : "Unknown error"; isCommentError = true; } finally { isCommentLoading = false;