From 1d3cd4df392671bc738f085120f60c0102cee37e Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Thu, 9 Feb 2023 21:51:55 +0100 Subject: [PATCH 01/33] chore: add radix us primitives for tabs --- npm-shrinkwrap.json | 106 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 ++ tailwind.config.js | 26 ++++++----- 3 files changed, 123 insertions(+), 12 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d015aef418..a7b07784cd 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -23,6 +23,7 @@ "@radix-ui/react-hover-card": "^1.0.3", "@radix-ui/react-select": "^1.2.0", "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.2", "@radix-ui/react-toggle-group": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.3", "@react-spring/web": "^9.6.1", @@ -32,6 +33,7 @@ "@supabase/gotrue-js": "^1.22.22", "@supabase/supabase-js": "^2.7.0", "@supabase/ui": "^0.36.5", + "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", "date-fns": "^2.29.3", "echarts": "^5.4.1", @@ -48,6 +50,7 @@ "react-spring": "^9.6.1", "stripe": "^11.8.0", "swr": "^2.0.3", + "tailwind-merge": "^1.9.1", "tailwindcss-radix": "^2.7.0", "zustand": "^4.3.2" }, @@ -6605,6 +6608,88 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.2.tgz", + "integrity": "sha512-gOUwh+HbjCuL0UCo8kZ+kdUEG8QtpdO4sMQduJ34ZEz0r4922g9REOBM+vIsfwtGxSug4Yb1msJMJYN2Bk8TpQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-roving-focus": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.1.tgz", + "integrity": "sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz", + "integrity": "sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-collection": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/@radix-ui/react-toggle": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.1.tgz", @@ -17417,6 +17502,22 @@ "node": ">=0.10.0" } }, + "node_modules/class-variance-authority": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.4.0.tgz", + "integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==", + "funding": { + "url": "https://joebell.co.uk" + }, + "peerDependencies": { + "typescript": ">= 4.5.5 < 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -32151,6 +32252,11 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, + "node_modules/tailwind-merge": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.9.1.tgz", + "integrity": "sha512-ED9MkiUHlmfh58EC1xHRqXcH1IQyRtmDP0AmXlugYkP2tvfu7ejtjFEHJLJt93mQ7ZJkcqSIgm9M394bq5vOJg==" + }, "node_modules/tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", diff --git a/package.json b/package.json index d97defec9c..4743153407 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@radix-ui/react-hover-card": "^1.0.3", "@radix-ui/react-select": "^1.2.0", "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.2", "@radix-ui/react-toggle-group": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.3", "@react-spring/web": "^9.6.1", @@ -66,6 +67,7 @@ "@supabase/gotrue-js": "^1.22.22", "@supabase/supabase-js": "^2.7.0", "@supabase/ui": "^0.36.5", + "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", "date-fns": "^2.29.3", "echarts": "^5.4.1", @@ -82,6 +84,7 @@ "react-spring": "^9.6.1", "stripe": "^11.8.0", "swr": "^2.0.3", + "tailwind-merge": "^1.9.1", "tailwindcss-radix": "^2.7.0", "zustand": "^4.3.2" }, diff --git a/tailwind.config.js b/tailwind.config.js index e94f2c4bb5..d0701a4c30 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,7 +3,8 @@ module.exports = { theme: { extend: { gridTemplateColumns: { - autodesktop: "repeat(auto-fit, minmax(410px, 1fr))", automobile: "repeat(auto-fit, minmax(300px, 1fr))" + autodesktop: "repeat(auto-fit, minmax(410px, 1fr))", + automobile: "repeat(auto-fit, minmax(300px, 1fr))" }, screens: { xs: "425px", @@ -15,12 +16,15 @@ module.exports = { blur: { "4xl": "5rem" }, - spacing:{ - "98": "29rem", - "97":"28rem", - "99":"56rem" + spacing: { + 98: "29rem", + 97: "28rem", + 99: "56rem" }, colors: { + sauced: { + orange: "hsla(19, 100%, 50%, 1)" + }, gradient: { "dark-two": "hsl(26, 54%, 15%)", "dark-one": "hsl(23, 75%, 5%)", @@ -201,17 +205,15 @@ module.exports = { } }, boxShadow: { - "login": "0px 64px 100px -80px #FF5F13, 0px 16px 56px rgba(181, 107, 72, 0.19)", - "superlative": "0px 22px 24px -8px rgba(0, 0, 0, 0.05), 0px 4px 8px rgba(0, 0, 0, 0.1)", - "paginate": "0px 1px 2px rgba(237, 95, 0, 0.05), 0px 1px 1px rgba(237, 95, 0, 0.05)", - "input": "0px 1px 2px rgba(17, 24, 28, 0.05), 0px 1px 1px rgba(17, 24, 28, 0.05)" + login: "0px 64px 100px -80px #FF5F13, 0px 16px 56px rgba(181, 107, 72, 0.19)", + superlative: "0px 22px 24px -8px rgba(0, 0, 0, 0.05), 0px 4px 8px rgba(0, 0, 0, 0.1)", + paginate: "0px 1px 2px rgba(237, 95, 0, 0.05), 0px 1px 1px rgba(237, 95, 0, 0.05)", + input: "0px 1px 2px rgba(17, 24, 28, 0.05), 0px 1px 1px rgba(17, 24, 28, 0.05)" }, backgroundImage: { "gradient-radial": "radial-gradient(var(--tw-gradient-stops))" } } }, - plugins: [ - require("tailwindcss-radix")() - ] + plugins: [require("tailwindcss-radix")()] }; From 4caccb02ff7f66438b067407ca6af7815a2125cb Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Thu, 9 Feb 2023 21:52:28 +0100 Subject: [PATCH 02/33] feat: create reusable tab component --- components/atoms/Tabs/tabs.tsx | 43 +++++++++++++++++++ .../HighlightInput/highlight-input-form.tsx | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 components/atoms/Tabs/tabs.tsx diff --git a/components/atoms/Tabs/tabs.tsx b/components/atoms/Tabs/tabs.tsx new file mode 100644 index 0000000000..efe0f51bf0 --- /dev/null +++ b/components/atoms/Tabs/tabs.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; + +import { cn } from "lib/utils/cn"; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/components/molecules/HighlightInput/highlight-input-form.tsx b/components/molecules/HighlightInput/highlight-input-form.tsx index a08258cae8..2455b7b69b 100644 --- a/components/molecules/HighlightInput/highlight-input-form.tsx +++ b/components/molecules/HighlightInput/highlight-input-form.tsx @@ -50,7 +50,7 @@ const HighlightInputForm = (): JSX.Element => { }; return ( -
+
{ setIsDivFocused(true); From 0fb9f26d3cc02ff2521ee3861c72696d5f232790 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Thu, 9 Feb 2023 21:53:01 +0100 Subject: [PATCH 03/33] feat: refactor contributor profile page to use tabs --- .../contributor-profile-tab.tsx | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 components/organisms/ContributorProfileTab/contributor-profile-tab.tsx diff --git a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx new file mode 100644 index 0000000000..0d27a91497 --- /dev/null +++ b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx @@ -0,0 +1,128 @@ +import Avatar from "components/atoms/Avatar/avatar"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "components/atoms/Tabs/tabs"; +import HighlightInputForm from "components/molecules/HighlightInput/highlight-input-form"; +import Text from "components/atoms/Typography/text"; +import { getRelativeDays } from "lib/utils/date-utils"; +import Pill from "components/atoms/Pill/pill"; +import CardLineChart from "components/molecules/CardLineChart/card-line-chart"; +import CardRepoList, { RepoList } from "components/molecules/CardRepoList/card-repo-list"; +import PullRequestTable from "components/molecules/PullRequestTable/pull-request-table"; +import { getAvatarByUsername } from "lib/utils/github"; +import Image, { StaticImageData } from "next/image"; + +interface ContributorProfileTabProps { + user?: DbUser; + prTotal: number; + openPrs: number; + prVelocity: number; + prMerged: number; + recentContributionCount: number; + prsMergedPercentage: number; + chart: Object; + githubName: string; + repoList: RepoList[]; +} + +const ContributorProfileTab = ({ + user, + openPrs, + prMerged, + prTotal, + prVelocity, + prsMergedPercentage, + chart, + githubName, + recentContributionCount, + repoList +}: ContributorProfileTabProps): JSX.Element => { + return ( + + + + Highlights + + + Contributions + + + + {/* Highlights Tab details */} + + +
+ + + +
+
+ + {/* Contributions Tab Details */} + + +
+
+
+
+ PRs opened + {openPrs ? ( +
+ {openPrs} PRs +
+ ) : ( +
-
+ )} +
+
+ Avg PRs velocity + {prVelocity ? ( +
+ + {getRelativeDays(prVelocity)} + + + +
+ ) : ( +
-
+ )} +
+
+ Contributed Repos + {recentContributionCount ? ( +
+ + {`${recentContributionCount} Repo${recentContributionCount > 1 ? "s" : ""}`} + +
+ ) : ( +
-
+ )} +
+
+
+ +
+
+ +
+ +
+ +
+
+

The data for these contributions is from publicly available open source projects on GitHub.

+
+
+
+
+
+ ); +}; + +export default ContributorProfileTab; From df7ce4efb209a48521e071f190f3cefa3187e98e Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Thu, 9 Feb 2023 21:53:34 +0100 Subject: [PATCH 04/33] chore: install tailwind class name and merger --- .../contributor-profile-page.tsx | 105 +++++++----------- lib/utils/cn.ts | 6 + .../contributor-profile-tab.stotries.tsx | 0 3 files changed, 45 insertions(+), 66 deletions(-) create mode 100644 lib/utils/cn.ts create mode 100644 stories/organisms/contributor-profile-tab.stotries.tsx diff --git a/components/organisms/ContributorProfilePage/contributor-profile-page.tsx b/components/organisms/ContributorProfilePage/contributor-profile-page.tsx index 9188a6ffe8..b070dd631a 100644 --- a/components/organisms/ContributorProfilePage/contributor-profile-page.tsx +++ b/components/organisms/ContributorProfilePage/contributor-profile-page.tsx @@ -14,6 +14,7 @@ import { getRelativeDays } from "lib/utils/date-utils"; import Pill from "components/atoms/Pill/pill"; import getPercent from "lib/utils/get-percent"; import ContributorProfileInfo from "components/molecules/ContributorProfileInfo/contributor-profile-info"; +import ContributorProfileTab from "../ContributorProfileTab/contributor-profile-tab"; const colorKeys = Object.keys(color); interface PrObjectType { @@ -76,15 +77,30 @@ const ContributorProfilePage = ({ const isLoaded = !loading && !error; // eslint-disable-next-line camelcase - const { bio, location, interests, name, twitter_username, timezone, display_local_time: displayLocalTime } = user || {}; + const { + bio, + location, + interests, + name, + // eslint-disable-next-line camelcase + twitter_username, + timezone, + display_local_time: displayLocalTime + } = user || {}; return (
- {loading ? : } + {loading ? ( + + ) : ( + + )}
- { - loading ? : <> + {loading ? ( + + ) : ( + <>
- } + )}
- {loading ? : <> -
- - Contribution Insights - -
-
-
-
- PRs opened - {openPrs ? ( -
- {openPrs} PRs -
- ) : ( -
-
- )} -
-
- Avg PRs velocity - {prVelocity ? ( -
- - {getRelativeDays(prVelocity)} - - - - -
- ) : ( -
-
- )} -
-
- Contributed Repos - {recentContributionCount ? ( -
- - {`${recentContributionCount} Repo${recentContributionCount > 1 ? "s" : ""}`} - -
- ) : ( -
-
- )} -
-
-
- -
-
- -
- -
- -
-
-

The data for these contributions is from publicly available open source projects on GitHub.

-
-
- } + {loading ? ( + + ) : ( + <> + + + )}
diff --git a/lib/utils/cn.ts b/lib/utils/cn.ts new file mode 100644 index 0000000000..cec6ac9e86 --- /dev/null +++ b/lib/utils/cn.ts @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/stories/organisms/contributor-profile-tab.stotries.tsx b/stories/organisms/contributor-profile-tab.stotries.tsx new file mode 100644 index 0000000000..e69de29bb2 From 2bd8e145b2d396f351249e12796a47bb734eabc9 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Fri, 10 Feb 2023 22:42:28 +0100 Subject: [PATCH 05/33] chore: add DbHighlights to global interfaces --- next-types.d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/next-types.d.ts b/next-types.d.ts index e573a14f0e..83dfd1f260 100644 --- a/next-types.d.ts +++ b/next-types.d.ts @@ -132,3 +132,15 @@ interface DbUser { readonly display_email: boolean; readonly timezone: string; } + +interface DbHighlight { + readonly id: string; + readonly user_id: string; + readonly url: string; + readonly title: string; + readonly highlight: string; + readonly pinned: boolean; + readonly created_at: string; + readonly updated_at: string; + readonly deleted_at: string; +} From 41973a3386546d28d34c1b209767d1616a5e8161 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Fri, 10 Feb 2023 22:43:26 +0100 Subject: [PATCH 06/33] feat: create pull request highlight card --- .../contributor-highlight-card.tsx | 44 +++++++++++++ .../pull-request-highlight-card.tsx | 62 +++++++++++++++++++ .../contributor-profile-tab.tsx | 55 ++++++++++++++-- .../contributor-profile-tab.stotries.tsx | 0 4 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 components/molecules/ContributorHighlight/contributor-highlight-card.tsx create mode 100644 components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx delete mode 100644 stories/organisms/contributor-profile-tab.stotries.tsx diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx new file mode 100644 index 0000000000..82d0799937 --- /dev/null +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -0,0 +1,44 @@ +import Title from "components/atoms/Typography/title"; +import { generateApiPrUrl } from "lib/utils/github"; +import React from "react"; +import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card"; + +interface ContributorHighlightCardProps { + title?: string; + desc?: string; + prLink: string; +} +const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightCardProps) => { + console.log(generateApiPrUrl(prLink)); + return ( +
+
+ {title && ( + + {title} + + )} +
+
+ + {/* Highlight body section */} +
+

{desc}

+
+ {/* Highlight Link section */} + + + + {/* Generated OG card section */} +
+ +
+
+ ); +}; + +export default ContributorHighlightCard; diff --git a/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx b/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx new file mode 100644 index 0000000000..38d2611911 --- /dev/null +++ b/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx @@ -0,0 +1,62 @@ +import Avatar from "components/atoms/Avatar/avatar"; +import React from "react"; +import { AiOutlineGithub } from "react-icons/ai"; +import { TbMessages } from "react-icons/tb"; +import CardHorizontalBarChart from "../CardHorizontalBarChart/card-horizontal-bar-chart"; + +interface PullRequestHighlightCardProps { + orgName: string; + repoName: string; + ticketNumber: number; + prTitle: string; + commentCount: number; + userAvatar: string; + createdAt: string; + userName: string; +} +const PullRequestHighlightCard = () => { + return ( +
+
+
+ open-sauced/insights +

+ #24 fix: Remote semicolons +

+
+ 1 comment +
+
+
+ +
+
+ +
+
+
+ +

+ natemoo-re opened on Dec 22, 2023 +

+
+ +
+
+
+ +
+
+

fix: Remote semicolons · Issue #827 · open-sauced/insights

+ GITHUB.COM +
+
+
+
+ ); +}; + +export default PullRequestHighlightCard; diff --git a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx index 0d27a91497..9f22bb2713 100644 --- a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx +++ b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx @@ -2,13 +2,16 @@ import Avatar from "components/atoms/Avatar/avatar"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "components/atoms/Tabs/tabs"; import HighlightInputForm from "components/molecules/HighlightInput/highlight-input-form"; import Text from "components/atoms/Typography/text"; -import { getRelativeDays } from "lib/utils/date-utils"; +import { getFormattedDate, getRelativeDays } from "lib/utils/date-utils"; import Pill from "components/atoms/Pill/pill"; import CardLineChart from "components/molecules/CardLineChart/card-line-chart"; import CardRepoList, { RepoList } from "components/molecules/CardRepoList/card-repo-list"; import PullRequestTable from "components/molecules/PullRequestTable/pull-request-table"; -import { getAvatarByUsername } from "lib/utils/github"; -import Image, { StaticImageData } from "next/image"; +import HightlightEmptyState from "components/molecules/ContributorHighlight/highlight-empty-state"; +import ContributorHighlightCard from "components/molecules/ContributorHighlight/contributor-highlight-card"; +import { useFetchUserHighlights } from "lib/hooks/useFetchUserHighlights"; +import { useState } from "react"; +import Button from "components/atoms/Button/button"; interface ContributorProfileTabProps { user?: DbUser; @@ -35,6 +38,11 @@ const ContributorProfileTab = ({ recentContributionCount, repoList }: ContributorProfileTabProps): JSX.Element => { + const { login } = user || {}; + + const { data: highlights, isError, isLoading } = useFetchUserHighlights(login || ""); + const [inputVisible, setInputVisible] = useState(highlights.length > 0 ? true : false); + return ( @@ -55,10 +63,45 @@ const ContributorProfileTab = ({ {/* Highlights Tab details */} -
- + {inputVisible && ( +
+ + + +
+ )} +
+ {/* */} - + {isLoading && <>Loading...} + {isError && <>An error occured} + {highlights && highlights.length > 0 ? ( + // eslint-disable-next-line camelcase + highlights.map(({ id, title, highlight, url, created_at }) => ( +
+

{getFormattedDate(created_at)}

+ +
+ )) + ) : ( +
+
+

+ You don't have any highlights yet!
Highlights are a great way to show off your + contributions. Merge any new pull requests recently? +

+ {!inputVisible && ( + + )} +
+
+ )}
diff --git a/stories/organisms/contributor-profile-tab.stotries.tsx b/stories/organisms/contributor-profile-tab.stotries.tsx deleted file mode 100644 index e69de29bb2..0000000000 From da4dc40f37f4d6d5431a918b296dbb2ce9f100db Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Fri, 10 Feb 2023 22:44:32 +0100 Subject: [PATCH 07/33] chore: add fetch user highlights hooks --- lib/hooks/useFetchGithubPRInfo.ts | 0 lib/hooks/useFetchUserHighlights.ts | 22 ++++++++++++++++++++++ lib/utils/date-utils.ts | 10 ++++++++++ lib/utils/github.ts | 21 ++++++++++----------- 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 lib/hooks/useFetchGithubPRInfo.ts create mode 100644 lib/hooks/useFetchUserHighlights.ts diff --git a/lib/hooks/useFetchGithubPRInfo.ts b/lib/hooks/useFetchGithubPRInfo.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/hooks/useFetchUserHighlights.ts b/lib/hooks/useFetchUserHighlights.ts new file mode 100644 index 0000000000..b6b507cc7a --- /dev/null +++ b/lib/hooks/useFetchUserHighlights.ts @@ -0,0 +1,22 @@ +import publicApiFetcher from "lib/utils/public-api-fetcher"; +import useSWR, { Fetcher } from "swr"; + +interface useFetchUserHighlightsResponse { + data: DbHighlight[]; + meta: Meta; +} +const useFetchUserHighlights = (username: string) => { + const { data, error } = useSWR( + `users/${username}/highlights`, + publicApiFetcher as Fetcher + ); + + return { + data: data?.data ?? [], + meta: data?.meta ?? { itemCount: 0, limit: 0, page: 0, hasNextPage: false, hasPreviousPage: false, pageCount: 0 }, + isLoading: !error && !data, + isError: !!error + }; +}; + +export { useFetchUserHighlights }; diff --git a/lib/utils/date-utils.ts b/lib/utils/date-utils.ts index dfa6bc17e2..09d8e5ccec 100644 --- a/lib/utils/date-utils.ts +++ b/lib/utils/date-utils.ts @@ -1,4 +1,5 @@ import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict"; +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; export const calcDaysFromToday = (endDate: Date) => { const timeFromNowArray = formatDistanceToNowStrict(endDate, { @@ -59,3 +60,12 @@ export const calcDistanceFromToday = (endDate: Date) => { return `${getRelativeDays(daysFromNow)}`; }; + +export const getFormattedDate = (dateString: string): string => { + const date = new Date(dateString); + const month = months[date.getMonth()]; + const day = date.getDate(); + const year = date.getFullYear(); + + return `${month} ${day}, ${year}`; +}; diff --git a/lib/utils/github.ts b/lib/utils/github.ts index ead1b40ab1..b85a2c7c45 100644 --- a/lib/utils/github.ts +++ b/lib/utils/github.ts @@ -3,21 +3,20 @@ * @todo Use `getAvatarById` instead of `getAvatarByUsername` whenever possible * @see {@link https://github.com/open-sauced/insights/issues/746} */ -const getAvatarByUsername = (username: string | null, size=460) => +const getAvatarByUsername = (username: string | null, size = 460) => `https://www.github.com/${username ?? "github"}.png?size=${size}`; -const getAvatarById = (id: string | null, size=460) => +const getAvatarById = (id: string | null, size = 460) => `https://avatars.githubusercontent.com/u/${id ?? "9919"}?size=${size}&v=4`; -const getProfileLink = (username: string | null) => - `https://github.com/${username ?? ""}`; +const getProfileLink = (username: string | null) => `https://github.com/${username ?? ""}`; -const getRepoIssuesLink = (repoName: string | null) => - `https://github.com/${repoName && `${repoName}/issues` || ""}`; +const getRepoIssuesLink = (repoName: string | null) => `https://github.com/${(repoName && `${repoName}/issues`) || ""}`; -export { - getAvatarById, - getAvatarByUsername, - getProfileLink, - getRepoIssuesLink +const generateApiPrUrl = (url: string | null) => { + const newUrl = url ? url.toLowerCase().trim() : ""; + const apiUrl = newUrl?.substring(0, 8) + "api." + newUrl.substring(8, 19) + "repos" + newUrl.substring(18); + return apiUrl; }; + +export { getAvatarById, getAvatarByUsername, getProfileLink, getRepoIssuesLink, generateApiPrUrl }; From 2e6aa5cbe3956ff91f36704febed48d90489eb28 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Fri, 10 Feb 2023 22:50:25 +0100 Subject: [PATCH 08/33] fix: build error --- .../ContributorProfileTab/contributor-profile-tab.tsx | 1 - lib/hooks/useFetchGithubPRInfo.ts | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx index 9f22bb2713..03617dd76d 100644 --- a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx +++ b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx @@ -7,7 +7,6 @@ import Pill from "components/atoms/Pill/pill"; import CardLineChart from "components/molecules/CardLineChart/card-line-chart"; import CardRepoList, { RepoList } from "components/molecules/CardRepoList/card-repo-list"; import PullRequestTable from "components/molecules/PullRequestTable/pull-request-table"; -import HightlightEmptyState from "components/molecules/ContributorHighlight/highlight-empty-state"; import ContributorHighlightCard from "components/molecules/ContributorHighlight/contributor-highlight-card"; import { useFetchUserHighlights } from "lib/hooks/useFetchUserHighlights"; import { useState } from "react"; diff --git a/lib/hooks/useFetchGithubPRInfo.ts b/lib/hooks/useFetchGithubPRInfo.ts index e69de29bb2..bef7d77f43 100644 --- a/lib/hooks/useFetchGithubPRInfo.ts +++ b/lib/hooks/useFetchGithubPRInfo.ts @@ -0,0 +1,3 @@ +const useFetchGithubPRInfo = async () => {}; + +export { useFetchGithubPRInfo }; From 1d39c0d9024b89386a9f5427be531cc28713593f Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Sat, 11 Feb 2023 08:48:24 +0100 Subject: [PATCH 09/33] refactor: update github utils function --- lib/utils/github.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/utils/github.ts b/lib/utils/github.ts index b85a2c7c45..4878589358 100644 --- a/lib/utils/github.ts +++ b/lib/utils/github.ts @@ -15,7 +15,18 @@ const getRepoIssuesLink = (repoName: string | null) => `https://github.com/${(re const generateApiPrUrl = (url: string | null) => { const newUrl = url ? url.toLowerCase().trim() : ""; - const apiUrl = newUrl?.substring(0, 8) + "api." + newUrl.substring(8, 19) + "repos" + newUrl.substring(18); + let pulls: string[]; + + if (newUrl.substring(0, 8) === "https://") { + pulls = newUrl.substring(19).split("/"); + } else if (newUrl.substring(0, 7) === "http://") { + pulls = newUrl.substring(18).split("/"); + } else { + pulls = newUrl.substring(11).split("/"); + } + const [orgName, repoName, , IssueId] = pulls; + + const apiUrl = `${orgName}/${repoName}/pulls/${IssueId}`; return apiUrl; }; From c55d9bd02299277a6ff02eabb294f85e0cefb767 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Sat, 11 Feb 2023 08:49:00 +0100 Subject: [PATCH 10/33] feat: create github pull info fetch hook --- lib/hooks/useFetchGithubPRInfo.ts | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/hooks/useFetchGithubPRInfo.ts b/lib/hooks/useFetchGithubPRInfo.ts index bef7d77f43..2d7097c099 100644 --- a/lib/hooks/useFetchGithubPRInfo.ts +++ b/lib/hooks/useFetchGithubPRInfo.ts @@ -1,3 +1,42 @@ -const useFetchGithubPRInfo = async () => {}; +import { Fetcher } from "swr"; +import useSWR from "swr"; + +// Leaving this fetcher here as this solution will be changed in the future when the backend supports the github Pull information + +const fetcher: Fetcher = async (githubUrl: string) => { + const res = await fetch(`https://api.github.com/repos/${githubUrl}`); + + if (!res.ok) { + const error = new Error("HttpError"); + + error.message = `${res.status} ${res.statusText}`; + error.stack = JSON.stringify(await res.json()); + + throw error; + } + + return res.json(); +}; + +interface GithubPRInfoResponse { + readonly head: { repo: { name: string; full_name: string }; user: { login: string; full_name: string } }; + readonly title: string; + readonly number: number; + readonly comments: number; + readonly user: { login: string; avatar_url: string }; + readonly created_at: string; +} +const useFetchGithubPRInfo = (url: string) => { + const { data, error } = useSWR( + url, + fetcher as unknown as Fetcher + ); + + return { + data: data ?? undefined, + isLoading: !error && !data, + isError: !!error + }; +}; export { useFetchGithubPRInfo }; From 3fcf68c2c97c978302af77c62c53eb1921c53f77 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Sat, 11 Feb 2023 08:49:41 +0100 Subject: [PATCH 11/33] refactor: update highlight card props --- .../contributor-highlight-card.tsx | 23 +++++-- .../pull-request-highlight-card.tsx | 34 ++++++++--- .../contributor-profile-tab.tsx | 61 +++++++++++-------- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx index 82d0799937..fc441ba635 100644 --- a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -2,6 +2,7 @@ import Title from "components/atoms/Typography/title"; import { generateApiPrUrl } from "lib/utils/github"; import React from "react"; import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card"; +import { useFetchGithubPRInfo } from "lib/hooks/useFetchGithubPRInfo"; interface ContributorHighlightCardProps { title?: string; @@ -9,9 +10,10 @@ interface ContributorHighlightCardProps { prLink: string; } const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightCardProps) => { - console.log(generateApiPrUrl(prLink)); + const { data } = useFetchGithubPRInfo(generateApiPrUrl(prLink)); + return ( -
+
{title && ( @@ -34,9 +36,20 @@ const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightC </div> {/* Generated OG card section */} - <div> - <PullRequestHighlightCard /> - </div> + {data && ( + <div> + <PullRequestHighlightCard + userAvatar={data.user.avatar_url} + userName={data.user.login} + repoName={data.head.repo.name} + ticketNumber={data.number} + createdAt={data.created_at} + commentCount={data.comments} + prTitle={data.title} + orgName={data.head.user.login} + /> + </div> + )} </article> ); }; diff --git a/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx b/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx index 38d2611911..62cbb05882 100644 --- a/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx +++ b/components/molecules/PullRequestHighlightCard/pull-request-highlight-card.tsx @@ -3,6 +3,7 @@ import React from "react"; import { AiOutlineGithub } from "react-icons/ai"; import { TbMessages } from "react-icons/tb"; import CardHorizontalBarChart from "../CardHorizontalBarChart/card-horizontal-bar-chart"; +import { getFormattedDate } from "lib/utils/date-utils"; interface PullRequestHighlightCardProps { orgName: string; @@ -14,30 +15,41 @@ interface PullRequestHighlightCardProps { createdAt: string; userName: string; } -const PullRequestHighlightCard = () => { +const PullRequestHighlightCard = ({ + prTitle, + commentCount, + userAvatar, + createdAt, + userName, + ticketNumber, + repoName, + orgName +}: PullRequestHighlightCardProps) => { return ( - <div className="flex flex-col gap-16 bg-white pt-8 pb-4"> + <div className="flex max-w-[45rem] flex-1 flex-col gap-16 bg-white pt-8 pb-4"> <div className="px-8 flex justify-between"> <div className="flex flex-col gap-2"> - <span className="text-sm font-normal">open-sauced/insights</span> - <h3 className="text-3xl font-semibold"> - <span className="text-light-slate-9 font-normal">#24</span> fix: Remote semicolons + <span className="text-sm font-normal">{`${orgName}/${repoName}`}</span> + <h3 className="text-3xl font-semibold flex items-center gap-1"> + <span className="text-light-slate-9 font-normal">{`#${ticketNumber}`}</span> <p>{prTitle}</p> </h3> <div className="flex gap-2 text-light-slate-9 text-sm"> - <TbMessages className="text-lg" />1 comment + <TbMessages className="text-lg" /> + {commentCount} comment </div> </div> <div> - <Avatar isCircle size="lg" avatarURL="https://avatars.githubusercontent.com/u/57568598?v=4" /> + <Avatar isCircle size="lg" avatarURL={userAvatar} /> </div> </div> <div> <div className="px-8 flex justify-between items-center"> <div className="flex text-sm items-center gap-2"> - <Avatar isCircle size="sm" avatarURL="https://avatars.githubusercontent.com/u/57568598?v=4" /> + <Avatar isCircle size="sm" avatarURL={userAvatar} /> <p className="text-light-slate-9 font-normal"> - <span className="text-light-slate-12 font-semibold">natemoo-re</span> opened on Dec 22, 2023 + <span className="text-light-slate-12 font-semibold">{userName}</span> opened on{" "} + {getFormattedDate(createdAt)} </p> </div> <AiOutlineGithub /> @@ -50,7 +62,9 @@ const PullRequestHighlightCard = () => { /> </div> <div className="px-8"> - <p>fix: Remote semicolons · Issue #827 · open-sauced/insights</p> + <p className="font-semibold"> + {prTitle}· Issue {`#${ticketNumber}`} · {`${orgName}/${repoName}`} + </p> <span>GITHUB.COM</span> </div> </div> diff --git a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx index 03617dd76d..774b69eff7 100644 --- a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx +++ b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx @@ -9,7 +9,7 @@ import CardRepoList, { RepoList } from "components/molecules/CardRepoList/card-r import PullRequestTable from "components/molecules/PullRequestTable/pull-request-table"; import ContributorHighlightCard from "components/molecules/ContributorHighlight/contributor-highlight-card"; import { useFetchUserHighlights } from "lib/hooks/useFetchUserHighlights"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import Button from "components/atoms/Button/button"; interface ContributorProfileTabProps { @@ -40,7 +40,11 @@ const ContributorProfileTab = ({ const { login } = user || {}; const { data: highlights, isError, isLoading } = useFetchUserHighlights(login || ""); - const [inputVisible, setInputVisible] = useState(highlights.length > 0 ? true : false); + const [inputVisible, setInputVisible] = useState(false); + + useEffect(() => { + setInputVisible(highlights && highlights.length !== 0 ? true : false); + }, [highlights]); return ( <Tabs defaultValue="Highlights" className=""> @@ -61,9 +65,9 @@ const ContributorProfileTab = ({ {/* Highlights Tab details */} - <TabsContent value="Highlights"> + <TabsContent className="" value="Highlights"> {inputVisible && ( - <div className="pl-20 gap-x-3 pt-4 flex "> + <div className="pl-20 gap-x-3 pt-4 flex max-w-[48rem]"> <Avatar alt="user profile avatar" size="sm" @@ -76,30 +80,35 @@ const ContributorProfileTab = ({ <div className="mt-4 flex flex-col gap-8"> {/* <HightlightEmptyState /> */} - {isLoading && <>Loading...</>} {isError && <>An error occured</>} - {highlights && highlights.length > 0 ? ( - // eslint-disable-next-line camelcase - highlights.map(({ id, title, highlight, url, created_at }) => ( - <div className="flex gap-7" key={id}> - <p className="text-light-slate-10 text-sm">{getFormattedDate(created_at)}</p> - <ContributorHighlightCard title={title} desc={highlight} prLink={url} /> - </div> - )) + {isLoading ? ( + <>Loading...</> ) : ( - <div className="flex justify-center rounded-xl border border-dashed border-light-slate-8 px-32 py-20 items-center"> - <div className="text-center"> - <p> - You don't have any highlights yet! <br /> Highlights are a great way to show off your - contributions. Merge any new pull requests recently? - </p> - {!inputVisible && ( - <Button onClick={() => setInputVisible(true)} className="mt-5" type="primary"> - Add a highlight - </Button> - )} - </div> - </div> + <> + {highlights && highlights.length > 0 ? ( + // eslint-disable-next-line camelcase + highlights.map(({ id, title, highlight, url, created_at }) => ( + <div className="flex gap-7" key={id}> + <p className="text-light-slate-10 text-sm">{getFormattedDate(created_at)}</p> + <ContributorHighlightCard title={title} desc={highlight} prLink={url} /> + </div> + )) + ) : ( + <div className="flex justify-center rounded-xl border border-dashed border-light-slate-8 px-32 py-20 items-center"> + <div className="text-center"> + <p> + You don't have any highlights yet! <br /> Highlights are a great way to show off your + contributions. Merge any new pull requests recently? + </p> + {!inputVisible && ( + <Button onClick={() => setInputVisible(true)} className="mt-5" type="primary"> + Add a highlight + </Button> + )} + </div> + </div> + )} + </> )} </div> </TabsContent> From 76c2ef3b338c51f24c0dbb8570e89d9610a0d511 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna <oliviamegan11@gmail.com> Date: Mon, 13 Feb 2023 15:30:24 +0100 Subject: [PATCH 12/33] refactor: improve textarea input --- components/atoms/Tabs/tabs.tsx | 8 ++++---- components/atoms/Textarea/text-area.tsx | 21 +++++++++++++++++++++ package.json | 2 -- 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 components/atoms/Textarea/text-area.tsx diff --git a/components/atoms/Tabs/tabs.tsx b/components/atoms/Tabs/tabs.tsx index efe0f51bf0..b376434966 100644 --- a/components/atoms/Tabs/tabs.tsx +++ b/components/atoms/Tabs/tabs.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import * as TabsPrimitive from "@radix-ui/react-tabs"; -import { cn } from "lib/utils/cn"; +import { classNames } from "lib/utils/cn"; const Tabs = TabsPrimitive.Root; @@ -11,7 +11,7 @@ const TabsList = React.forwardRef< >(({ className, ...props }, ref) => ( <TabsPrimitive.List ref={ref} - className={cn("inline-flex items-center justify-center rounded-md px-1 ", className)} + className={classNames("inline-flex items-center justify-center rounded-md px-1 ", className)} {...props} /> )); @@ -22,7 +22,7 @@ const TabsTrigger = React.forwardRef< React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> >(({ className, ...props }, ref) => ( <TabsPrimitive.Trigger - className={cn( + className={classNames( "inline-flex min-w-[100px] items-center justify-center px-3 py-1.5 text-sm font-medium text-slate-700 transition-all disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-slate-900", className )} @@ -36,7 +36,7 @@ const TabsContent = React.forwardRef< React.ElementRef<typeof TabsPrimitive.Content>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> >(({ className, ...props }, ref) => ( - <TabsPrimitive.Content className={cn("mt-2 rounded-md ", className)} {...props} ref={ref} /> + <TabsPrimitive.Content className={classNames("mt-2 rounded-md ", className)} {...props} ref={ref} /> )); TabsContent.displayName = TabsPrimitive.Content.displayName; diff --git a/components/atoms/Textarea/text-area.tsx b/components/atoms/Textarea/text-area.tsx new file mode 100644 index 0000000000..618c71bb2d --- /dev/null +++ b/components/atoms/Textarea/text-area.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; + +import { classNames } from "lib/utils/cn"; + +export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} + +const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => { + return ( + <textarea + className={classNames( + "flex h-20 w-full rounded-md bg-transparent py-2 text-sm placeholder:text-slate-400 disabled:cursor-not-allowed disabled:opacity-50", + className + )} + ref={ref} + {...props} + /> + ); +}); +Textarea.displayName = "Textarea"; + +export { Textarea }; diff --git a/package.json b/package.json index 4743153407..7633e49091 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "@supabase/gotrue-js": "^1.22.22", "@supabase/supabase-js": "^2.7.0", "@supabase/ui": "^0.36.5", - "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", "date-fns": "^2.29.3", "echarts": "^5.4.1", @@ -84,7 +83,6 @@ "react-spring": "^9.6.1", "stripe": "^11.8.0", "swr": "^2.0.3", - "tailwind-merge": "^1.9.1", "tailwindcss-radix": "^2.7.0", "zustand": "^4.3.2" }, From 3a1e315dc857cb5630fea4a76952319d37e4b184 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna <oliviamegan11@gmail.com> Date: Mon, 13 Feb 2023 15:31:30 +0100 Subject: [PATCH 13/33] feat: add create highlight function to libs --- .../contributor-highlight-card.tsx | 4 +- .../HighlightInput/highlight-input-form.tsx | 42 +++++++++++++++---- lib/hooks/createHighlights.ts | 30 +++++++++++++ lib/utils/cn.ts | 5 +-- pages/user/settings.tsx | 5 ++- 5 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 lib/hooks/createHighlights.ts diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx index fc441ba635..fd26f0230f 100644 --- a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -10,7 +10,7 @@ interface ContributorHighlightCardProps { prLink: string; } const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightCardProps) => { - const { data } = useFetchGithubPRInfo(generateApiPrUrl(prLink)); + const { data, isLoading, isError } = useFetchGithubPRInfo(generateApiPrUrl(prLink)); return ( <article className="flex flex-col max-w-[40rem] flex-1 gap-6"> @@ -36,6 +36,8 @@ const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightC </div> {/* Generated OG card section */} + {isLoading && <>Loading...</>} + {isError && <>An error occured...</>} {data && ( <div> <PullRequestHighlightCard diff --git a/components/molecules/HighlightInput/highlight-input-form.tsx b/components/molecules/HighlightInput/highlight-input-form.tsx index 2455b7b69b..260b18e76d 100644 --- a/components/molecules/HighlightInput/highlight-input-form.tsx +++ b/components/molecules/HighlightInput/highlight-input-form.tsx @@ -1,5 +1,8 @@ import Button from "components/atoms/Button/button"; +import { Textarea } from "components/atoms/Textarea/text-area"; +import { createHighlights } from "lib/hooks/createHighlights"; +import { ToastTrigger } from "lib/utils/toast-trigger"; import { ChangeEvent, useEffect, useRef, useState } from "react"; const HighlightInputForm = (): JSX.Element => { @@ -41,12 +44,33 @@ const HighlightInputForm = (): JSX.Element => { }; // Handle submit highlights - const handlePostHighlight = (e: React.FormEvent<HTMLFormElement>) => { + const handlePostHighlight = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); - setIsDivFocused(false); + // Trigger api call to post highlight - setBodyText(""); - setTitle(""); + const pullLink = bodyText.match(/\bhttps?:\/\/\S+/gi); + const [url] = pullLink || []; + const highlight = bodyText.replace(url as string, ""); + + if (url === null || undefined || "" || []) { + ToastTrigger({ message: "A valid Pull request Link is required", type: "error" }); + return; + } else { + const res = await createHighlights({ + highlight, + title, + url: url || "" + }); + setBodyText(""); + setTitle(""); + setIsDivFocused(false); + + if (res) { + ToastTrigger({ message: "Highlight uploade success", type: "success" }); + } else { + ToastTrigger({ message: "An error occured!!!", type: "error" }); + } + } }; return ( @@ -64,14 +88,14 @@ const HighlightInputForm = (): JSX.Element => { type="text" placeholder={isDivFocused ? "Add title (optional)" : "Highlight your merged PRs and provide a link!"} /> - <textarea - rows={row} - value={bodyText} - onChange={(e) => handleTextAreaInputChange(e)} - ref={textAreaRef} + <Textarea className={`resize-none font-normal text-light-slate-11 mb-2 transition focus:outline-none rounded-lg ${ !isDivFocused ? "hidden" : "" }`} + ref={textAreaRef} + rows={row} + value={bodyText} + onChange={(e) => handleTextAreaInputChange(e)} /> </div> diff --git a/lib/hooks/createHighlights.ts b/lib/hooks/createHighlights.ts new file mode 100644 index 0000000000..5a6f56a257 --- /dev/null +++ b/lib/hooks/createHighlights.ts @@ -0,0 +1,30 @@ +import { supabase } from "lib/utils/supabase"; + +interface CreateHighlightsProps { + url: string; + title?: string; + highlight: string; +} + +const baseUrl = process.env.NEXT_PUBLIC_API_URL; +const createHighlights = async (data: CreateHighlightsProps) => { + const sessionResponse = await supabase.auth.getSession(); + const sessionToken = sessionResponse?.data.session?.access_token; + try { + const res = await fetch(`${baseUrl}/user/highlights`, { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: `Bearer ${sessionToken}` + }, + method: "POST", + body: JSON.stringify({ ...data }) + }); + + return res.json(); + } catch (e) { + return false; + } +}; + +export { createHighlights }; diff --git a/lib/utils/cn.ts b/lib/utils/cn.ts index cec6ac9e86..aee7325c0d 100644 --- a/lib/utils/cn.ts +++ b/lib/utils/cn.ts @@ -1,6 +1,5 @@ import { ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); +export function classNames(...inputs: ClassValue[]) { + return clsx(inputs); } diff --git a/pages/user/settings.tsx b/pages/user/settings.tsx index 2f11a98b25..734da4ebe8 100644 --- a/pages/user/settings.tsx +++ b/pages/user/settings.tsx @@ -4,13 +4,14 @@ import UserSettingsPage from "components/organisms/UserSettingsPage/user-setting import ProfileLayout from "layouts/profile"; const ProfileSettings = (): JSX.Element => { - const { user } = useSupabaseAuth(); + const { user, sessionToken } = useSupabaseAuth(); + console.log(sessionToken); return ( <> {user ? ( <div className="w-full px-4 md:px-16 lg:px-48 py-16"> - <UserSettingsPage user={user} /> + <UserSettingsPage user={user} /> </div> ) : ( "" From 883288ba86aad695c8ef66ddb04acdfddaf8c509 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna <oliviamegan11@gmail.com> Date: Mon, 13 Feb 2023 19:38:09 +0100 Subject: [PATCH 14/33] feat: create and implement dropdown --- components/atoms/Dropdown/dropdown.tsx | 45 ++++++++++++++ components/atoms/Tabs/tabs.tsx | 2 +- .../contributor-highlight-card.tsx | 62 ++++++++++++++++++- .../contributor-profile-page.tsx | 2 +- .../contributor-profile-tab.tsx | 36 +++++++---- npm-shrinkwrap.json | 23 ------- 6 files changed, 131 insertions(+), 39 deletions(-) create mode 100644 components/atoms/Dropdown/dropdown.tsx diff --git a/components/atoms/Dropdown/dropdown.tsx b/components/atoms/Dropdown/dropdown.tsx new file mode 100644 index 0000000000..8edd2b1f66 --- /dev/null +++ b/components/atoms/Dropdown/dropdown.tsx @@ -0,0 +1,45 @@ +import * as React from "react"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { classNames } from "lib/utils/cn"; + +const DropdownMenu = DropdownMenuPrimitive.Root; +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> +>(({ className, sideOffset = 4, ...props }, ref) => ( + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + ref={ref} + sideOffset={sideOffset} + className={classNames( + "z-50 min-w-[8rem] overflow-hidden rounded-lg border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 ", + className + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef<typeof DropdownMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + <DropdownMenuPrimitive.Item + ref={ref} + className={classNames( + "relative flex cursor-default select-none items-center rounded-md py-1.5 px-2 text-sm font-medium focus:text-sauced-orange outline-none focus:bg-orange-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ", + inset && "pl-8", + className + )} + {...props} + /> +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +export { DropdownMenu, DropdownMenuItem, DropdownMenuContent, DropdownMenuTrigger }; diff --git a/components/atoms/Tabs/tabs.tsx b/components/atoms/Tabs/tabs.tsx index b376434966..3874ff5a62 100644 --- a/components/atoms/Tabs/tabs.tsx +++ b/components/atoms/Tabs/tabs.tsx @@ -11,7 +11,7 @@ const TabsList = React.forwardRef< >(({ className, ...props }, ref) => ( <TabsPrimitive.List ref={ref} - className={classNames("inline-flex items-center justify-center rounded-md px-1 ", className)} + className={classNames("inline-flex items-center rounded-md px-1 ", className)} {...props} /> )); diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx index fd26f0230f..8494d58c99 100644 --- a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -3,6 +3,18 @@ import { generateApiPrUrl } from "lib/utils/github"; import React from "react"; import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card"; import { useFetchGithubPRInfo } from "lib/hooks/useFetchGithubPRInfo"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from "components/atoms/Dropdown/dropdown"; +import { HiOutlineEmojiHappy } from "react-icons/hi"; +import { TfiMoreAlt } from "react-icons/tfi"; +import { FiLinkedin, FiTwitter } from "react-icons/fi"; +import { BsLink45Deg } from "react-icons/bs"; +import { FaUserPlus } from "react-icons/fa"; +import { GrFlag } from "react-icons/gr"; interface ContributorHighlightCardProps { title?: string; @@ -14,13 +26,59 @@ const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightC return ( <article className="flex flex-col max-w-[40rem] flex-1 gap-6"> - <div> + <div className="flex justify-between items-center"> {title && ( <Title className="!text-xl !text-light-slate-12" level={4}> {title} )} -
+
+ + + + + + + + + + + + + + +
+ + Share to Twitter +
+
+ +
+ + Share to Linkedin +
+
+ +
+ + Copy link +
+
+ +
+ + Follow user +
+
+ +
+ + Report content +
+
+
+
+
{/* Highlight body section */} diff --git a/components/organisms/ContributorProfilePage/contributor-profile-page.tsx b/components/organisms/ContributorProfilePage/contributor-profile-page.tsx index b070dd631a..784ca5d1b8 100644 --- a/components/organisms/ContributorProfilePage/contributor-profile-page.tsx +++ b/components/organisms/ContributorProfilePage/contributor-profile-page.tsx @@ -132,7 +132,7 @@ const ContributorProfilePage = ({ openPrs={openPrs} githubName={githubName} prMerged={prMerged} - user={user} + contributor={user} prTotal={prTotal} prsMergedPercentage={prsMergedPercentage} /> diff --git a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx index 774b69eff7..ba66bd35e4 100644 --- a/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx +++ b/components/organisms/ContributorProfileTab/contributor-profile-tab.tsx @@ -11,9 +11,10 @@ import ContributorHighlightCard from "components/molecules/ContributorHighlight/ import { useFetchUserHighlights } from "lib/hooks/useFetchUserHighlights"; import { useEffect, useState } from "react"; import Button from "components/atoms/Button/button"; +import useSupabaseAuth from "lib/hooks/useSupabaseAuth"; interface ContributorProfileTabProps { - user?: DbUser; + contributor?: DbUser; prTotal: number; openPrs: number; prVelocity: number; @@ -26,7 +27,7 @@ interface ContributorProfileTabProps { } const ContributorProfileTab = ({ - user, + contributor, openPrs, prMerged, prTotal, @@ -37,7 +38,8 @@ const ContributorProfileTab = ({ recentContributionCount, repoList }: ContributorProfileTabProps): JSX.Element => { - const { login } = user || {}; + const { login } = contributor || {}; + const { user } = useSupabaseAuth(); const { data: highlights, isError, isLoading } = useFetchUserHighlights(login || ""); const [inputVisible, setInputVisible] = useState(false); @@ -46,6 +48,8 @@ const ContributorProfileTab = ({ setInputVisible(highlights && highlights.length !== 0 ? true : false); }, [highlights]); + console.log(user); + return ( @@ -66,7 +70,7 @@ const ContributorProfileTab = ({ {/* Highlights Tab details */} - {inputVisible && ( + {inputVisible && user?.user_metadata.user_name === login && (
-

- You don't have any highlights yet!
Highlights are a great way to show off your - contributions. Merge any new pull requests recently? -

- {!inputVisible && ( - + {user?.user_metadata.user_name === login ? ( + <> +

+ You don't have any highlights yet!
Highlights are a great way to show off your + contributions. Merge any new pull requests recently? +

+ {!inputVisible && ( + + )} + + ) : ( + <> +

{` ${login} hasn't posted any highlights yet!`}

+ )}
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index a7b07784cd..00e31dce62 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -33,7 +33,6 @@ "@supabase/gotrue-js": "^1.22.22", "@supabase/supabase-js": "^2.7.0", "@supabase/ui": "^0.36.5", - "class-variance-authority": "^0.4.0", "clsx": "^1.2.1", "date-fns": "^2.29.3", "echarts": "^5.4.1", @@ -50,7 +49,6 @@ "react-spring": "^9.6.1", "stripe": "^11.8.0", "swr": "^2.0.3", - "tailwind-merge": "^1.9.1", "tailwindcss-radix": "^2.7.0", "zustand": "^4.3.2" }, @@ -17502,22 +17500,6 @@ "node": ">=0.10.0" } }, - "node_modules/class-variance-authority": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.4.0.tgz", - "integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==", - "funding": { - "url": "https://joebell.co.uk" - }, - "peerDependencies": { - "typescript": ">= 4.5.5 < 5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -32252,11 +32234,6 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, - "node_modules/tailwind-merge": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.9.1.tgz", - "integrity": "sha512-ED9MkiUHlmfh58EC1xHRqXcH1IQyRtmDP0AmXlugYkP2tvfu7ejtjFEHJLJt93mQ7ZJkcqSIgm9M394bq5vOJg==" - }, "node_modules/tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", From 98151029ad6be023329235b63d292744207c25f4 Mon Sep 17 00:00:00 2001 From: Sunday Ogbonna Date: Mon, 13 Feb 2023 20:49:42 +0100 Subject: [PATCH 15/33] refactor: implement responsiveness on user profile --- .../contributor-highlight-card.tsx | 8 +- .../contributor-profile-header.tsx | 7 +- .../pull-request-highlight-card.tsx | 15 +-- .../contributor-profile-page.tsx | 91 ++++++++++++++++--- .../contributor-profile-tab.tsx | 8 +- 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx index 8494d58c99..aae2eacdbc 100644 --- a/components/molecules/ContributorHighlight/contributor-highlight-card.tsx +++ b/components/molecules/ContributorHighlight/contributor-highlight-card.tsx @@ -25,14 +25,14 @@ const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightC const { data, isLoading, isError } = useFetchGithubPRInfo(generateApiPrUrl(prLink)); return ( -
+
{title && ( - + <Title className="!text-sm lg:!text-xl !text-light-slate-12" level={4}> {title} )} -
+
@@ -83,7 +83,7 @@ const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightC {/* Highlight body section */}
-

{desc}

+

{desc}

{/* Highlight Link section */} diff --git a/components/molecules/ContributorProfileHeader/contributor-profile-header.tsx b/components/molecules/ContributorProfileHeader/contributor-profile-header.tsx index 4679a38265..4321f60bdc 100644 --- a/components/molecules/ContributorProfileHeader/contributor-profile-header.tsx +++ b/components/molecules/ContributorProfileHeader/contributor-profile-header.tsx @@ -19,11 +19,14 @@ const ContributorProfileHeader = ({ avatarUrl, githubName, isConnected }: Contri )}
-
+
+
+ +
{isConnected && ( -
+
diff --git a/components/molecules/HighlightInput/highlight-input-form.tsx b/components/molecules/HighlightInput/highlight-input-form.tsx index 260b18e76d..75bcdd1de1 100644 --- a/components/molecules/HighlightInput/highlight-input-form.tsx +++ b/components/molecules/HighlightInput/highlight-input-form.tsx @@ -4,6 +4,7 @@ import { createHighlights } from "lib/hooks/createHighlights"; import { ToastTrigger } from "lib/utils/toast-trigger"; import { ChangeEvent, useEffect, useRef, useState } from "react"; +import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card"; const HighlightInputForm = (): JSX.Element => { const [isDivFocused, setIsDivFocused] = useState(false); @@ -11,6 +12,7 @@ const HighlightInputForm = (): JSX.Element => { const [bodyText, setBodyText] = useState(""); const [row, setRow] = useState(1); const [title, setTitle] = useState(""); + const [pullrequestLink, setPullRequestLink] = useState(""); const ref = useRef(null); let rowLomit = 5; let messageLastScrollHeight = textAreaRef.current ? textAreaRef.current?.scrollHeight : 50; @@ -24,12 +26,18 @@ const HighlightInputForm = (): JSX.Element => { } }; document.addEventListener("mousedown", checkIfClickedOutside); + const pullLink = bodyText.match(/\bhttps?:\/\/\S+/gi); + if (pullLink && pullLink.length > 0) { + setPullRequestLink(pullLink[0]); + } else { + setPullRequestLink(""); + } return () => { // Cleanup the event listener document.removeEventListener("mousedown", checkIfClickedOutside); }; - }, [isDivFocused, bodyText]); + }, [isDivFocused, bodyText, pullrequestLink]); const handleTextAreaInputChange = (e: ChangeEvent) => { setBodyText(e.target.value); @@ -38,6 +46,10 @@ const HighlightInputForm = (): JSX.Element => { } else if (row > 1 && textAreaRef.current && textAreaRef.current?.scrollHeight < messageLastScrollHeight) { setRow((prev) => prev--); } + // const pullLink = bodyText.match(/\bhttps?:\/\/\S+/gi); + // if (pullLink && pullLink.length > 0) { + // setPullRequestLink(pullLink[0]); + // } if (!bodyText) setRow(1); messageLastScrollHeight = textAreaRef.current?.scrollHeight || 60; @@ -99,6 +111,8 @@ const HighlightInputForm = (): JSX.Element => { />
+ {pullrequestLink && isDivFocused && } + {isDivFocused && (