Skip to content

Commit 4ac3608

Browse files
committed
refactor: enhance type safety and improve API response handling in GitHub integration
1 parent 5574de7 commit 4ac3608

File tree

6 files changed

+97
-36
lines changed

6 files changed

+97
-36
lines changed

src/app/dashboard/page.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,37 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
1414

1515
import { useRequireAuth } from '@/hooks/useAuth'
1616
import { Target, MessageSquare, Clock, Zap, Search, ExternalLink, Sparkles } from "lucide-react"
17-
import { SearchModal } from '@/components/search/SearchModal'
1817
import { useSearchStore, useActionItemsStore } from '@/stores'
1918
import { ThemeToggle } from '@/components/theme/ThemeToggle'
2019
import RefreshButton from "@/components/Refresh/RefreshButton";
2120

21+
interface ActionItemInput {
22+
id: string | number;
23+
title: string;
24+
repo?: string;
25+
url?: string;
26+
labels?: string[];
27+
createdAt?: string;
28+
updatedAt?: string;
29+
difficulty?: 'easy' | 'medium';
30+
language?: string;
31+
author?: string;
32+
priority?: string;
33+
}
34+
2235
const VALID_PRIORITIES = ['urgent', 'high', 'medium', 'low'] as const;
23-
function mapActionItemToGitHubIssue(item: any): GitHubIssue {
36+
type ValidPriority = typeof VALID_PRIORITIES[number];
37+
38+
function isValidPriority(priority: string): priority is ValidPriority {
39+
return VALID_PRIORITIES.includes(priority as ValidPriority);
40+
}
41+
42+
function mapActionItemToGitHubIssue(item: ActionItemInput): GitHubIssue {
2443
if (!item) {
2544
throw new Error('Invalid item provided to mapActionItemToGitHubIssue');
2645
}
2746
return {
28-
id: item.id,
47+
id: typeof item.id === 'string' ? parseInt(item.id, 10) || 0 : item.id,
2948
title: item.title,
3049
repository: item.repo || '',
3150
repositoryUrl: item.repo ? `https://github.com/${item.repo}` : '',
@@ -38,9 +57,10 @@ function mapActionItemToGitHubIssue(item: any): GitHubIssue {
3857
stars: 0,
3958
author: { login: item.author || '', avatar_url: '' },
4059
comments: 0,
41-
state: 'open',
60+
state: 'open' as const,
4261
assignee: null,
43-
priority: VALID_PRIORITIES.includes(item.priority) ? item.priority : 'low',
62+
priority: (item.priority && isValidPriority(item.priority)) ?
63+
item.priority as 'low' | 'medium' | 'high' : 'low',
4464
}
4565
}
4666

src/app/quick-wins/page.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import { useSearchParams, useRouter } from 'next/navigation'
55
import { Layout } from '@/components/layout/Layout'
66

7-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
87
import { Button } from '@/components/ui/button'
98
import { Badge } from '@/components/ui/badge'
109
import { Alert, AlertDescription } from '@/components/ui/alert'
@@ -21,7 +20,7 @@ import {
2120
Wrench,
2221
Search,
2322
AlertTriangle,
24-
Github
23+
2524
} from 'lucide-react'
2625
import { useSearchStore } from '@/stores'
2726
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
@@ -51,9 +50,7 @@ export default function QuickWinsPage() {
5150
refreshGoodIssues,
5251
refreshEasyFixes,
5352
refreshAll,
54-
totalIssues,
5553
needsToken,
56-
hasData
5754
} = useQuickWins()
5855

5956
const handleTabChange = (tab: string) => {

src/components/quick-wins/QuickWinsTable.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// src/components/quick-wins/QuickWinsTable.tsx
21
'use client'
32

43
import { useState, useMemo } from 'react'
@@ -23,7 +22,6 @@ import {
2322
TableRow,
2423
} from '@/components/ui/table'
2524
import { Button } from '@/components/ui/button'
26-
import { Input } from '@/components/ui/input'
2725
import { Badge } from '@/components/ui/badge'
2826
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
2927
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'

src/lib/api/github.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
import { GitHubIssue } from '@/types/quickWins';
22

3+
// GitHub API response types
4+
interface GitHubIssueResponse {
5+
id: number;
6+
title: string;
7+
repository_url: string;
8+
html_url: string;
9+
labels: Array<{
10+
name: string;
11+
color: string;
12+
}>;
13+
created_at: string;
14+
updated_at: string;
15+
user: {
16+
login: string;
17+
avatar_url: string;
18+
};
19+
comments: number;
20+
state: 'open' | 'closed';
21+
assignee?: {
22+
login: string;
23+
avatar_url: string;
24+
} | null;
25+
}
26+
27+
interface GitHubRepoResponse {
28+
full_name: string;
29+
language: string | null;
30+
stargazers_count: number;
31+
}
32+
33+
interface GitHubLabel {
34+
name: string;
35+
color: string;
36+
}
37+
338
const GITHUB_API_URL = 'https://api.github.com';
439

540
const CACHE_DURATION = 1000 * 60 * 60 * 12;
@@ -34,35 +69,35 @@ async function fetchIssuesFromGitHub({
3469
}
3570
const res = await fetch(url, { headers });
3671
if (!res.ok) throw new Error('GitHub API error');
37-
const data = await res.json();
72+
const data: GitHubIssueResponse[] = await res.json();
3873
// Her issue için kendi repository'sinden language ve stars bilgisini çek
39-
const issues = await Promise.all(data.map(async (issue: any) => {
40-
let repoData: any = {};
74+
const issues = await Promise.all(data.map(async (issue: GitHubIssueResponse) => {
75+
let repoData: GitHubRepoResponse = {} as GitHubRepoResponse;
4176
try {
4277
const repoRes = await fetch(issue.repository_url, { headers });
4378
if (repoRes.ok) {
4479
repoData = await repoRes.json();
4580
}
46-
} catch (e) {
47-
repoData = {};
81+
} catch {
82+
repoData = {} as GitHubRepoResponse;
4883
}
4984
return {
5085
id: issue.id,
5186
title: issue.title,
5287
repository: repoData.full_name || `${owner}/${repo}`,
5388
repositoryUrl: issue.repository_url.replace('api.github.com/repos', 'github.com'),
5489
url: issue.html_url,
55-
labels: issue.labels.map((l: any) => ({ name: l.name, color: l.color })),
90+
labels: issue.labels.map((l: GitHubLabel) => ({ name: l.name, color: l.color })),
5691
created_at: issue.created_at,
5792
updated_at: issue.updated_at,
5893
difficulty:
59-
issue.labels.some((l: any) => l.name.toLowerCase().includes('good first issue'))
60-
? 'good'
61-
: issue.labels.some((l: any) => l.name.toLowerCase().includes('easy'))
62-
? 'easy'
63-
: issue.labels.some((l: any) => l.name.toLowerCase().includes('medium'))
64-
? 'medium'
65-
: undefined,
94+
issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('good first issue'))
95+
? 'easy' as const
96+
: issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('easy'))
97+
? 'easy' as const
98+
: issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('medium'))
99+
? 'medium' as const
100+
: 'medium' as const, // Default to medium instead of undefined
66101
language: repoData.language || 'unknown',
67102
stars: repoData.stargazers_count || 0,
68103
author: {
@@ -72,7 +107,7 @@ async function fetchIssuesFromGitHub({
72107
comments: issue.comments,
73108
state: issue.state,
74109
assignee: issue.assignee,
75-
priority: 'medium',
110+
priority: 'medium' as const,
76111
};
77112
}));
78113
issuesCache[cacheKey] = { timestamp: now, data: issues };

src/stores/actionItems.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,37 +234,37 @@ export const useActionItemsStore = create<ActionItemsState>()(
234234
set((state) => ({ loading: { ...state.loading, [t]: true }, errors: { ...state.errors, [t]: null } }))
235235

236236
try {
237-
let items: any[] = []
237+
let items: unknown[] = []
238238

239239
switch (t) {
240240
case 'assigned':
241-
items = await githubAPIClient.getAssignedItems(username) as RawAPIItem[]
242-
get().setAssignedItems(items.map(item => ({ ...item, assignedAt: item.assignedAt || item.createdAt })))
241+
items = await githubAPIClient.getAssignedItems(username)
242+
get().setAssignedItems((items as RawAPIItem[]).map(item => ({ ...item, assignedAt: item.assignedAt || item.createdAt })))
243243
break
244244
case 'mentions':
245245
items = await githubAPIClient.getMentionItems(username)
246-
get().setMentionItems(items.map(item => ({
246+
get().setMentionItems((items as RawAPIItem[]).map(item => ({
247247
...item,
248248
mentionType: item.mentionType || 'mention',
249249
mentionedAt: item.mentionedAt || item.updatedAt
250250
})))
251251
break
252252
case 'stale':
253253
items = await githubAPIClient.getStaleItems(username)
254-
get().setStaleItems(items.map(item => ({
254+
get().setStaleItems((items as RawAPIItem[]).map(item => ({
255255
...item,
256256
lastActivity: item.lastActivity || item.updatedAt,
257257
daysStale: item.daysStale || item.daysOld || 0,
258258
reviewStatus: item.reviewStatus || 'pending'
259259
})))
260260
break
261261
case 'goodFirstIssues':
262-
items = await githubAPIClient.getGoodFirstIssues()
263-
get().setGoodFirstIssues(items)
262+
items = await githubAPIClient.getGoodFirstIssues()
263+
get().setGoodFirstIssues(items as ActionItem[])
264264
break
265265
case 'easyFixes':
266-
items = await githubAPIClient.getEasyFixes()
267-
get().setEasyFixes(items)
266+
items = await githubAPIClient.getEasyFixes()
267+
get().setEasyFixes(items as ActionItem[])
268268
break
269269
}
270270

src/stores/quickWins.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,18 @@ async function fetchIssuesFromPopularRepos(
1414
throw new Error(`GitHub API error: ${repoRes.status}`);
1515
}
1616

17-
const repoData = await repoRes.json();
17+
interface GitHubRepoSearchItem {
18+
name: string;
19+
owner: {
20+
login: string;
21+
};
22+
}
23+
24+
interface GitHubSearchResponse {
25+
items: GitHubRepoSearchItem[];
26+
}
27+
28+
const repoData: GitHubSearchResponse = await repoRes.json();
1829

1930
if (!repoData.items || !Array.isArray(repoData.items)) {
2031
throw new Error('Repo bulunamadı');
@@ -27,7 +38,7 @@ async function fetchIssuesFromPopularRepos(
2738
for (let i = 0; i < repoData.items.length; i += batchSize) {
2839
const batch = repoData.items.slice(i, i + batchSize);
2940
const batchResults = await Promise.all(
30-
batch.map(async (repo: any) => {
41+
batch.map(async (repo: GitHubRepoSearchItem) => {
3142
try {
3243
const issues = await fetchIssuesFromGitHub({
3344
owner: repo.owner.login,

0 commit comments

Comments
 (0)