From 12c4ee85f656a226b4307921a0caf4a4258b5888 Mon Sep 17 00:00:00 2001 From: Samet Date: Thu, 2 Mar 2023 04:19:34 +0300 Subject: [PATCH] feat: created SVG document components --- components/codewars/index.ts | 21 --- components/github/index.ts | 25 --- components/icons/index.tsx | 83 ++++++++++ components/stackoverflow/index.ts | 21 --- components/svgs/codewars/index.tsx | 39 +++++ components/svgs/document.tsx | 194 ++++++++++++++++++++++++ components/svgs/github/index.tsx | 90 +++++++++++ components/svgs/stackoverflow/index.tsx | 29 ++++ package.json | 10 +- pages/api/platform/codewars.ts | 2 +- pages/api/platform/github.ts | 2 +- pages/api/platform/stackoverflow.ts | 2 +- services/platform/github/index.ts | 2 +- services/platform/index.ts | 2 +- services/platform/response.ts | 9 +- utils/index.ts | 10 ++ utils/render.ts | 15 ++ 17 files changed, 474 insertions(+), 82 deletions(-) delete mode 100644 components/codewars/index.ts delete mode 100644 components/github/index.ts create mode 100644 components/icons/index.tsx delete mode 100644 components/stackoverflow/index.ts create mode 100644 components/svgs/codewars/index.tsx create mode 100644 components/svgs/document.tsx create mode 100644 components/svgs/github/index.tsx create mode 100644 components/svgs/stackoverflow/index.tsx create mode 100644 utils/index.ts create mode 100644 utils/render.ts diff --git a/components/codewars/index.ts b/components/codewars/index.ts deleted file mode 100644 index d3510b8..0000000 --- a/components/codewars/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const getUser = (result: any, platform: any) => { - return ` - Github Total Contributions - - Layer 1 - - - ${platform} - - - Total - - - Contributions - - - ${0} - - - `.replace(/\t/g, ""); -}; diff --git a/components/github/index.ts b/components/github/index.ts deleted file mode 100644 index 52d9e32..0000000 --- a/components/github/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const getContributions = (result: any, platform: any) => { - return ` - ${platform} Total Contributions - - Layer 1 - - - ${platform} - - - Total - - - Contributions - - - 0 - - - `; -}; - -export const getPopularContributions = (result: any) => {}; - -export const getContributionsSummary = (result: any) => {}; diff --git a/components/icons/index.tsx b/components/icons/index.tsx new file mode 100644 index 0000000..d0274bb --- /dev/null +++ b/components/icons/index.tsx @@ -0,0 +1,83 @@ +export const IssueIcon = () => ( + +); + +export const PullRequestIcon = () => ( + + + +); + +export const MergeIcon = () => ( + + + +); + +export const CommitIcon = () => ( + + + +); + +export const RepositoryIcon = () => ( + + + +); + +export const JavaScriptIcon = () => ( + + + +); + +export const PythonIcon = () => ( + + + + +); + +export const TypeScriptIcon = () => ( + + + +); + +export const ScoreIcon = () => ( + + + +); + +export const StarIcon = () => ( + + + +); + +export const CodeIcon = () => ( + + + +); diff --git a/components/stackoverflow/index.ts b/components/stackoverflow/index.ts deleted file mode 100644 index 28ff719..0000000 --- a/components/stackoverflow/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const getReputation = (result: any, platform: any) => { - return ` - Github Total Contributions - - Layer 1 - - - ${platform} - - - Total - - - Contributions - - - ${0} - - - `.replace(/\t/g, ""); -}; diff --git a/components/svgs/codewars/index.tsx b/components/svgs/codewars/index.tsx new file mode 100644 index 0000000..2cddb8e --- /dev/null +++ b/components/svgs/codewars/index.tsx @@ -0,0 +1,39 @@ +import * as Icons from "@components/icons"; +import { + Document, + DocumentTitle, + List, + ListItem, +} from "@components/svgs/document"; +import { ObjectEntries } from "@utils"; + +export const getUser = (result: any, platform: any) => { + const langs = ObjectEntries(result.ranks.languages).map(([key, value]) => ({ + lang: key, + ...value, + })); + + const langItems = langs.map((lang, index) => ( + } + text={lang.lang as string} + value={lang.score} + /> + )); + + return ( + + Codewars + + } text="Honor" value={result.honor} /> + } + text="Overall Score" + value={result.ranks.overall.score} + /> + {langItems} + + + ); +}; diff --git a/components/svgs/document.tsx b/components/svgs/document.tsx new file mode 100644 index 0000000..f66655f --- /dev/null +++ b/components/svgs/document.tsx @@ -0,0 +1,194 @@ +import { ReactElement } from "react"; + +export const getDocumentStyle = () => { + return ``; +}; + +type IDocument = { + (props: { + width: number; + height: number; + children: JSX.Element | JSX.Element[]; + }): JSX.Element; +}; + +type IDocumentHeader = { + (props: { title: string; desc: string }): JSX.Element; +}; + +type IDocumentTitle = { + (props: { children: string }): JSX.Element; +}; + +type IList = { + (props: { children: any }): JSX.Element; +}; + +export type IListItem = { + (props: IItem): JSX.Element; +}; + +type IItem = { + icon: JSX.Element; + url?: string; + text: string; + value?: string; + index?: number; + gap?: number; +}; + +export const Document: IDocument = ({ width, height, children }) => { + return ( + + {getDocumentStyle()} + + {/* */} + {/* */} + {children} + + ); +}; + +export const DocumentTitle: IDocumentTitle = ({ children }) => { + return ( + + + + {children} + + + + ); +}; + +export const DocumentHeader: IDocumentHeader = ({ title, desc }) => { + return ( + <> + {title} + {desc} + + ); +}; + +export const DocumentBackground = () => { + return ( + + ); +}; + +export const List: IList = ({ children }) => { + if (Array.isArray(children)) children = children.flat(); + + if (!Array.isArray(children)) children = [children]; + + const makeTitleShort = ( + element: ReactElement + ): JSX.Element => { + return { + ...element, + props: { + ...element.props, + value: + element.props?.value && String(element.props.value).length > 45 + ? String(element.props.value).slice(0, 45) + "..." + : String(element.props.value), + }, + }; + }; + + const calculateGap = (elements: ReactElement[]) => { + if (!children) { + return []; + } + const gap = + elements + .map((child) => child.props.text.length * 6.7) + .reduce((a, b) => Math.max(a, b), 0) + 45; + return elements.map((child, index) => ({ + ...child, + props: { ...child.props, index, gap: gap }, + })); + }; + + children = calculateGap(children.map(makeTitleShort)); + + return ( + + + {children} + + + ); +}; + +export const ListItem: IListItem = ({ + index = 0, + gap = 0, + icon, + text, + value, + url, +}) => { + return ( + + + + {icon} + + {text} + + + + {url ? ( + + + {value} + + + ) : ( + + {value} + + )} + + + ); +}; diff --git a/components/svgs/github/index.tsx b/components/svgs/github/index.tsx new file mode 100644 index 0000000..6519362 --- /dev/null +++ b/components/svgs/github/index.tsx @@ -0,0 +1,90 @@ +import * as Icons from "@components/icons"; +import { + Document, + DocumentTitle, + List, + ListItem, +} from "@components/svgs/document"; + +export const getCurrentYearContributions = (result: any, platform: any) => { + const { totalContributions } = + result.data.user.contributionsCollection.contributionCalendar; + + return ( + + Github + + } + text="Contibutions in this year:" + value={totalContributions} + /> + + + ); +}; + +export const getPopularContributions = (result: any) => { + const popularContributions = result.data.user.contributionsCollection; + const [popularIssue, popularPullrequest] = [ + popularContributions.popularIssueContribution.issue, + popularContributions.popularPullRequestContribution.pullRequest, + ]; + + return ( + + Github + + } + text="The most popular issue:" + value={popularIssue.title} + url={popularIssue.url} + /> + } + text="The most popular PR:" + value={popularPullrequest.title} + url={popularPullrequest.url} + /> + + + ); +}; + +export const getContributionsSummary = (result: any) => { + const { + totalRepositoryContributions, + totalRepositoriesWithContributedCommits, + totalRepositoriesWithContributedPullRequests, + totalRepositoriesWithContributedIssues, + } = result.data.user.contributionsCollection; + + return ( + + Github + + } + text="Total repository contributions:" + value={totalRepositoryContributions} + /> + } + text="Total repositories with contributed commits:" + value={totalRepositoriesWithContributedCommits} + /> + } + text="Total repositories with contributed PRs:" + value={totalRepositoriesWithContributedPullRequests} + /> + } + text="Total repositories with contributed issues:" + value={totalRepositoriesWithContributedIssues} + /> + + + ); +}; diff --git a/components/svgs/stackoverflow/index.tsx b/components/svgs/stackoverflow/index.tsx new file mode 100644 index 0000000..07bacc9 --- /dev/null +++ b/components/svgs/stackoverflow/index.tsx @@ -0,0 +1,29 @@ +import { + Document, + DocumentTitle, + List, + ListItem, +} from "@components/svgs/document"; +import * as Icons from "@components/icons"; + +export const getReputation = (result: any, platform: any) => { + return ( + + Stackoverflow + + } + text="Reputation" + value={result.reputation} + url={`https://stackoverflow.com/users/${result.user_id}`} + /> + } + text="Total Answered Question" + value={result.reputation} + url={`https://stackoverflow.com/users/${result.user_id}`} + /> + + + ); +}; diff --git a/package.json b/package.json index 82e83d5..ffb8d14 100644 --- a/package.json +++ b/package.json @@ -22,16 +22,16 @@ "react-dom": "18.2.0" }, "devDependencies": { - "typescript": "4.8.4", + "@commitlint/cli": "^17.4.4", + "@commitlint/config-conventional": "^17.4.4", "@types/node": "18.11.9", "@types/react": "18.0.24", "@types/react-dom": "18.0.8", - "@commitlint/cli": "^17.4.4", - "@commitlint/config-conventional": "^17.4.4", "eslint": "8.34.0", "eslint-config-next": "13.2.0", "husky": "^8.0.0", "prettier": "^2.8.4", - "prisma": "^4.9.0" + "prisma": "^4.9.0", + "typescript": "4.8.4" } -} \ No newline at end of file +} diff --git a/pages/api/platform/codewars.ts b/pages/api/platform/codewars.ts index 1e3848f..8adab84 100644 --- a/pages/api/platform/codewars.ts +++ b/pages/api/platform/codewars.ts @@ -1,5 +1,5 @@ import * as services from "@services/platform/codewars"; -import * as templates from "@components/codewars"; +import * as templates from "@components/svgs/codewars"; import handlePlatformAPI from "@services/api/handler"; export default handlePlatformAPI("codewars", services, templates); diff --git a/pages/api/platform/github.ts b/pages/api/platform/github.ts index 193aa97..5db025e 100644 --- a/pages/api/platform/github.ts +++ b/pages/api/platform/github.ts @@ -1,5 +1,5 @@ import * as services from "@services/platform/github"; -import * as templates from "@components/github"; +import * as templates from "@components/svgs/github"; import handlePlatformAPI from "@services/api/handler"; export default handlePlatformAPI("github", services, templates); diff --git a/pages/api/platform/stackoverflow.ts b/pages/api/platform/stackoverflow.ts index 071394e..2f5658c 100644 --- a/pages/api/platform/stackoverflow.ts +++ b/pages/api/platform/stackoverflow.ts @@ -1,5 +1,5 @@ import * as services from "@services/platform/stackoverflow"; -import * as templates from "@components/stackoverflow"; +import * as templates from "@components/svgs/stackoverflow"; import handlePlatformAPI from "@services/api/handler"; export default handlePlatformAPI("stackoverflow", services, templates); diff --git a/services/platform/github/index.ts b/services/platform/github/index.ts index 38540c0..d7873f9 100644 --- a/services/platform/github/index.ts +++ b/services/platform/github/index.ts @@ -5,7 +5,7 @@ import { GithubUserConfig } from "@services/platform/types"; // Github GraphQL API Explorer can be used to discover // https://docs.github.com/en/graphql/overview/explorer -export const getContributions = async ( +export const getCurrentYearContributions = async ( userConfig: GithubUserConfig ): Promise => { const query = `{ user(login: "${userConfig.username}") { diff --git a/services/platform/index.ts b/services/platform/index.ts index 84ccc96..c96da5d 100644 --- a/services/platform/index.ts +++ b/services/platform/index.ts @@ -1,5 +1,5 @@ export type { - getContributions, + getCurrentYearContributions, getPopularContributions, getContributionsSummary, } from "@services/platform/github"; diff --git a/services/platform/response.ts b/services/platform/response.ts index dd9d5e9..1b7cfe4 100644 --- a/services/platform/response.ts +++ b/services/platform/response.ts @@ -1,3 +1,6 @@ +import JSXRender from "@utils/render"; +import { trimChars } from "@utils"; + export const getPlatformResponse = async ( query: any, services: any, @@ -62,11 +65,7 @@ export const getPlatformResponse = async ( return { success: true, status: 200, - data: trimChars(templateOutput), + data: trimChars(JSXRender(templateOutput)), contentType: "image/svg+xml", }; }; - -const trimChars = (body: string) => { - return body.replace(/[\t|\n]/g, "").replace(/ /g, ""); -}; diff --git a/utils/index.ts b/utils/index.ts new file mode 100644 index 0000000..35f4e93 --- /dev/null +++ b/utils/index.ts @@ -0,0 +1,10 @@ +export const trimChars = (body: string) => { + return body.replace(/[\t|\n]/g, "").replace(/ /g, ""); +}; + +type ValueOf = T[keyof T]; +type Entries = [keyof T, ValueOf][]; + +export function ObjectEntries(obj: T): Entries { + return Object.entries(obj) as Entries; +} diff --git a/utils/render.ts b/utils/render.ts new file mode 100644 index 0000000..18b00b8 --- /dev/null +++ b/utils/render.ts @@ -0,0 +1,15 @@ +import ReactDOMServer from "react-dom/server"; + +const render = (JSXString: JSX.Element) => { + const SVGDocumentString = decodeURIComponent( + ReactDOMServer.renderToString(JSXString) + ) + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'"); + + return SVGDocumentString; +}; + +export default render;