From 03f04e23394d076defe4697c8836c6b99a0d8de0 Mon Sep 17 00:00:00 2001 From: Jason Schwarz Date: Sun, 8 Oct 2023 06:17:35 -0300 Subject: [PATCH 1/5] Added new vercel kv api routes --- app/admin/page.tsx | 25 +++++++++++++++++++ pages/api/vercel/get-all-recent-activity.ts | 16 ++++++++++++ pages/api/vercel/set-recent-activity.ts | 27 +++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 pages/api/vercel/get-all-recent-activity.ts create mode 100644 pages/api/vercel/set-recent-activity.ts diff --git a/app/admin/page.tsx b/app/admin/page.tsx index 3dd6a43..7285a8f 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -79,6 +79,22 @@ const Admin = () => { console.log(data); }; + const setRecentActivity = async () => { + const res = await fetch( + `/api/vercel/set-recent-activity?key=${setterKey}&value=${setterValue}` + ); + const data = await res.json(); + console.log(data); + }; + + const getAllRecentActivity = async () => { + const res = await fetch( + `/api/vercel/get-all-recent-activity?key=${inputValue}` + ); + const data = await res.json(); + console.log(data); + }; + const buttons = [ { name: "flushall", @@ -109,6 +125,15 @@ const Admin = () => { name: "setData", method: () => setData(), }, + { + name: "setRecentActivity", + method: () => setRecentActivity(), + }, + + { + name: "getAllRecentActivity - by parent wallet", + method: () => getAllRecentActivity(), + }, ]; if (!isUnlocked) { diff --git a/pages/api/vercel/get-all-recent-activity.ts b/pages/api/vercel/get-all-recent-activity.ts new file mode 100644 index 0000000..cfe069d --- /dev/null +++ b/pages/api/vercel/get-all-recent-activity.ts @@ -0,0 +1,16 @@ +import { kv } from "@vercel/kv"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + const { key } = req.query as { key: string }; + const data = await kv.hgetall(`activity::${key}`); + return res.status(200).json(data); + } catch (error) { + console.error("Error:", error); + return res.status(500).json({ error: "An error occurred" }); + } +} diff --git a/pages/api/vercel/set-recent-activity.ts b/pages/api/vercel/set-recent-activity.ts new file mode 100644 index 0000000..4a8cab6 --- /dev/null +++ b/pages/api/vercel/set-recent-activity.ts @@ -0,0 +1,27 @@ +import { kv } from "@vercel/kv"; +import { NextApiRequest, NextApiResponse } from "next"; + +//https://redis.io/commands/hset/ + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + const { key, value } = req.query as unknown as { + key: string; + value: string; + }; + + // value = "::" + + const data = await kv.hset(`activity::${key}`, { + [Math.floor(Date.now() / 1000)]: value, + }); + + return res.status(200).json(data); + } catch (error) { + console.error("Error:", error); + return res.status(500).json({ error: "An error occurred" }); + } +} From 36b0fee9d6a0183e3ff2c33717cab421ce899331 Mon Sep 17 00:00:00 2001 From: Jason Schwarz Date: Sun, 8 Oct 2023 06:29:55 -0300 Subject: [PATCH 2/5] Added utils registerActivityEvent helper function --- src/components/forms/RegisterParentForm.tsx | 7 +++++++ src/utils/recentActivity.ts | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/utils/recentActivity.ts diff --git a/src/components/forms/RegisterParentForm.tsx b/src/components/forms/RegisterParentForm.tsx index 43afbb3..f7bb1e6 100644 --- a/src/components/forms/RegisterParentForm.tsx +++ b/src/components/forms/RegisterParentForm.tsx @@ -33,6 +33,7 @@ import shallow from "zustand/shallow"; import { useRouter } from "next/navigation"; import { useAccount } from "wagmi"; import { TestnetNetworks, NetworkType } from "@/data-schema/enums"; +import { registerActivityEvent } from "@/utils/recentActivity"; export const RegisterParentForm = ({ onClose }: { onClose: () => void }) => { //============================================================================= @@ -144,6 +145,12 @@ export const RegisterParentForm = ({ onClose }: { onClose: () => void }) => { setUserDetails(body); setIsLoggedIn(true); + registerActivityEvent( + String(address), + String(address), + "Registered as parent" + ); + const emailSent = await sendEmailConfirmation(); if (!emailSent) { return; diff --git a/src/utils/recentActivity.ts b/src/utils/recentActivity.ts new file mode 100644 index 0000000..b5d5b58 --- /dev/null +++ b/src/utils/recentActivity.ts @@ -0,0 +1,18 @@ +export const registerActivityEvent = async ( + parentAddress: string, + memberAddress: string, + eventDescription: string +) => { + const key = parentAddress; + const value = `${memberAddress}::${eventDescription}`; + + try { + const res = await fetch( + `/api/vercel/set-recent-activity?key=${key}&value=${value}` + ); + const data = await res.json(); + return data; + } catch (e) { + console.error(e); + } +}; From d94e0e8ca1ae7cc53ae4d82b5bb852b9cc35c0e6 Mon Sep 17 00:00:00 2001 From: Jason Schwarz Date: Sun, 8 Oct 2023 06:58:02 -0300 Subject: [PATCH 3/5] Fetched recent activity data on parent dashboard --- app/parent-dashboard/page.tsx | 57 +++++++------------ .../parentDashboard/RecentMemberActivity.tsx | 24 +++++++- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/app/parent-dashboard/page.tsx b/app/parent-dashboard/page.tsx index ebfcd30..dcee3d6 100644 --- a/app/parent-dashboard/page.tsx +++ b/app/parent-dashboard/page.tsx @@ -1,5 +1,5 @@ "use client"; -/* eslint-disable react/no-children-prop */ +/* eslint-disable react/no-members-prop */ import { Box, @@ -40,8 +40,8 @@ const Parent: React.FC = () => { //============================================================================= const [childKey, setChildKey] = useState(0); - const [childrenLoading, setChildrenLoading] = useState(false); - const [children, setChildren] = useState([]); + const [membersLoading, setMembersLoading] = useState(false); + const [members, setMembers] = useState([]); // const [stakeContract, setStakeContract] = useState(); const [familyDetails, setFamilyDetails] = useState({} as User); @@ -84,12 +84,6 @@ const Parent: React.FC = () => { onClose: onCloseEtherScan, } = useDisclosure(); - const { - isOpen: isOpenChildDetails, - onOpen: onOpenChildDetails, - onClose: onCloseChildDetails, - } = useDisclosure(); - const { isOpen: isChangeUsernameOpen, onOpen: onChangeUsernameOpen, @@ -134,16 +128,9 @@ const Parent: React.FC = () => { useEffect(() => { fetchFamilyDetails(); - fetchChildren(); + fetchMembers(); }, []); - // useEffect(() => { - // if (!stakeContract || !children.length) { - // setChildrenStakes({}); - // return; - // } - // }, [stakeContract, children]); - //============================================================================= // FUNCTIONS //============================================================================= @@ -159,30 +146,30 @@ const Parent: React.FC = () => { setFamilyDetails(user); }, [userDetails?.wallet]); - const fetchChildren = useCallback(async () => { - const getChildren = async () => { + const fetchMembers = useCallback(async () => { + const getMembers = async () => { if (!userDetails?.wallet) return; - const children = [] as User[]; + const members = [] as User[]; familyDetails.children?.forEach(async (walletAddress) => { const { data } = await axios.get( `/api/vercel/get-json?key=${walletAddress}` ); - children.push(data as User); + members.push(data as User); }); - if (children.length) { - const childrenWalletBalances = await axios.post( + if (members.length) { + const membersWalletBalances = await axios.post( `/api/etherscan/balancemulti`, { - addresses: children.map((c) => c.wallet), + addresses: members.map((c) => c.wallet), } ); - const childrenWithBalances = children.map((c) => { - const balance = childrenWalletBalances.data.find( + const membersWithBalances = members.map((c) => { + const balance = membersWalletBalances.data.find( (b) => b.account === c.wallet ); return { @@ -191,17 +178,17 @@ const Parent: React.FC = () => { }; }); - // setChildren(childrenWithBalances); + setMembers(membersWithBalances); } else { - // setChildren(children); + setMembers(members); } - setChildrenLoading(false); + setMembersLoading(false); }; - await getChildren(); + await getMembers(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [children.length, userDetails?.wallet]); + }, [members.length, userDetails?.wallet]); const closeTab = () => { setSelectedTab(ParentDashboardTabs.DASHBOARD); @@ -213,7 +200,7 @@ const Parent: React.FC = () => { { bg={useColorModeValue("gray.100", "gray.900")} borderRadius={isMobileSize ? "0" : "10px"} > - + { isOpen={isChangeUsernameOpen} onClose={onChangeUsernameClose} childKey={childKey} - children={children} + children={members} familyId={familyDetails.familyId} - fetchChildren={fetchChildren} + fetchChildren={fetchMembers} fetchFamilyDetails={fetchFamilyDetails} /> diff --git a/src/components/parentDashboard/RecentMemberActivity.tsx b/src/components/parentDashboard/RecentMemberActivity.tsx index a0cd205..a1c692d 100644 --- a/src/components/parentDashboard/RecentMemberActivity.tsx +++ b/src/components/parentDashboard/RecentMemberActivity.tsx @@ -1,4 +1,4 @@ -import { Fragment } from "react"; +import { Fragment, useEffect } from "react"; import { Container, Flex, @@ -11,6 +11,9 @@ import { Heading, Button, } from "@chakra-ui/react"; +import { useAuthStore } from "@/store/auth/authStore"; +import shallow from "zustand/shallow"; +import { User } from "@/data-schema/types"; interface Activity { activity: string; @@ -61,7 +64,24 @@ const memberActivity: Activity[] = [ }, ]; -const RecentMemberActivity = () => { +const RecentMemberActivity = ({ members }: { members: User[] }) => { + console.log("RecentMemberActivity", members); + const { userDetails } = useAuthStore( + (state) => ({ + userDetails: state.userDetails, + }), + shallow + ); + + useEffect(() => { + const getData = async () => { + const res = await fetch(`/api/vercel/get-data?key=${userDetails.wallet}`); + const data = await res.json(); + console.log(data); + }; + getData(); + }, []); + return ( From 448c77827cb2d81ab8e409fad2cdc421d65cc570 Mon Sep 17 00:00:00 2001 From: Jason Schwarz Date: Sun, 8 Oct 2023 08:08:59 -0300 Subject: [PATCH 4/5] Added DB data to activity table --- src/components/AvatarSelection.tsx | 2 +- src/components/CollapsedDashboardMenu.tsx | 5 +- src/components/LoggedInNavbar.tsx | 6 +- src/components/parentDashboard/Avatar.tsx | 2 +- .../parentDashboard/RecentMemberActivity.tsx | 103 ++++++------------ src/utils/dateTime.ts | 7 ++ 6 files changed, 45 insertions(+), 80 deletions(-) diff --git a/src/components/AvatarSelection.tsx b/src/components/AvatarSelection.tsx index 89c3426..04117b9 100644 --- a/src/components/AvatarSelection.tsx +++ b/src/components/AvatarSelection.tsx @@ -159,7 +159,7 @@ export const AvatarSelection = ({ diff --git a/src/components/CollapsedDashboardMenu.tsx b/src/components/CollapsedDashboardMenu.tsx index 361f38e..1910acd 100644 --- a/src/components/CollapsedDashboardMenu.tsx +++ b/src/components/CollapsedDashboardMenu.tsx @@ -66,11 +66,8 @@ export const CollapsedDashboardMenu = ({ name={userDetails?.username} sx={{ fontFamily: "Slackey", - bgColor: `${ - userDetails?.avatarURI ? "transparent" : "purple.500" - }`, }} - src={userDetails?.avatarURI || "/images/placeholder-avatar.jpeg"} + src={userDetails?.avatarURI} /> {userDetails?.username} diff --git a/src/components/LoggedInNavbar.tsx b/src/components/LoggedInNavbar.tsx index cdae809..7e12988 100644 --- a/src/components/LoggedInNavbar.tsx +++ b/src/components/LoggedInNavbar.tsx @@ -52,11 +52,7 @@ export default function LoggedInNavBar() { { setMobileMenuOpen(!mobileMenuOpen); }} diff --git a/src/components/parentDashboard/Avatar.tsx b/src/components/parentDashboard/Avatar.tsx index 5ebadc7..303606e 100644 --- a/src/components/parentDashboard/Avatar.tsx +++ b/src/components/parentDashboard/Avatar.tsx @@ -20,7 +20,7 @@ const ParentAvatar = () => { sx={{ bgColor: `${!userDetails?.avatarURI && "purple.500"}`, }} - src={userDetails?.avatarURI || "/images/placeholder-avatar.jpeg"} + src={userDetails?.avatarURI} /> ); diff --git a/src/components/parentDashboard/RecentMemberActivity.tsx b/src/components/parentDashboard/RecentMemberActivity.tsx index a1c692d..8415061 100644 --- a/src/components/parentDashboard/RecentMemberActivity.tsx +++ b/src/components/parentDashboard/RecentMemberActivity.tsx @@ -1,4 +1,4 @@ -import { Fragment, useEffect } from "react"; +import { Fragment, useEffect, useState } from "react"; import { Container, Flex, @@ -14,58 +14,11 @@ import { import { useAuthStore } from "@/store/auth/authStore"; import shallow from "zustand/shallow"; import { User } from "@/data-schema/types"; - -interface Activity { - activity: string; - dateTime: string; - userName: string; - userAvatar: string; -} - -const memberActivity: Activity[] = [ - { - activity: `Dan Abrahmov Updated Avatar.`, - dateTime: "September 10th at 9:10 AM", - userName: "Dan Abrahmov", - userAvatar: "https://bit.ly/dan-abramov", - }, - { - activity: `Kent Dodds Staked 1.5 ETH.`, - dateTime: "yesterday", - userName: "Kent Dodds", - userAvatar: "https://bit.ly/kent-c-dodds", - }, - { - activity: `Jena Karlis Timelocked 5 ETH.`, - dateTime: "4 days ago", - userName: "Jena Karlis", - userAvatar: - "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=334&q=80", - }, - { - activity: `Jena Karlis Staked 2 ETH.`, - dateTime: "4 days ago", - userName: "Jena Karlis", - userAvatar: - "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=334&q=80", - }, - { - activity: `Jena Karlis Updated Avatar.`, - dateTime: "4 days ago", - userName: "Jena Karlis", - userAvatar: - "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=334&q=80", - }, - { - activity: `Kent Dodds Claimed 1.5 ETH in staking rewards.`, - dateTime: "5 days ago", - userName: "Kent Dodds", - userAvatar: "https://bit.ly/kent-c-dodds", - }, -]; +import { dateInSecondsToLongDate } from "@/utils/dateTime"; const RecentMemberActivity = ({ members }: { members: User[] }) => { - console.log("RecentMemberActivity", members); + const [activity, setActivity] = useState([]); + const { userDetails } = useAuthStore( (state) => ({ userDetails: state.userDetails, @@ -75,12 +28,24 @@ const RecentMemberActivity = ({ members }: { members: User[] }) => { useEffect(() => { const getData = async () => { - const res = await fetch(`/api/vercel/get-data?key=${userDetails.wallet}`); - const data = await res.json(); - console.log(data); + const res = await fetch( + `/api/vercel/get-all-recent-activity?key=${userDetails.wallet}` + ); + let data = await res.json(); + data = Object.entries(data).reverse().slice(0, 6); + setActivity(data); }; + + if (!userDetails.wallet) return; getData(); - }, []); + }, [userDetails.wallet]); + + const parseUser = (wallet: string) => { + const member = members.find((m) => m.wallet === wallet); + const username = member ? member.username : userDetails.username; + const avatar = member ? member.avatarURI : userDetails.avatarURI; + return { username, avatar }; + }; return ( @@ -102,39 +67,39 @@ const RecentMemberActivity = ({ members }: { members: User[] }) => { rounded="md" overflow="hidden" spacing={0} - my="2.5rem" + mb="2.5rem" > - {memberActivity.map((activity, index) => ( + {activity.map(([timestamp, record]: [string, string], index) => ( - + + - {activity.dateTime} + {dateInSecondsToLongDate(timestamp)} - {memberActivity.length - 1 !== index && } + ))} diff --git a/src/utils/dateTime.ts b/src/utils/dateTime.ts index 8a88c65..92b4f20 100644 --- a/src/utils/dateTime.ts +++ b/src/utils/dateTime.ts @@ -6,3 +6,10 @@ export const formatDateToIsoString = (timestamp: number) => { export const timestampInSeconds = (timestampInMilliseconds: number) => Math.floor(timestampInMilliseconds / 1000); + +export const dateInSecondsToLongDate = (date: string) => { + const dateInSeconds = parseInt(date); + const dateInMilliseconds = dateInSeconds * 1000; + const dateObject = new Date(dateInMilliseconds); + return dateObject.toLocaleDateString(); +}; From a593dfa85111866a71d45f2dc0ad19d9ac254266 Mon Sep 17 00:00:00 2001 From: Jason Schwarz Date: Sun, 8 Oct 2023 08:14:19 -0300 Subject: [PATCH 5/5] Fixed build errors --- app/parent-dashboard/page.tsx | 5 ++--- src/components/ExpandedDashboardMenu.tsx | 6 +++--- src/components/modals/UsernameModal.tsx | 4 ++-- src/components/parentDashboard/ButtonMenu.tsx | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/parent-dashboard/page.tsx b/app/parent-dashboard/page.tsx index dcee3d6..a8439c2 100644 --- a/app/parent-dashboard/page.tsx +++ b/app/parent-dashboard/page.tsx @@ -1,5 +1,4 @@ "use client"; -/* eslint-disable react/no-members-prop */ import { Box, @@ -200,7 +199,7 @@ const Parent: React.FC = () => { { isOpen={isChangeUsernameOpen} onClose={onChangeUsernameClose} childKey={childKey} - children={members} + members={members} familyId={familyDetails.familyId} fetchChildren={fetchMembers} fetchFamilyDetails={fetchFamilyDetails} diff --git a/src/components/ExpandedDashboardMenu.tsx b/src/components/ExpandedDashboardMenu.tsx index 6a9c849..c12ea45 100644 --- a/src/components/ExpandedDashboardMenu.tsx +++ b/src/components/ExpandedDashboardMenu.tsx @@ -29,7 +29,7 @@ import { useNetwork } from "wagmi"; export const ExpandedDashboardMenu = ({ familyDetails, - children, + members, onAddChildOpen, setSelectedTab, onToggleCollapsedMenu, @@ -44,7 +44,7 @@ export const ExpandedDashboardMenu = ({ onOpenMembersTableModal, }: { familyDetails: User; - children: User[]; + members: User[]; onAddChildOpen: () => void; setSelectedTab: (tab: ParentDashboardTabs) => void; onToggleCollapsedMenu: () => void; @@ -133,7 +133,7 @@ export const ExpandedDashboardMenu = ({ onOpenSendFundsModal={onOpenSendFundsModal} onOpenNetworkModal={onOpenNetworkModal} onOpenMembersTableModal={onOpenMembersTableModal} - children={children} + members={members} /> diff --git a/src/components/modals/UsernameModal.tsx b/src/components/modals/UsernameModal.tsx index fc5c276..28ea2a5 100644 --- a/src/components/modals/UsernameModal.tsx +++ b/src/components/modals/UsernameModal.tsx @@ -33,7 +33,7 @@ export const UsernameModal = ({ isOpen, onClose, childKey, - children, + members, familyId, fetchChildren, fetchFamilyDetails, @@ -43,7 +43,7 @@ export const UsernameModal = ({ isOpen: boolean; onClose: () => void; childKey?: number; - children?: any; + members?: any; familyId?: string; fetchChildren?: () => void; fetchFamilyDetails?: () => void; diff --git a/src/components/parentDashboard/ButtonMenu.tsx b/src/components/parentDashboard/ButtonMenu.tsx index 5cf5eb9..e7e8844 100644 --- a/src/components/parentDashboard/ButtonMenu.tsx +++ b/src/components/parentDashboard/ButtonMenu.tsx @@ -16,7 +16,7 @@ const ButtonMenu = ({ onOpenSendFundsModal, onOpenNetworkModal, onOpenMembersTableModal, - children, + members, }: { onAddChildOpen: () => void; setSelectedTab: (tab: ParentDashboardTabs) => void; @@ -26,7 +26,7 @@ const ButtonMenu = ({ onOpenSendFundsModal: () => void; onOpenNetworkModal: () => void; onOpenMembersTableModal: () => void; - children?: User[]; + members?: User[]; }) => { const { userDetails } = useAuthStore( (state) => ({