Skip to content

Commit d3d5d49

Browse files
committed
refactor: improve code quality and naming consistency
- Make limit parameters required (no defaults) in database functions - Rename misleading variables: allProjects → featuredProjects - Rename ambiguous parameters: id → articleId/memberId/projectId - Rename abbreviations: msg → errorMessage in migration files - Add shared constants for magic numbers (HOME_FEATURED_*, DB_*_LIMIT) - Use Promise.all for parallel data fetching on home page - Update all callers to provide explicit limit values
1 parent 043e830 commit d3d5d49

18 files changed

+208
-166
lines changed
Lines changed: 75 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,83 @@
11
<script lang="ts">
2-
import { ArrowRight } from "lucide-svelte";
3-
import type { getPublicProjects } from "$lib/data/public/index.remote";
4-
import { PROJECT_CATEGORIES } from "$lib/shared/models/schema";
2+
import { ArrowRight } from "lucide-svelte";
3+
import type { getPublicProjects } from "$lib/data/public/index.remote";
4+
import { PROJECT_CATEGORIES } from "$lib/shared/models/schema";
55
6-
type PublicProject = Awaited<ReturnType<typeof getPublicProjects>>[number];
6+
type PublicProject = Awaited<ReturnType<typeof getPublicProjects>>[number];
77
8-
type Props = {
9-
projects: PublicProject[];
10-
};
8+
type Props = {
9+
projects: PublicProject[];
10+
};
1111
12-
const { projects }: Props = $props();
12+
const { projects }: Props = $props();
1313
</script>
1414

1515
<section class="bg-zinc-50 py-24">
16-
<div class="mx-auto max-w-6xl px-6">
17-
<div class="mb-20 flex items-end justify-between">
18-
<div>
19-
<div class="mb-4 font-mono text-sm font-medium uppercase tracking-widest text-primary">PROJECTS</div>
20-
<h2 class="text-5xl font-bold text-zinc-900 lg:text-6xl">
21-
つくったもの
22-
</h2>
23-
<p class="mt-4 max-w-2xl text-lg text-zinc-600">
24-
実際に使われるプロダクトを開発し、世に届ける
25-
</p>
26-
</div>
27-
<a
28-
href="/projects"
29-
class="group hidden items-center gap-2 rounded-lg bg-zinc-900 px-6 py-3 font-semibold text-white transition-all hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 sm:flex"
30-
>
31-
すべて見る
32-
<ArrowRight class="h-5 w-5 transition-transform group-hover:translate-x-1" />
33-
</a>
34-
</div>
35-
<div class="grid gap-6 lg:grid-cols-3">
36-
{#each projects as project (project.id)}
37-
<a
38-
href="/projects/{project.slug}"
39-
class="group overflow-hidden rounded-2xl border border-zinc-200 bg-white p-6 transition-all hover:border-primary/30 hover:bg-primary/5 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
40-
>
41-
{#if project.coverUrl}
42-
<div class="mb-4 overflow-hidden rounded-2xl">
43-
<img
44-
src={project.coverUrl}
45-
alt={project.name}
46-
class="aspect-[5/3] w-full object-cover"
47-
loading="lazy"
48-
/>
49-
</div>
50-
{:else}
51-
<div class="mb-4 flex aspect-[5/3] w-full items-center justify-center overflow-hidden rounded-2xl bg-zinc-100">
52-
<span class="font-mono text-sm font-medium text-zinc-400">
53-
No Image
54-
</span>
55-
</div>
56-
{/if}
57-
<h3 class="mb-2 text-xl font-bold text-zinc-900">
58-
{project.name}
59-
</h3>
60-
{#if project.description}
61-
<p class="mb-4 line-clamp-2 text-sm leading-relaxed text-zinc-600">{project.description}</p>
62-
{/if}
63-
<div class="flex flex-wrap gap-2">
64-
<span
65-
class="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-1 font-mono text-xs text-zinc-500"
66-
>
67-
{PROJECT_CATEGORIES[project.category]}
68-
</span>
69-
</div>
70-
</a>
71-
{/each}
72-
</div>
73-
<a
74-
href="/projects"
75-
class="mt-8 flex items-center justify-center gap-2 font-bold text-zinc-600 transition-colors hover:text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 sm:hidden"
76-
>
77-
すべて見る
78-
<ArrowRight class="h-5 w-5" />
79-
</a>
80-
</div>
16+
<div class="mx-auto max-w-6xl px-6">
17+
<div class="mb-20 flex items-end justify-between">
18+
<div>
19+
<div class="mb-4 font-mono text-sm font-medium tracking-widest text-primary uppercase">
20+
PROJECTS
21+
</div>
22+
<h2 class="text-5xl font-bold text-zinc-900 lg:text-6xl">つくったもの</h2>
23+
<p class="mt-4 max-w-2xl text-lg text-zinc-600">
24+
実際に使われるプロダクトを開発し、世に届ける
25+
</p>
26+
</div>
27+
<a
28+
href="/projects"
29+
class="group hidden items-center gap-2 rounded-lg bg-zinc-900 px-6 py-3 font-semibold text-white transition-all hover:bg-zinc-800 focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:outline-none sm:flex"
30+
>
31+
すべて見る
32+
<ArrowRight class="h-5 w-5 transition-transform group-hover:translate-x-1" />
33+
</a>
34+
</div>
35+
<div class="grid gap-6 lg:grid-cols-3">
36+
{#each projects as project (project.id)}
37+
<a
38+
href="/projects/{project.slug}"
39+
class="group overflow-hidden rounded-2xl border border-zinc-200 bg-white p-6 transition-all hover:border-primary/30 hover:bg-primary/5 focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:outline-none"
40+
>
41+
{#if project.coverUrl}
42+
<div class="mb-4 overflow-hidden rounded-2xl">
43+
<img
44+
src={project.coverUrl}
45+
alt={project.name}
46+
class="aspect-[5/3] w-full object-cover"
47+
loading="lazy"
48+
/>
49+
</div>
50+
{:else}
51+
<div
52+
class="mb-4 flex aspect-[5/3] w-full items-center justify-center overflow-hidden rounded-2xl bg-zinc-100"
53+
>
54+
<span class="font-mono text-sm font-medium text-zinc-400"> No Image </span>
55+
</div>
56+
{/if}
57+
<h3 class="mb-2 text-xl font-bold text-zinc-900">
58+
{project.name}
59+
</h3>
60+
{#if project.description}
61+
<p class="mb-4 line-clamp-2 text-sm leading-relaxed text-zinc-600">
62+
{project.description}
63+
</p>
64+
{/if}
65+
<div class="flex flex-wrap gap-2">
66+
<span
67+
class="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-1 font-mono text-xs text-zinc-500"
68+
>
69+
{PROJECT_CATEGORIES[project.category]}
70+
</span>
71+
</div>
72+
</a>
73+
{/each}
74+
</div>
75+
<a
76+
href="/projects"
77+
class="mt-8 flex items-center justify-center gap-2 font-bold text-zinc-600 transition-colors hover:text-primary focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:outline-none sm:hidden"
78+
>
79+
すべて見る
80+
<ArrowRight class="h-5 w-5" />
81+
</a>
82+
</div>
8183
</section>

src/lib/data/private/articles.remote.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import { requireUtCodeMember } from "$lib/server/database/auth.server";
1515
import { getMemberByUserId } from "$lib/server/database/members.server";
1616
import { requireArticleOwnership } from "$lib/server/database/ownership";
1717
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
18+
import { DB_LARGE_LIMIT } from "$lib/shared/constants";
1819

1920
export const getArticles = query(async () => {
2021
await requireUtCodeMember();
21-
return await listAllArticles();
22+
return await listAllArticles(DB_LARGE_LIMIT);
2223
});
2324

2425
export const getArticle = query(v.string(), async (id) => {

src/lib/data/private/members.remote.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import {
1010
} from "$lib/server/database/members.server";
1111
import { requireMemberOwnership } from "$lib/server/database/ownership";
1212
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
13+
import { DB_MEMBERS_LIMIT } from "$lib/shared/constants";
1314

1415
export const getMembers = query(async () => {
1516
await requireUtCodeMember();
16-
return await listMembers();
17+
return await listMembers(DB_MEMBERS_LIMIT);
1718
});
1819

1920
export const getMember = query(v.string(), async (id) => {

src/lib/data/private/projects.remote.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
updateProject,
1616
} from "$lib/server/database/projects.server";
1717
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
18+
import { DB_DEFAULT_LIMIT } from "$lib/shared/constants";
1819
import type { ProjectCategory, ProjectRole } from "$lib/shared/models/schema";
1920

2021
// Re-export getMembers from canonical source
@@ -36,7 +37,7 @@ const roleSchema = v.picklist(PROJECT_ROLE_VALUES);
3637

3738
export const getProjects = query(async () => {
3839
await requireUtCodeMember();
39-
return await listProjects();
40+
return await listProjects(DB_DEFAULT_LIMIT);
4041
});
4142

4243
export const getProject = query(v.string(), async (id) => {

src/lib/data/private/search.remote.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { searchAllArticles } from "$lib/server/database/articles.server";
44
import { requireUtCodeMember } from "$lib/server/database/auth.server";
55
import { searchMembers } from "$lib/server/database/members.server";
66
import { searchProjects } from "$lib/server/database/projects.server";
7+
import { DB_SEARCH_LIMIT } from "$lib/shared/constants";
78
import type { AdminSearchResult } from "$lib/shared/logic/search";
89

910
export const searchAdmin = query(
@@ -12,9 +13,9 @@ export const searchAdmin = query(
1213
await requireUtCodeMember();
1314

1415
const [articles, projects, members] = await Promise.all([
15-
searchAllArticles(searchQuery),
16-
searchProjects(searchQuery),
17-
searchMembers(searchQuery),
16+
searchAllArticles(searchQuery, DB_SEARCH_LIMIT),
17+
searchProjects(searchQuery, DB_SEARCH_LIMIT),
18+
searchMembers(searchQuery, DB_SEARCH_LIMIT),
1819
]);
1920

2021
const articleResults: AdminSearchResult[] = articles.map((article) => ({

src/lib/data/private/stats.remote.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import {
99
import { requireUtCodeMember } from "$lib/server/database/auth.server";
1010
import { countMembers } from "$lib/server/database/members.server";
1111
import { countProjects, getRecentProjects } from "$lib/server/database/projects.server";
12+
import {
13+
DASHBOARD_RECENT_ARTICLES_LIMIT,
14+
DASHBOARD_RECENT_PROJECTS_LIMIT,
15+
DASHBOARD_VIEW_TREND_DAYS,
16+
DB_LARGE_LIMIT,
17+
} from "$lib/shared/constants";
1218

1319
export const getAdminStats = query(async () => {
1420
await requireUtCodeMember();
@@ -28,11 +34,11 @@ export const getAdminStats = query(async () => {
2834
countArticles(),
2935
countPublishedArticles(),
3036
countProjects(),
31-
getRecentArticles(5),
32-
getRecentProjects(3),
33-
listDraftArticles(),
37+
getRecentArticles(DASHBOARD_RECENT_ARTICLES_LIMIT),
38+
getRecentProjects(DASHBOARD_RECENT_PROJECTS_LIMIT),
39+
listDraftArticles(DB_LARGE_LIMIT),
3440
getAnalyticsSummary(),
35-
getRecentViewTrend(14),
41+
getRecentViewTrend(DASHBOARD_VIEW_TREND_DAYS),
3642
]);
3743

3844
return {

src/lib/data/public/index.remote.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import {
1414
listRecentProjects,
1515
searchProjects,
1616
} from "$lib/server/database/projects.server";
17+
import { DB_DEFAULT_LIMIT, DB_MEMBERS_LIMIT, DB_SEARCH_LIMIT } from "$lib/shared/constants";
1718
import type { SearchResult } from "$lib/shared/logic/search";
1819

19-
export const getPublicArticles = query(async () => listPublishedArticles());
20+
export const getPublicArticles = query(async () => listPublishedArticles(DB_DEFAULT_LIMIT));
2021
export const getPublicArticle = query(v.string(), async (slug) => getPublishedArticle(slug));
2122
export const getPublicRelatedArticles = query(
2223
v.object({
@@ -27,24 +28,24 @@ export const getPublicRelatedArticles = query(
2728
async ({ articleId, authorId, limit }) => getRelatedArticles(articleId, authorId, limit),
2829
);
2930

30-
export const getPublicProjects = query(async () => listProjects());
31+
export const getPublicProjects = query(async () => listProjects(DB_DEFAULT_LIMIT));
3132
export const getPublicProject = query(v.string(), async (slug) => getProjectBySlug(slug));
3233

3334
export const getHomeArticles = query(v.number(), async (limit) =>
3435
getRecentPublishedArticles(limit),
3536
);
3637
export const getHomeProjects = query(v.number(), async (limit) => listRecentProjects(limit));
3738

38-
export const getPublicMembers = query(async () => listMembers());
39+
export const getPublicMembers = query(async () => listMembers(DB_MEMBERS_LIMIT));
3940
export const getPublicMember = query(v.string(), async (slug) => getMemberBySlug(slug));
4041

4142
export const searchPublic = query(
4243
v.string(),
4344
async (searchQuery: string): Promise<SearchResult[]> => {
4445
const [articles, projects, members] = await Promise.all([
45-
searchPublishedArticles(searchQuery),
46-
searchProjects(searchQuery),
47-
searchMembers(searchQuery),
46+
searchPublishedArticles(searchQuery, DB_SEARCH_LIMIT),
47+
searchProjects(searchQuery, DB_SEARCH_LIMIT),
48+
searchMembers(searchQuery, DB_SEARCH_LIMIT),
4849
]);
4950

5051
const articleResults: SearchResult[] = articles.map((article) => ({

src/lib/server/database/articles.server.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function addExcerpts<T extends Article>(articles: T[]): (T & { excerpt: string }
2525
return articles.map((a) => ({ ...a, excerpt: generateExcerpt(a.content) }));
2626
}
2727

28-
export async function listPublishedArticles(limit = 100) {
28+
export async function listPublishedArticles(limit: number) {
2929
const articles = await db.query.article.findMany({
3030
where: eq(article.published, true),
3131
orderBy: desc(article.publishedAt),
@@ -66,7 +66,7 @@ export async function getPublishedArticle(slug: string) {
6666
return result ? addExcerpt(result) : null;
6767
}
6868

69-
export async function listAllArticles(limit = 1000) {
69+
export async function listAllArticles(limit: number) {
7070
const articles = await db.query.article.findMany({
7171
orderBy: desc(article.createdAt),
7272
limit,
@@ -75,9 +75,9 @@ export async function listAllArticles(limit = 1000) {
7575
return addExcerpts(articles);
7676
}
7777

78-
export async function getArticleById(id: string) {
78+
export async function getArticleById(articleId: string) {
7979
const result = await db.query.article.findFirst({
80-
where: eq(article.id, id),
80+
where: eq(article.id, articleId),
8181
with: { author: true },
8282
});
8383
return result ? addExcerpt(result) : null;
@@ -89,26 +89,26 @@ export async function createArticle(data: NewArticle) {
8989
return created;
9090
}
9191

92-
export async function updateArticle(id: string, data: Partial<Omit<NewArticle, "id">>) {
92+
export async function updateArticle(articleId: string, data: Partial<Omit<NewArticle, "id">>) {
9393
const [updated] = await db
9494
.update(article)
9595
.set({ ...data, updatedAt: new Date() })
96-
.where(eq(article.id, id))
96+
.where(eq(article.id, articleId))
9797
.returning();
9898
if (!updated) throw new Error("Failed to update article");
9999
return updated;
100100
}
101101

102-
export async function deleteArticle(id: string) {
103-
await db.delete(article).where(eq(article.id, id));
102+
export async function deleteArticle(articleId: string) {
103+
await db.delete(article).where(eq(article.id, articleId));
104104
}
105105

106-
export async function publishArticle(id: string) {
107-
return updateArticle(id, { published: true, publishedAt: new Date() });
106+
export async function publishArticle(articleId: string) {
107+
return updateArticle(articleId, { published: true, publishedAt: new Date() });
108108
}
109109

110-
export async function unpublishArticle(id: string) {
111-
return updateArticle(id, { published: false, publishedAt: null });
110+
export async function unpublishArticle(articleId: string) {
111+
return updateArticle(articleId, { published: false, publishedAt: null });
112112
}
113113

114114
export async function getRelatedArticles(
@@ -152,7 +152,7 @@ export async function getRelatedArticles(
152152
return addExcerpts(sameAuthorArticles);
153153
}
154154

155-
export async function searchPublishedArticles(query: string, limit = 50) {
155+
export async function searchPublishedArticles(query: string, limit: number) {
156156
const searchPattern = createSearchPattern(query);
157157
if (!searchPattern) {
158158
return [];
@@ -170,7 +170,7 @@ export async function searchPublishedArticles(query: string, limit = 50) {
170170
return addExcerpts(articles);
171171
}
172172

173-
export async function searchAllArticles(query: string, limit = 100) {
173+
export async function searchAllArticles(query: string, limit: number) {
174174
const searchPattern = createSearchPattern(query);
175175
if (!searchPattern) {
176176
return [];
@@ -215,7 +215,7 @@ export async function getRecentDraftArticles(limit: number) {
215215
return addExcerpts(articles);
216216
}
217217

218-
export async function listDraftArticles(limit = 1000) {
218+
export async function listDraftArticles(limit: number) {
219219
const articles = await db.query.article.findMany({
220220
where: eq(article.published, false),
221221
orderBy: desc(article.updatedAt),

0 commit comments

Comments
 (0)