Skip to content

Commit 9269f1c

Browse files
authored
feat: new insights tab layout for list feature (#1682)
1 parent 50cd629 commit 9269f1c

File tree

11 files changed

+316
-121
lines changed

11 files changed

+316
-121
lines changed

components/atoms/Tabs/tabs.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const TabsTrigger = React.forwardRef<
1919
>(({ className, ...props }, ref) => (
2020
<TabsPrimitive.Trigger
2121
className={clsx(
22-
className,
23-
"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"
22+
"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",
23+
className
2424
)}
2525
{...props}
2626
ref={ref}

components/molecules/ListCard/list-card.tsx

+21-34
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,43 @@ import { FiPlus } from "react-icons/fi";
55
import { RiDeleteBinLine } from "react-icons/ri";
66
import Text from "components/atoms/Typography/text";
77
import Tooltip from "components/atoms/Tooltip/tooltip";
8+
import { useFetchListContributors } from "lib/hooks/useList";
89
import StackedAvatar, { Contributor } from "../StackedAvatar/stacked-avatar";
910

1011
interface ListCardProps {
1112
list: DbUserList;
1213
}
1314
const ListCard = ({ list }: ListCardProps) => {
14-
const dummyContributorsCount = 105;
15-
const dummyContributors: Contributor[] = [
16-
{
17-
host_login: "bdougie",
18-
},
19-
{
20-
host_login: "brandonroberts",
21-
},
22-
{
23-
host_login: "ritadee",
24-
},
25-
{
26-
host_login: "ogdev-01",
27-
},
28-
{
29-
host_login: "jpmcb",
30-
},
31-
{
32-
host_login: "divyanshu013",
33-
},
34-
];
15+
const { data: contributors } = useFetchListContributors(list.id);
16+
17+
const contributorsAvatar: Contributor[] = contributors?.map((contributor) => ({
18+
host_login: contributor.login,
19+
}));
20+
21+
const dummyContributorsCount = contributors?.length ?? 0;
22+
3523
return (
3624
<div>
37-
<div className="flex flex-col items-start w-full gap-4 px-4 py-5 bg-white rounded-lg md:items-center md:justify-between md:flex-row lg:px-8 lg:gap-2">
25+
<div className="flex flex-col items-start w-full gap-4 px-4 py-6 bg-white rounded-lg md:items-center md:justify-between md:flex-row lg:px-8 lg:gap-2">
3826
<div className="flex flex-col flex-1 gap-4 lg:gap-6">
3927
<div className="flex items-center gap-4 lg:items-center ">
4028
<div className="w-4 h-4 rounded-full bg-light-orange-10"></div>
4129
<div className="flex justify-between text-xl text-light-slate-12">
42-
<Link href={`/list/${list.user.login}/${list.id}/overview`}>{list.name}</Link>
30+
<Link href={`/lists/${list.id}/overview`}>{list.name}</Link>
4331
</div>
4432
<div className="px-2 border rounded-2xl text-light-slate-11">{!!list.is_public ? "public" : "private"}</div>
4533
</div>
4634
</div>
4735
<div className="">
4836
<div className="flex items-center justify-end w-full gap-8">
4937
{/* Contributors section */}
50-
<div className="flex flex-col flex-1 gap-2 mr-2">
38+
<div className="flex flex-col items-center flex-1 gap-1 mr-2">
5139
<span className="text-xs text-light-slate-11">Contributors</span>
52-
<Text className="flex items-center text-xl">{dummyContributorsCount}</Text>
40+
<Text className="flex items-center text-2xl">{dummyContributorsCount}</Text>
5341
</div>
5442

5543
<div className="flex items-center">
56-
<StackedAvatar contributors={dummyContributors} visibleQuantity={6} classNames="scale-125" />
44+
<StackedAvatar contributors={contributorsAvatar} visibleQuantity={6} classNames="scale-125" />
5745
<Tooltip content="Add more contributors">
5846
<button
5947
className="z-50 w-8 h-8 overflow-hidden scale-110 bg-white border-2 border-white rounded-full"
@@ -67,15 +55,14 @@ const ListCard = ({ list }: ListCardProps) => {
6755
</div>
6856
<div className="justify-end flex-1 hidden md:flex">
6957
{/* Delete button */}
70-
<button type="button">
71-
<span className=" bg-light-slate-1 inline-block rounded-lg p-2.5 border mr-2 cursor-pointer">
72-
<RiDeleteBinLine title="Edit Insight Page" className="text-lg text-light-slate-10" />
73-
</span>
58+
<button className="inline-block p-3 mr-2 border rounded-lg cursor-pointer bg-light-slate-1" type="button">
59+
<RiDeleteBinLine title="Edit Insight Page" className="text-lg text-light-slate-10" />
7460
</button>
75-
<Link href={`/list/${list.user.login}/${list.id}/overview`}>
76-
<span className=" bg-light-slate-1 inline-block rounded-lg p-2.5 border cursor-pointer">
77-
<MdOutlineArrowForwardIos title="Go To Insight Page" className="text-lg text-light-slate-10" />
78-
</span>
61+
<Link
62+
className="inline-block p-3 mr-2 border rounded-lg cursor-pointer bg-light-slate-1"
63+
href={`/lists/${list.id}/overview`}
64+
>
65+
<MdOutlineArrowForwardIos title="Go To Insight Page" className="text-lg text-light-slate-10" />
7966
</Link>
8067
</div>
8168
</div>

layouts/hub-page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const HubPageLayout = ({ children }: { children: React.ReactNode }) => {
2727
return (
2828
<div className="flex flex-col min-h-screen">
2929
<TopNav />
30-
<div className="page-container flex grow bg-light-slate-3 flex-col items-center pt-20 md:pt-14">
30+
<div className="flex flex-col items-center pt-20 page-container grow bg-light-slate-3 md:pt-14">
3131
<div className="info-container container w-full min-h-[100px]">
3232
<Header>
3333
{insight ? (

layouts/hub.tsx

+86-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,97 @@
1-
import React from "react";
1+
import React, { useEffect } from "react";
22

3+
import { useRouter } from "next/router";
4+
import Link from "next/link";
5+
import clsx from "clsx";
6+
7+
import useSession from "lib/hooks/useSession";
8+
import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
9+
import { supabase } from "lib/utils/supabase";
10+
11+
import Title from "components/atoms/Typography/title";
12+
import Search from "components/atoms/Search/search";
13+
import Button from "components/atoms/Button/button";
314
import Footer from "../components/organisms/Footer/footer";
415
import TopNav from "../components/organisms/TopNav/top-nav";
516

617
const HubLayout = ({ children }: { children: React.ReactNode }) => {
18+
const { onboarded } = useSession();
19+
const { user } = useSupabaseAuth();
20+
const navLinks = [
21+
{ name: "Insights", href: "/hub/insights" },
22+
{ name: "Lists", href: "/hub/lists" },
23+
];
24+
const router = useRouter();
25+
26+
useEffect(() => {
27+
async function getUser() {
28+
try {
29+
const currentUser = await supabase.auth.getSession();
30+
31+
if (!currentUser?.data?.session || onboarded === false) {
32+
await router.push("/feed");
33+
}
34+
} catch (e: unknown) {
35+
router.push("/feed");
36+
}
37+
}
38+
39+
getUser()
40+
.catch(console.error)
41+
.then(() => {});
42+
}, [router, onboarded]);
43+
44+
const getActiveLinkClassNames = (href: string) => {
45+
return router.pathname === href ? "text-light-slate-11" : "text-slate-300";
46+
};
47+
748
return (
849
<div className="flex flex-col min-h-screen">
950
<TopNav />
10-
<div className="page-container flex grow flex-col items-center pt-20 md:pt-14">
11-
<main className="flex w-full flex-1 flex-col items-center px-3 md:px-2 py-8 bg-light-slate-2">
12-
<div className="container mx-auto px-2 md:px-16">{children}</div>
51+
<div className="flex flex-col items-center pt-20 page-container grow md:pt-14">
52+
<main className="flex flex-col items-center flex-1 w-full px-3 py-8 md:px-2 bg-light-slate-2">
53+
{user && onboarded ? (
54+
<div className="container px-2 mx-auto md:px-16">
55+
<div className="container flex flex-col w-full gap-4 py-2">
56+
<Title className="-mb-6 text-base text-sauced-orange">Your pages</Title>
57+
58+
<nav className="items-center justify-between block py-2 sm:flex ">
59+
<ul className="flex items-center gap-3">
60+
{navLinks.map((link, index) => (
61+
<li key={`hub-nav-${index}-${link.name}`}>
62+
<Link
63+
className={clsx("text-3xl leading-none font-medium mx-0", getActiveLinkClassNames(link.href))}
64+
href={link.href}
65+
>
66+
{link.name}
67+
</Link>
68+
</li>
69+
))}
70+
</ul>
71+
72+
<div className="flex items-center gap-3 mt-4">
73+
{/* Search box temporarily hidden */}
74+
<div className="hidden w-58">
75+
<Search placeholder="Search repositories" className="max-w-full" name={"query"} />
76+
</div>
77+
{router.pathname.split("/")[2] === "insights" ? (
78+
<Button href="/hub/insights/new" variant="primary">
79+
Add Insight Page
80+
</Button>
81+
) : (
82+
<Button href="/hub/lists/new" variant="primary">
83+
Add List
84+
</Button>
85+
)}
86+
</div>
87+
</nav>
88+
</div>
89+
90+
{children}
91+
</div>
92+
) : (
93+
<></>
94+
)}
1395
</main>
1496
</div>
1597
<Footer />

layouts/lists.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TopNav from "components/organisms/TopNav/top-nav";
88
import ListHeader from "components/ListHeader/list-header";
99
import TabsList from "components/TabList/tab-list";
1010

11-
import useList from "lib/hooks/useList";
11+
import { useList } from "lib/hooks/useList";
1212
import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
1313

1414
const ListPageLayout = ({ children }: { children: React.ReactNode }) => {
@@ -32,7 +32,7 @@ const ListPageLayout = ({ children }: { children: React.ReactNode }) => {
3232
<div className="flex flex-col min-h-screen">
3333
<TopNav />
3434

35-
<div className="page-container flex grow bg-light-slate-3 flex-col items-center pt-20 md:pt-14">
35+
<div className="flex flex-col items-center pt-20 page-container grow bg-light-slate-3 md:pt-14">
3636
<div className="info-container container w-full min-h-[100px]">
3737
<Header>
3838
{list ? (

lib/hooks/useList.ts

+71-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,73 @@
1-
// import useSWR, { Fetcher } from "swr";
2-
// import publicApiFetcher from "lib/utils/public-api-fetcher";
1+
import useSWR, { Fetcher } from "swr";
2+
import { useState } from "react";
3+
4+
import publicApiFetcher from "lib/utils/public-api-fetcher";
5+
6+
interface PaginatedListResponse {
7+
data: DbUserList[];
8+
meta: Meta;
9+
}
10+
11+
interface PaginatedListContributorsResponse {
12+
data: DbListContibutor[];
13+
meta: Meta;
14+
}
15+
16+
const useFetchAllLists = (range = 30) => {
17+
const [page, setPage] = useState(1);
18+
const [limit, setLimit] = useState(10);
19+
20+
const query = new URLSearchParams();
21+
22+
query.set("page", `${page}`);
23+
query.set("limit", `${limit}`);
24+
query.set("range", `${range}`);
25+
26+
const baseEndpoint = `lists?${query.toString()}`;
27+
const endpointString = `${baseEndpoint}`;
28+
29+
const { data, error, mutate } = useSWR<PaginatedListResponse, Error>(
30+
endpointString,
31+
publicApiFetcher as Fetcher<PaginatedListResponse, Error>
32+
);
33+
34+
return {
35+
data: data?.data ?? [],
36+
isLoading: !error && !data,
37+
isError: !!error,
38+
meta: data?.meta ?? { itemCount: 0, limit: 0, page: 0, hasNextPage: false, hasPreviousPage: false, pageCount: 0 },
39+
mutate,
40+
setPage,
41+
setLimit,
42+
};
43+
};
44+
45+
const useFetchListContributors = (id: string, range = 30) => {
46+
const [page, setPage] = useState(1);
47+
const [limit, setLimit] = useState(10);
48+
49+
const query = new URLSearchParams();
50+
51+
query.set("page", `${page}`);
52+
query.set("limit", `${limit}`);
53+
query.set("range", `${range}`);
54+
const endpointString = `lists/${id}/contributors?${query.toString()}`;
55+
56+
const { data, error, mutate } = useSWR<PaginatedListContributorsResponse, Error>(
57+
id ? endpointString : null,
58+
publicApiFetcher as Fetcher<PaginatedListContributorsResponse, Error>
59+
);
60+
61+
return {
62+
data: data?.data ?? [],
63+
isLoading: !error && !data,
64+
isError: !!error,
65+
meta: data?.meta ?? { itemCount: 0, limit: 0, page: 0, hasNextPage: false, hasPreviousPage: false, pageCount: 0 },
66+
mutate,
67+
setPage,
68+
setLimit,
69+
};
70+
};
371

472
const useList = (id: string) => {
573
const baseEndpoint = `lists/${id}`;
@@ -27,4 +95,4 @@ const useList = (id: string) => {
2795
};
2896
};
2997

30-
export default useList;
98+
export { useList, useFetchAllLists, useFetchListContributors };

next-types.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ interface DbListOwner {
289289
readonly login: string;
290290
readonly name: string;
291291
}
292+
293+
interface DbListContibutor {
294+
readonly id: string;
295+
readonly list_id: string;
296+
readonly user_id: string;
297+
readonly login: string;
298+
readonly created_at: string;
299+
}
292300
interface DbUserList {
293301
readonly id: string;
294302
readonly user: DbListOwner;

pages/hub/index.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useRouter } from "next/router";
2+
import React, { useEffect } from "react";
3+
4+
const Hub = () => {
5+
const router = useRouter();
6+
7+
useEffect(() => {
8+
router.push("/hub/insights");
9+
}, []);
10+
11+
return <></>;
12+
};
13+
14+
export default Hub;

0 commit comments

Comments
 (0)