Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,37 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'

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

interface ActionItemInput {
id: string | number;
title: string;
repo?: string;
url?: string;
labels?: string[];
createdAt?: string;
updatedAt?: string;
difficulty?: 'easy' | 'medium';
language?: string;
author?: string;
priority?: string;
}

const VALID_PRIORITIES = ['urgent', 'high', 'medium', 'low'] as const;
function mapActionItemToGitHubIssue(item: any): GitHubIssue {
type ValidPriority = typeof VALID_PRIORITIES[number];

function isValidPriority(priority: string): priority is ValidPriority {
return VALID_PRIORITIES.includes(priority as ValidPriority);
}

function mapActionItemToGitHubIssue(item: ActionItemInput): GitHubIssue {
if (!item) {
throw new Error('Invalid item provided to mapActionItemToGitHubIssue');
}
return {
id: item.id,
id: typeof item.id === 'string' ? parseInt(item.id, 10) || 0 : item.id,
title: item.title,
repository: item.repo || '',
repositoryUrl: item.repo ? `https://github.com/${item.repo}` : '',
Expand All @@ -38,9 +57,10 @@ function mapActionItemToGitHubIssue(item: any): GitHubIssue {
stars: 0,
author: { login: item.author || '', avatar_url: '' },
comments: 0,
state: 'open',
state: 'open' as const,
assignee: null,
priority: VALID_PRIORITIES.includes(item.priority) ? item.priority : 'low',
priority: (item.priority && isValidPriority(item.priority)) ?
item.priority as 'low' | 'medium' | 'high' : 'low',
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/app/quick-wins/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { useSearchParams, useRouter } from 'next/navigation'
import { Layout } from '@/components/layout/Layout'

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Alert, AlertDescription } from '@/components/ui/alert'
Expand All @@ -21,7 +20,7 @@ import {
Wrench,
Search,
AlertTriangle,
Github

} from 'lucide-react'
import { useSearchStore } from '@/stores'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
Expand Down Expand Up @@ -51,9 +50,7 @@ export default function QuickWinsPage() {
refreshGoodIssues,
refreshEasyFixes,
refreshAll,
totalIssues,
needsToken,
hasData
} = useQuickWins()

const handleTabChange = (tab: string) => {
Expand Down
2 changes: 0 additions & 2 deletions src/components/quick-wins/QuickWinsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// src/components/quick-wins/QuickWinsTable.tsx
'use client'

import { useState, useMemo } from 'react'
Expand All @@ -23,7 +22,6 @@ import {
TableRow,
} from '@/components/ui/table'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
Expand Down
63 changes: 49 additions & 14 deletions src/lib/api/github.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
import { GitHubIssue } from '@/types/quickWins';

// GitHub API response types
interface GitHubIssueResponse {
id: number;
title: string;
repository_url: string;
html_url: string;
labels: Array<{
name: string;
color: string;
}>;
created_at: string;
updated_at: string;
user: {
login: string;
avatar_url: string;
};
comments: number;
state: 'open' | 'closed';
assignee?: {
login: string;
avatar_url: string;
} | null;
}

interface GitHubRepoResponse {
full_name: string;
language: string | null;
stargazers_count: number;
}

interface GitHubLabel {
name: string;
color: string;
}

const GITHUB_API_URL = 'https://api.github.com';

const CACHE_DURATION = 1000 * 60 * 60 * 12;
Expand Down Expand Up @@ -34,35 +69,35 @@ async function fetchIssuesFromGitHub({
}
const res = await fetch(url, { headers });
if (!res.ok) throw new Error('GitHub API error');
const data = await res.json();
const data: GitHubIssueResponse[] = await res.json();
// Her issue için kendi repository'sinden language ve stars bilgisini çek
const issues = await Promise.all(data.map(async (issue: any) => {
let repoData: any = {};
const issues = await Promise.all(data.map(async (issue: GitHubIssueResponse) => {
let repoData: GitHubRepoResponse = {} as GitHubRepoResponse;
try {
const repoRes = await fetch(issue.repository_url, { headers });
if (repoRes.ok) {
repoData = await repoRes.json();
}
} catch (e) {
repoData = {};
} catch {
repoData = {} as GitHubRepoResponse;
}
return {
id: issue.id,
title: issue.title,
repository: repoData.full_name || `${owner}/${repo}`,
repositoryUrl: issue.repository_url.replace('api.github.com/repos', 'github.com'),
url: issue.html_url,
labels: issue.labels.map((l: any) => ({ name: l.name, color: l.color })),
labels: issue.labels.map((l: GitHubLabel) => ({ name: l.name, color: l.color })),
created_at: issue.created_at,
updated_at: issue.updated_at,
difficulty:
issue.labels.some((l: any) => l.name.toLowerCase().includes('good first issue'))
? 'good'
: issue.labels.some((l: any) => l.name.toLowerCase().includes('easy'))
? 'easy'
: issue.labels.some((l: any) => l.name.toLowerCase().includes('medium'))
? 'medium'
: undefined,
issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('good first issue'))
? 'easy' as const
: issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('easy'))
? 'easy' as const
: issue.labels.some((l: GitHubLabel) => l.name.toLowerCase().includes('medium'))
? 'medium' as const
: 'medium' as const, // Default to medium instead of undefined
language: repoData.language || 'unknown',
stars: repoData.stargazers_count || 0,
author: {
Expand All @@ -72,7 +107,7 @@ async function fetchIssuesFromGitHub({
comments: issue.comments,
state: issue.state,
assignee: issue.assignee,
priority: 'medium',
priority: 'medium' as const,
};
}));
issuesCache[cacheKey] = { timestamp: now, data: issues };
Expand Down
18 changes: 9 additions & 9 deletions src/stores/actionItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,37 +234,37 @@ export const useActionItemsStore = create<ActionItemsState>()(
set((state) => ({ loading: { ...state.loading, [t]: true }, errors: { ...state.errors, [t]: null } }))

try {
let items: any[] = []
let items: unknown[] = []

switch (t) {
case 'assigned':
items = await githubAPIClient.getAssignedItems(username) as RawAPIItem[]
get().setAssignedItems(items.map(item => ({ ...item, assignedAt: item.assignedAt || item.createdAt })))
items = await githubAPIClient.getAssignedItems(username)
get().setAssignedItems((items as RawAPIItem[]).map(item => ({ ...item, assignedAt: item.assignedAt || item.createdAt })))
break
case 'mentions':
items = await githubAPIClient.getMentionItems(username)
get().setMentionItems(items.map(item => ({
get().setMentionItems((items as RawAPIItem[]).map(item => ({
...item,
mentionType: item.mentionType || 'mention',
mentionedAt: item.mentionedAt || item.updatedAt
})))
break
case 'stale':
items = await githubAPIClient.getStaleItems(username)
get().setStaleItems(items.map(item => ({
get().setStaleItems((items as RawAPIItem[]).map(item => ({
...item,
lastActivity: item.lastActivity || item.updatedAt,
daysStale: item.daysStale || item.daysOld || 0,
reviewStatus: item.reviewStatus || 'pending'
})))
break
case 'goodFirstIssues':
items = await githubAPIClient.getGoodFirstIssues()
get().setGoodFirstIssues(items)
items = await githubAPIClient.getGoodFirstIssues()
get().setGoodFirstIssues(items as ActionItem[])
break
case 'easyFixes':
items = await githubAPIClient.getEasyFixes()
get().setEasyFixes(items)
items = await githubAPIClient.getEasyFixes()
get().setEasyFixes(items as ActionItem[])
break
}

Expand Down
15 changes: 13 additions & 2 deletions src/stores/quickWins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@ async function fetchIssuesFromPopularRepos(
throw new Error(`GitHub API error: ${repoRes.status}`);
}

const repoData = await repoRes.json();
interface GitHubRepoSearchItem {
name: string;
owner: {
login: string;
};
}

interface GitHubSearchResponse {
items: GitHubRepoSearchItem[];
}

const repoData: GitHubSearchResponse = await repoRes.json();

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