-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #108 from kudos-ink/beta
Beta
- Loading branch information
Showing
79 changed files
with
8,012 additions
and
5,184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
type Options = { | ||
tag?: string; | ||
noStoreCache?: boolean; | ||
}; | ||
|
||
export default class APIClient { | ||
private baseURL: string; | ||
|
||
constructor(baseURL: string) { | ||
this.baseURL = baseURL; | ||
} | ||
|
||
private async request<T>(url: string, options: RequestInit): Promise<T> { | ||
const response = await fetch(`${this.baseURL}${url}`, options); | ||
if (!response.ok) { | ||
const error = new Error("HTTP Error") as any; | ||
error.status = response.status; | ||
error.response = await response.json(); | ||
throw error; | ||
} | ||
return (await response.json()) as T; | ||
} | ||
|
||
public get<T>(url: string, options: Options = {}): Promise<T> { | ||
const fetchOptions: RequestInit = { | ||
method: "GET", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
...(options.tag ? { next: { tags: [options.tag] } } : {}), | ||
...(options.noStoreCache ? { cache: "no-store" } : {}), | ||
}; | ||
return this.request<T>(url, fetchOptions); | ||
} | ||
|
||
public post<T, D>(url: string, data: D, options: Options = {}): Promise<T> { | ||
const fetchOptions: RequestInit = { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(data), | ||
...(options.tag ? { next: { tags: [options.tag] } } : {}), | ||
...(options.noStoreCache ? { cache: "no-store" } : {}), | ||
}; | ||
return this.request<T>(url, fetchOptions); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { sanitizeUrl } from "@/utils/url"; | ||
import APIClient from "@/api/client"; | ||
|
||
const CONFIG_API_URL = sanitizeUrl( | ||
process.env.PROJECT_CLASSIFICATION_URL || "", | ||
); | ||
export const configApiClient = new APIClient(CONFIG_API_URL); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ProjectInfos } from "@/types/project"; | ||
import tags from "@/utils/tags"; | ||
import { configApiClient } from "./_client"; | ||
|
||
const PROJECTS_PATH = "/projects"; | ||
|
||
export async function getProjectInfos(slug: string): Promise<ProjectInfos> { | ||
const url = `${PROJECTS_PATH}/${slug}.json`; | ||
const tag = tags.projectInfos(slug); | ||
return configApiClient.get<ProjectInfos>(url, { tag }); | ||
} | ||
|
||
export default { getProjectInfos }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { sanitizeUrl } from "@/utils/url"; | ||
import APIClient from "@/api/client"; | ||
|
||
const CORE_API_URL = sanitizeUrl(process.env.NEXT_PUBLIC_API_URL || ""); | ||
export const coreApiClient = new APIClient(CORE_API_URL); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { GOOD_FIRST_ISSUE_LABELS, KUDOS_ISSUE_LABELS } from "@/data/filters"; | ||
import { | ||
Issue, | ||
IssueDto, | ||
IssueQueryParamsWithPagination, | ||
IssueQueryParamsDto, | ||
} from "@/types/issue"; | ||
import { Project, ProjectDto } from "@/types/project"; | ||
import { Repository, RepositoryDto } from "@/types/repository"; | ||
|
||
export function dtoToIssue(dto: IssueDto): Issue { | ||
return { | ||
id: dto.id, | ||
issueId: dto.issue_id, | ||
isCertified: dto.certified, | ||
labels: dto.labels ?? [], | ||
repository: dtoToRepository(dto.repository), | ||
project: dtoToProject(dto.repository.project), | ||
title: dto.title, | ||
createdAt: dto.issue_created_at, | ||
url: dto.repository.url + `/issues/${dto.issue_id}`, | ||
}; | ||
} | ||
|
||
export function dtoToRepository(dto: RepositoryDto): Repository { | ||
return { | ||
id: dto.id, | ||
slug: dto.slug, | ||
language: dto.language_slug, | ||
// project: dtoToProject(dto.project), | ||
name: dto.name, | ||
url: dto.url, | ||
}; | ||
} | ||
|
||
export function dtoToProject(dto: ProjectDto): Project { | ||
return { | ||
id: dto.id, | ||
name: dto.name, | ||
slug: dto.slug, | ||
avatar: dto.avatar ?? null, | ||
categories: dto.categories ?? [], | ||
purposes: dto.purposes ?? [], | ||
stack_levels: dto.stack_levels ?? [], | ||
technologies: dto.technologies ?? [], | ||
}; | ||
} | ||
|
||
export function issueQueryParamsToDto( | ||
query: IssueQueryParamsWithPagination, | ||
allLanguages: string[], | ||
): IssueQueryParamsDto { | ||
const { technologies = [], labels = [], goodFirst } = query; | ||
|
||
const languageSlugs = technologies.filter((tech) => | ||
allLanguages.includes(tech), | ||
); | ||
const remainingTechnologies = technologies.filter( | ||
(tech) => !allLanguages.includes(tech), | ||
); | ||
|
||
const combinedLabels = goodFirst | ||
? [...labels, ...GOOD_FIRST_ISSUE_LABELS, ...KUDOS_ISSUE_LABELS] | ||
: labels; | ||
|
||
return { | ||
slugs: query.projects, | ||
certified: query.certified, | ||
purposes: query.purposes, | ||
stack_levels: query.stackLevels, | ||
labels: combinedLabels.length > 0 ? combinedLabels : undefined, | ||
open: query.open, | ||
technologies: | ||
remainingTechnologies.length > 0 ? remainingTechnologies : undefined, | ||
language_slugs: languageSlugs.length > 0 ? languageSlugs : undefined, | ||
offset: query.offset, | ||
limit: query.limit, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { DEFAULT_QUERY } from "@/data/fetch"; | ||
import { IssueQueryParams, IssueDto, Issue } from "@/types/issue"; | ||
import { | ||
PaginationQueryParams, | ||
PaginatedCustomResponse, | ||
PaginatedCustomResponseDto, | ||
} from "@/types/pagination"; | ||
import { prepareUrl } from "@/utils/url"; | ||
import { coreApiClient } from "./_client"; | ||
import { dtoToIssue, issueQueryParamsToDto } from "./_transformers"; | ||
import { getAllLanguages } from "./languages"; | ||
|
||
const ISSUES_PATH = "/issues"; | ||
|
||
export async function getIssues( | ||
query: IssueQueryParams & PaginationQueryParams = DEFAULT_QUERY, | ||
): Promise<PaginatedCustomResponse<Issue>> { | ||
let allLanguages: string[] = []; | ||
if (query?.technologies?.length) { | ||
allLanguages = await getAllLanguages(); | ||
} | ||
|
||
const queryDto = issueQueryParamsToDto(query, allLanguages); | ||
const url = prepareUrl(`${ISSUES_PATH}`, queryDto); | ||
const res = | ||
await coreApiClient.get<PaginatedCustomResponseDto<IssueDto>>(url); | ||
|
||
return { | ||
totalCount: res.total_count ?? 0, | ||
hasNextPage: res.has_next_page, | ||
hasPreviousPage: res.has_previous_page, | ||
data: res.data.map(dtoToIssue), | ||
}; | ||
} | ||
|
||
export default { getIssues }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import tags from "@/utils/tags"; | ||
import { coreApiClient } from "./_client"; | ||
|
||
const LANGUAGES_PATH = "/languages"; | ||
|
||
export async function getAllLanguages() { | ||
const response = await coreApiClient.get<string[]>(LANGUAGES_PATH, { | ||
tag: tags.languages, | ||
}); | ||
return response; | ||
} | ||
|
||
export default { getAllLanguages }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { DEFAULT_BIG_PAGE_SIZE, DEFAULT_QUERY } from "@/data/fetch"; | ||
import { | ||
PaginatedCustomResponse, | ||
PaginationQueryParams, | ||
} from "@/types/pagination"; | ||
import { IFilterOption } from "@/types/filters"; | ||
import { Project } from "@/types/project"; | ||
import tags from "@/utils/tags"; | ||
import { prepareUrl } from "@/utils/url"; | ||
import { coreApiClient } from "./_client"; | ||
|
||
type QueryParams = { | ||
slugs?: string[]; | ||
categories?: string[]; | ||
stackLevels?: string[]; | ||
technologies?: string[]; | ||
}; | ||
|
||
const PROJECTS_PATH = "/projects"; | ||
|
||
async function getProjects( | ||
query: QueryParams & PaginationQueryParams = DEFAULT_QUERY, | ||
tag?: string, | ||
) { | ||
const url = prepareUrl(PROJECTS_PATH, query); | ||
const nextTag = tag ?? tags.projects(query.slugs?.join("-") || ""); | ||
|
||
return coreApiClient.get<PaginatedCustomResponse<Project>>(url, { | ||
tag: nextTag, | ||
}); | ||
} | ||
|
||
async function getAllProjectOptions() { | ||
let offset = 0; | ||
let hasMore = true; | ||
let projects: IFilterOption[] = []; | ||
|
||
while (hasMore) { | ||
const paginationParams = { | ||
offset, | ||
limit: DEFAULT_BIG_PAGE_SIZE, | ||
tag: "all-projects", | ||
}; | ||
const response = await getProjects(paginationParams, tags.projectOptions); | ||
|
||
projects = projects.concat( | ||
response.data.map((project) => ({ | ||
value: project.slug, | ||
label: project.name, | ||
})), | ||
); | ||
|
||
hasMore = response.hasNextPage; | ||
offset += DEFAULT_BIG_PAGE_SIZE; | ||
} | ||
|
||
return projects; | ||
} | ||
|
||
export default { getProjects, getAllProjectOptions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { | ||
PaginatedCustomResponse, | ||
PaginationQueryParams, | ||
} from "@/types/pagination"; | ||
import { Repository, RepositoryDto } from "@/types/repository"; | ||
import tags from "@/utils/tags"; | ||
import { prepareUrl } from "@/utils/url"; | ||
import { coreApiClient } from "./_client"; | ||
import { dtoToRepository } from "./_transformers"; | ||
|
||
type QueryParams = { | ||
slugs?: string[]; | ||
names?: string[]; | ||
languageIds?: string[]; | ||
projectIds?: string[]; | ||
}; | ||
|
||
const REPOSITORIES_PATH = "/repositories"; | ||
|
||
async function getRepositories(query: QueryParams & PaginationQueryParams) { | ||
const url = prepareUrl(REPOSITORIES_PATH, query); | ||
const tag = tags.repositories(query.slugs?.join("-") || ""); | ||
return coreApiClient.get<PaginatedCustomResponse<Repository>>(url, { tag }); | ||
} | ||
|
||
async function getRepositoryById(repositoryId: number): Promise<Repository> { | ||
const url = `${REPOSITORIES_PATH}/${repositoryId}`; | ||
const res = await coreApiClient.get<RepositoryDto>(url, { | ||
tag: `repository-${repositoryId}`, | ||
}); | ||
|
||
return dtoToRepository(res); | ||
} | ||
|
||
export default { getRepositories, getRepositoryById }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import NextImage from "next/image"; | ||
import { IconRepo, IconSocial, IconWeb } from "@/assets/icons"; | ||
import Countdown from "@/components/countdown"; | ||
|
||
const EventBanner = () => ( | ||
<article className="rounded-xl p-6 bg-container-1 border-container-stroke-separator relative overflow-hidden h-[675px]"> | ||
<NextImage | ||
className="pointer-events-none absolute -left-[5px] -top-[30px] h-[calc(100%_+_60px)] w-[calc(100%_+_10px)] max-w-[initial] object-cover object-top" | ||
src="/images/kudos-weeks.png" | ||
alt="Event banner" | ||
height={983} | ||
width={1200} | ||
/> | ||
<div className="relative flex flex-col gap-6 w-6/12"> | ||
<div className="flex flex-col gap-1"> | ||
<span className="text-xl text-primary italic"> | ||
{"kudos < > PBA host"} | ||
</span> | ||
<span className="tracking-tight text-6xl font-bold"> | ||
Kudos Carnival | ||
</span> | ||
<span className="text-xl text-default-600"> | ||
November 1, 2024 - December 15, 2024 | ||
</span> | ||
</div> | ||
|
||
<Countdown date="2024-11-01T12:00:00.536328Z" /> | ||
|
||
<h3 className="text-xl text-default-600 mt-4"> | ||
Want to level up your contributions? Make an impact on Polkadot, solve | ||
key issues, and rise up the Kudos leaderboard! | ||
</h3> | ||
<div className="gap-3 flex flex-col sm:flex-row"> | ||
<div className="flex min-w-40 flex-col gap-2 rounded-xl bg-default text-default-foreground p-3"> | ||
<div className="flex items-center gap-1"> | ||
<span className="text-xs font-medium text-text-1 flex items-center gap-2"> | ||
<IconSocial size={16} /> Participants | ||
</span> | ||
</div> | ||
<span className="text-2xl font-bold text-text-1">TBA</span> | ||
</div> | ||
<div className="flex min-w-40 flex-col gap-2 rounded-xl bg-default text-default-foreground p-3"> | ||
<div className="flex items-center gap-1"> | ||
<span className="text-xs font-medium text-text-1 flex items-center gap-2"> | ||
<IconRepo size={16} /> Issues to challenge | ||
</span> | ||
</div> | ||
<span className="text-2xl font-bold text-text-1"> | ||
TBA | ||
{/* 87 <span className="text-default-600 font-medium">/ 134</span> */} | ||
</span> | ||
</div> | ||
<div className="flex min-w-40 flex-col gap-2 rounded-xl bg-default text-default-foreground p-3"> | ||
<div className="flex items-center gap-1"> | ||
<span className="text-xs font-medium text-text-1 flex items-center gap-2"> | ||
<IconWeb size={16} /> Projects | ||
</span> | ||
</div> | ||
<span className="text-2xl font-bold text-text-1">TBA</span> | ||
</div> | ||
</div> | ||
</div> | ||
</article> | ||
); | ||
|
||
export default EventBanner; |
Oops, something went wrong.