Skip to content

Commit 5b4f2d9

Browse files
authored
Merge pull request #1187 from dacadeorg/ft/extend-user-referral-view
feat: extend the referral view on the user profile
2 parents f58ec00 + 0a398cf commit 5b4f2d9

File tree

12 files changed

+117
-74
lines changed

12 files changed

+117
-74
lines changed

public/locales/bg/common.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,13 @@
323323
"referrals.empty-state.title": "No referrals yet",
324324
"referrals.empty-state.subtitle": "Once someone uses your referral code to signup, they will showup here",
325325
"referrals.joined": "Joined",
326-
"referrals.challenge-participation": "Participated in a challenge in",
327-
"referrals.submission.evaluation": "Submission evaluated",
326+
"referrals.challenge-participation": "Participated in a challenge",
327+
"referrals.challenge.evaluation.status.passed": "Успешно преминал",
328+
"referrals.challenge.evaluation.status.failed": "Не премина",
329+
"referrals.challenge.evaluation..status.pending": "Участвал в",
330+
"referrals.challenge.evaluation.pending": "Изчаква оценка ...",
331+
"referrals.submission.evaluation": "Прегледан принос",
332+
"referrals.submission.challenge": "предизвикателство",
328333
"referrals.submission.points": "points",
329334
"referrals.reward.text": "You earned the referral reward",
330335
"submissions.empty-state.title": "No submissions yet.",

public/locales/en/common.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,12 @@
353353
"referrals.empty-state.subtitle": "Once someone uses your referral code to signup, they will showup here",
354354
"referrals.joined": "Joined",
355355
"referrals.challenge-participation": "Participated in a challenge in",
356+
"referrals.challenge.evaluation.status.passed": "Successfully passed",
357+
"referrals.challenge.evaluation.status.failed": "Did not pass",
358+
"referrals.challenge.evaluation.status.pending": "Participated in",
359+
"referrals.challenge.evaluation.pending": "Pending evaluation ...",
356360
"referrals.submission.evaluation": "Submission evaluated",
361+
"referrals.submission.challenge": "challenge",
357362
"referrals.submission.points": "points",
358363
"referrals.reward.text": "You earned the referral reward",
359364
"submissions.empty-state.title": "No submissions yet.",

public/locales/es/common.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,12 @@
324324
"referrals.empty-state.title": "No hay recomendaciones",
325325
"referrals.joined": "Registro completo",
326326
"referrals.challenge-participation": "Participó en un reto en",
327+
"referrals.challenge.evaluation.status.passed": "Successfully passed",
328+
"referrals.challenge.evaluation.status.failed": "No pasó",
329+
"referrals.challenge.evaluation.status.pending": "Participó en",
330+
"referrals.challenge.evaluation.pending": "Pendiente de evaluación ...",
327331
"referrals.submission.evaluation": "Propuestas evaluadas",
332+
"referrals.submission.challenge": "desafío",
328333
"referrals.submission.points": "puntos",
329334
"referrals.reward.text": "Ha ganado una recompensa por recomendación",
330335
"submissions.empty-state.title": "No hay propuestas todavía.",

public/locales/hr/common.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,12 @@
324324
"referrals.empty-state.subtitle": "Once someone uses your referral code to signup, they will showup here",
325325
"referrals.joined": "Joined",
326326
"referrals.challenge-participation": "Participated in a challenge in",
327-
"referrals.submission.evaluation": "Submission evaluated",
327+
"referrals.challenge.evaluation.status.passed": "Successfully passed",
328+
"referrals.challenge.evaluation.status.failed": "Nije prošao",
329+
"referrals.challenge.evaluation.status.pending": "Sudjelovao u",
330+
"referrals.challenge.evaluation.pending": "Na čekanju evaluacije ...",
331+
"referrals.submission.evaluation": "Ocijenjen prilog",
332+
"referrals.submission.challenge": "izazov",
328333
"referrals.submission.points": "points",
329334
"referrals.reward.text": "You earned the referral reward",
330335
"submissions.empty-state.title": "No submissions yet.",

src/components/cards/Referral.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Avatar from "@/components/ui/Avatar";
2-
import { Reward as IReward } from "@/types/course";
3-
import { Community } from "@/types/community";
2+
import { Referral as TReferral } from "@/types/community";
43
import RewardBadge from "../badges/RewardBadge";
54
import { useTranslation } from "next-i18next";
65
import { ReactElement } from "react";
@@ -16,11 +15,7 @@ import { useDispatch } from "@/hooks/useTypedDispatch";
1615
*/
1716

1817
interface ReferralProps {
19-
referral: {
20-
title?: string;
21-
community: Community;
22-
reward: IReward;
23-
};
18+
referral: TReferral;
2419
}
2520

2621
/**
Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { ReactElement, useMemo } from "react";
22
import Avatar from "@/components/ui/Avatar";
3-
import RewardBadge from "@/components/badges/RewardBadge";
43
import DateManager from "@/utilities/DateManager";
54
import { useTranslation } from "next-i18next";
65
import { useRouter } from "next/router";
76
import { Referral as ReferralType } from "@/types/community";
7+
import Link from "next/link";
8+
import TimeIcon from "@/icons/time.svg";
9+
import RewardBadge from "@/components/badges/RewardBadge";
10+
import { Evaluation, Submission } from "@/types/bounty";
811

912
interface ReferralProps {
1013
referral: ReferralType;
@@ -25,54 +28,58 @@ export default function Referral({ referral }: ReferralProps): ReactElement {
2528
const { t } = useTranslation();
2629
const { locale } = useRouter();
2730
const joinedAt = useMemo(() => DateManager.fromNow(referral.created_at, locale), [locale, referral.created_at]);
28-
const participatedAt = useMemo(() => (referral.submission ? DateManager.fromNow(referral.submission.created_at, locale) : null), [locale, referral.submission]);
2931
const rewardAt = useMemo(() => (referral.rewarded ? DateManager.fromNow(referral.updated_at, locale) : null), [locale, referral.rewarded, referral.updated_at]);
32+
const challengeLink = (submission: Submission) => `/communities/${submission.community.slug}/challenges/${submission.challenge.id}/submissions/${submission.id}`
33+
const status = (evaluation: Evaluation) => {
34+
if (!evaluation) return t("referrals.challenge.evaluation.status.pending")
35+
return t(evaluation?.reward ? "referrals.challenge.evaluation.status.passed" : "referrals.challenge.evaluation.status.failed")
36+
}
3037

38+
const formatDate = (date: Date) => {
39+
return date ? DateManager.fromNow(date, locale) : null
40+
}
3141
return (
32-
<div className="text-sm text-gray-700 bg-gray-50 md:mb-0">
33-
<div className="flex p-7">
34-
<div className="">
42+
<div className="text-sm text-gray-700 bg-gray-50 md:mb-0 p-7">
43+
<div className="w-full">
44+
<div className="flex gap-4 ">
3545
<Avatar size="large" user={referral.user} hideVerificationBadge />
36-
</div>
37-
<div className="ml-5">
38-
<span className="pb-1 text-lg font-medium leading-loose text-gray-900">{referral.user?.displayName}</span>
39-
<p>
40-
{t("referrals.joined")} {joinedAt}
46+
<p className="grid">
47+
<span className="pb-1 text-lg font-medium leading-loose text-gray-900">{referral.user?.displayName}</span>
48+
<span className="text-gray-500">{t("referrals.joined")} {joinedAt}</span>
4149
</p>
50+
</div>
51+
<div className="flex-grow md:ml-19">
4252
<div className="pt-1">
43-
<ul className="pb-1 text-sm font-light leading-loose text-gray-700 list">
44-
{referral.challenge && referral.community && (
45-
<li>
46-
<span className="ml-5">{t("referrals.challenge-participation")}</span>
47-
<span className="font-bold">{referral.community.name}</span>
48-
<span className="hidden md:inline-block">{participatedAt}</span>
49-
</li>
50-
)}
51-
52-
{referral.submission && referral.submission.metadata && referral.submission.metadata.evaluation && (
53-
<li>
54-
<span className="ml-5">{t("referrals.submission.evaluation")}</span>
55-
<span className="font-bold">
56-
{referral.submission.metadata.evaluation.points}/{referral.submission.metadata.evaluation.totalPoints}
53+
<ul className="pb-1 font-light leading-loose text-gray-700 grid divide-y-2 space-y-4 divide-gray-200">
54+
{referral?.submissions?.length ? referral.submissions.map((submission) => (
55+
<li key={submission.id} className="grid md:flex justify-between pt-4 gap-2 md:gap-0">
56+
<span className="grid gap-2">
57+
<span>
58+
{status(submission.metadata.evaluation)} <Link href={challengeLink(submission)} className="font-bold underline text-base- underline-offset-2">{submission.challenge.name} {t("referrals.submission.challenge")}</Link>
59+
</span>
60+
{!submission?.metadata?.evaluation && <span className="mr-0 flex items-center gap-2.5 leading-none "> <TimeIcon />{t("referrals.challenge.evaluation.pending")}</span>}
5761
</span>
58-
{t("referrals.submission.points")}
59-
<span className="hidden md:inline-block">{rewardAt}</span>
62+
<span className="text-gray-500">{formatDate(submission.updated_at)}</span>
6063
</li>
61-
)}
64+
)
65+
) : <></>}
6266

63-
{referral.rewarded && referral.metadata && referral.metadata.reward && (
64-
<li>
65-
<span className="ml-5">{t("referrals.reward.text")}</span>
66-
<span className="font-bold">
67-
<RewardBadge type="gray" reward={referral.metadata.reward} />
67+
{referral?.rewarded && referral.metadata && referral.metadata.reward && (
68+
<li className="pt-4 grid gap-2 md:gap-0 md:flex md:justify-between">
69+
<span className="grid md:gap-2 md:flex items-center">
70+
<span>{t("referrals.reward.text")}</span>
71+
<span className="font-bold flex justify-start">
72+
<RewardBadge type="gray" reward={referral.metadata.reward} />
73+
</span>
6874
</span>
69-
<span className="hidden md:inline-block">{rewardAt}</span>
75+
76+
<span className="text-gray-500">{rewardAt}</span>
7077
</li>
7178
)}
7279
</ul>
7380
</div>
74-
</div>
75-
</div>
76-
</div>
81+
</div >
82+
</div >
83+
</div >
7784
);
7885
}

src/components/sections/profile/Navigation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export default function ProfileNagivation(): ReactElement {
9898
const isCurrentMenuItem = (item: MenuItem) => item.link === router.asPath || (router.asPath === "/profile" && item.label.toLocaleLowerCase() === "overview");
9999

100100
return (
101-
<ul className="relative hidden lg:block xl:block">
101+
<ul className="relative block ml-5 md:ml-0">
102102
{menus.map((menu, i) => (
103103
<li key={`profile-menu-${i}`} className="mb-2 relative">
104104
<ProfileOverviewSection title={menu.title}>

src/pages/profile/referrals.tsx

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import ProfileLayout from "@/layouts/ProfileLayout";
88
import { userFetchReferrals } from "@/store/services/referrals.service";
99

1010
import Referral from "@/components/cards/profile/Referral";
11-
import EmptyState from "@/components/ui/EmptyState";
1211
import InfiniteScroll from "react-infinite-scroll-component";
1312
import AuthObserver from "@/contexts/AuthObserver";
1413
import { User } from "@/types/bounty";
1514
import { Referral as ReferralType } from "@/types/community";
1615
import { IRootState } from "@/store";
1716
import Loader from "@/components/ui/button/Loader";
17+
import ProfileOverviewSection from "@/components/sections/profile/overview/Section";
18+
import ReferralsList from "@/components/list/ReferralsList";
19+
import Spinner from "@/components/ui/Loader";
20+
1821

1922
/**
2023
* interface for UserReferrals multiSelector
@@ -27,6 +30,7 @@ interface UserReferralsMultiSelector {
2730
user: User | null;
2831
referrals: ReferralType[];
2932
hasMore: boolean;
33+
loading: boolean
3034
}
3135

3236
/**
@@ -38,11 +42,12 @@ interface UserReferralsMultiSelector {
3842
export default function UserReferrals(): ReactElement {
3943
const { t } = useTranslation();
4044
const [page, setPage] = useState(0);
41-
const [loading, setLoading] = useState(false);
42-
const { user, referrals, hasMore } = useMultiSelector<unknown, UserReferralsMultiSelector>({
45+
const [isLoadingMore, setIsLoadingMore] = useState(false);
46+
const { user, referrals, hasMore, loading } = useMultiSelector<unknown, UserReferralsMultiSelector>({
4347
user: (state: IRootState) => state.user.data,
4448
referrals: (state: IRootState) => state.userReferrals.userReferralList,
4549
hasMore: (state: IRootState) => state.userReferrals.hasMore,
50+
loading: (state: IRootState) => state.userReferrals.loading
4651
});
4752
const dispatch = useDispatch();
4853
const showLoadMore = useMemo(() => hasMore && referrals?.length >= 10, [referrals?.length, hasMore]);
@@ -54,35 +59,41 @@ export default function UserReferrals(): ReactElement {
5459
}, [dispatch, user?.referrals]);
5560

5661
const nextPage = async () => {
57-
if (loading || !hasMore) return;
58-
setLoading(true);
62+
if (isLoadingMore || !hasMore) return;
63+
setIsLoadingMore(true);
5964
const referralId = referrals[referrals.length - 1]?.id || null;
6065
await dispatch(userFetchReferrals({ startAfter: referralId || null }));
6166
setPage(page + 1);
62-
setLoading(false);
67+
setIsLoadingMore(false);
6368
};
69+
if (loading && !referrals.length) return <Spinner className="py-32" />;
6470

6571
return (
66-
<div className="w-full">
67-
{referrals.length ? (
72+
<div className="grid gap-7.5">
73+
<ReferralsList text="Invite your friends to Dacade" />
74+
<ProfileOverviewSection title="people that have used your invite code">
6875
<div className="relative w-full">
69-
<InfiniteScroll
70-
className="flex flex-col w-full overflow-hidden border border-gray-200 border-solid divide-y divide-gray-200 rounded-3xl divide-solid"
71-
dataLength={referrals.length}
72-
next={nextPage}
73-
hasMore={showLoadMore}
74-
// loader is required for InfiniteScroll to work
75-
loader={<></>}
76-
>
77-
{referrals.map((referral, i) => (
78-
<Referral key={`user-referral-${i}`} referral={referral} />
79-
))}
80-
</InfiniteScroll>
81-
{loading && <Loader loading={loading} className="sm:absolute sm:left-6 sm:-bottom-7.5" onClick={() => nextPage()} />}
76+
{referrals.length ? (
77+
<div className="w-full">
78+
<InfiniteScroll
79+
className="flex flex-col w-full overflow-hidden border border-gray-200 border-solid divide-y divide-gray-200 rounded-3xl divide-solid"
80+
dataLength={referrals.length}
81+
next={nextPage}
82+
hasMore={showLoadMore}
83+
// loader is required for InfiniteScroll to work
84+
loader={<></>}
85+
>
86+
{referrals.map((referral, i) => (
87+
<Referral key={`user-referral-${i}`} referral={referral} />
88+
))}
89+
</InfiniteScroll>
90+
{isLoadingMore && <Loader loading={isLoadingMore} className="absolute left-6 -bottom-7.5" onClick={() => nextPage()} />}
91+
</div>
92+
) : (
93+
<div className="w-full border bg-gray-50 border-gray-200 border-solid rounded-3xl text-gray-500 p-6.5 font-semibold">{t('referrals.empty-state.subtitle')}</div>
94+
)}
8295
</div>
83-
) : (
84-
<EmptyState title={t("referrals.empty-state.title")} subtitle={t("referrals.empty-state.subtitle")} />
85-
)}
96+
</ProfileOverviewSection>
8697
</div>
8798
);
8899
}

src/store/feature/user/referrals.slice.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ interface UserReferralsState {
1010
current: Referral | null;
1111
hasMore: boolean;
1212
count: number;
13+
loading: boolean;
1314
}
1415

1516
const initialState: UserReferralsState = {
1617
userReferralList: [],
1718
current: null,
1819
hasMore: true,
1920
count: 0,
21+
loading: false,
2022
};
2123

2224
/**
@@ -40,12 +42,16 @@ const userReferralsSlice = createSlice({
4042
setHasMoreReferrals: (state, action) => {
4143
state.hasMore = action.payload;
4244
},
45+
4346
setCount: (state, action) => {
4447
state.count = action.payload;
4548
},
49+
setLoading: (state, action) => {
50+
state.loading = action.payload;
51+
},
4652
},
4753
});
4854

49-
export const { setCurrent, clear, setUserReferralsList, setHasMoreReferrals, setCount } = userReferralsSlice.actions;
55+
export const { setCurrent, clear, setUserReferralsList, setHasMoreReferrals, setCount, setLoading } = userReferralsSlice.actions;
5056

5157
export default userReferralsSlice;

src/store/services/referrals.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import baseQuery from "@/config/baseQuery";
22
import { createApi } from "@reduxjs/toolkit/dist/query";
33
import { setReferralsList } from "../feature/referrals.slice";
4-
import { clear, setUserReferralsList, setHasMoreReferrals, setCount } from "../feature/user/referrals.slice";
4+
import { clear, setUserReferralsList, setHasMoreReferrals, setCount, setLoading } from "../feature/user/referrals.slice";
55
import { Referral } from "@/types/community";
66

77
/**
@@ -35,6 +35,7 @@ const referralsService = createApi({
3535
onQueryStarted: async ({ startAfter }, { dispatch, queryFulfilled, getState }) => {
3636
const state = getState() as any;
3737
try {
38+
dispatch(setLoading(true))
3839
const { data } = await queryFulfilled;
3940
const list: Referral[] = [];
4041
if (startAfter) {
@@ -47,6 +48,8 @@ const referralsService = createApi({
4748
dispatch(setUserReferralsList(list));
4849
dispatch(setCount(data?.count));
4950
dispatch(setHasMoreReferrals(data?.list?.length > 0 ? true : false));
51+
dispatch(setLoading(false))
52+
5053
} catch (error) {
5154
console.log("error in fetching the userFetchReferrals ", error);
5255
}

0 commit comments

Comments
 (0)