Skip to content

Commit dc999fe

Browse files
authored
refactor: teams in sidebar 👥 (#104)
1 parent 11f414a commit dc999fe

12 files changed

Lines changed: 211 additions & 64 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@chakra-ui/icons": "^1.1.7",
17-
"@chakra-ui/react": "^2.1.2",
17+
"@chakra-ui/react": "^2.2.1",
1818
"@emotion/react": "^11.9.0",
1919
"@emotion/styled": "^11.8.0",
2020
"@octokit/graphql": "^4.8.0",

src/components/layouts/app.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export default function Layout({ children }) {
9797
</Box>
9898
</Box>
9999
</Flex>
100-
<Sidebar user={user}>
100+
<Sidebar>
101101
{children}
102102
</Sidebar>
103103
</TeamProvider>

src/components/layouts/settings.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ const SettingsNav = () => {
4545
spacing={1}
4646
>
4747
{navItems.map((item, index) => (
48-
<NavItem key={item.name} {...item} />
48+
<NavItem
49+
key={`${item.name}-${index}`} {...item}
50+
/>
4951
))}
5052
</HStack>
5153
)
5254
}
5355

54-
const NavItem = ({ icon, name, href, ...props }) => {
56+
const NavItem = ({ name, href, icon, ...props }) => {
5557
const router = useRouter()
5658
const { pathname } = router
5759
const isActive = pathname === href || (href === '/settings/profile' && pathname === '/settings')
@@ -68,7 +70,8 @@ const NavItem = ({ icon, name, href, ...props }) => {
6870
>
6971
<Link href={href} passHref>
7072
<Button
71-
size='sm' as='a' leftIcon={icon} w='full' justifyContent='start' bg='none'
73+
as='a' size='sm' w='full' justifyContent='start' bg='none'
74+
leftIcon={icon}
7275
>
7376
<Text>
7477
{name}

src/components/projects/project.jsx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,64 @@ import NextLink from 'next/link'
88
import { Box, HStack, IconButton, useColorModeValue as mode, Link, Text } from '@chakra-ui/react'
99
import { useState, useContext } from 'react'
1010
import { TeamContext } from '@components/contexts/team-context'
11+
import { relativeTimeFromDates } from '@util/time'
1112

1213
/**
1314
* Deisgn used to list a user's Projects
1415
*
1516
* !todo: change 'title' to 'name'
1617
* !todo: add confirm dialog for deleting a project
1718
*/
18-
export const Project = ({ title, repos, href, description }) => {
19+
export const Project = ({ title, repos, href, description, createdAt, ...props }) => {
1920
const router = useRouter()
2021
const toast = useToast()
2122
const { mutate } = useSWRConfig()
2223
const { currentTeam } = useContext(TeamContext)
23-
2424
const [isDeleting, setIsDeleting] = useState(false)
2525

26+
const detailTextFontColor = mode('gray.500', 'white')
27+
2628
const deleteProject = async () => {
2729
setIsDeleting(true)
2830
await $fetch(`/api/teams/${currentTeam}/projects/${title}`, { method: 'DELETE' })
2931
toast({
30-
title: "Project deleted",
32+
title: 'Project deleted',
3133
description: `${title} has been successfully deleted!`,
32-
status: "error",
34+
status: 'error',
3335
duration: 9000,
3436
isClosable: true,
35-
position: "top-right"
37+
position: 'top-right'
3638
})
3739
setIsDeleting(false)
3840

3941
mutate(`/api/teams/${currentTeam}/projects`)
42+
setIsDeleting(false)
4043
}
4144

45+
const date = new Date(createdAt)
46+
const relativeDate = relativeTimeFromDates(date)
47+
4248
return (
43-
<Box position="relative" px={4}>
49+
<Box position='relative' px={4} {...props}>
4450
<Box>
4551
<NextLink href={`${router.asPath}/${href}`} passHref>
4652
<Link color={mode('#0c68da', '#549bf5')} fontSize='xl'>
4753
{title}
4854
</Link>
4955
</NextLink>
50-
<Box mt={1} mb={3} maxW="xl" color={mode('gray.600', 'gray.200')}>
56+
<Box mt={1} mb={3} maxW='xl' color={mode('gray.600', 'gray.200')}>
5157
<Text fontSize='md'>
5258
{description}
5359
</Text>
5460
</Box>
55-
<HStack color={mode('gray.500', 'white')} mt="1">
56-
<Box as={HiCollection} fontSize="lg" color="gray.400" />
57-
<span>{repos} {plur('repository', repos)}</span>
61+
<HStack mt='1' color={detailTextFontColor} spacing={6} fontSize='sm'>
62+
<HStack>
63+
<HiCollection size='16' />
64+
<Text>{repos} {plur('repository', repos)}</Text>
65+
</HStack>
66+
<Text>
67+
Created {relativeDate}
68+
</Text>
5869
</HStack>
5970
</Box>
6071

@@ -65,10 +76,11 @@ export const Project = ({ title, repos, href, description }) => {
6576
mt={{ base: '4', sm: '0' }}
6677
>
6778
<IconButton
68-
aria-label="Edit" icon={<HiPencilAlt />} rounded="full" size="md" cursor="pointer"
79+
aria-label='Edit' icon={<HiPencilAlt />} rounded='full' size='sm' cursor='pointer' colorScheme='gray'
80+
isDisabled
6981
/>
7082
<IconButton
71-
aria-label="Delete" icon={<HiTrash />} rounded="full" size="md" cursor="pointer"
83+
aria-label='Delete' icon={<HiTrash />} rounded='full' size='sm' cursor='pointer' colorScheme='gray'
7284
onClick={deleteProject} isLoading={isDeleting}
7385
/>
7486
</HStack>

src/components/sidebar/sidebar-link.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@ export default function SidebarLink({ href, children, icon = <ArrowRight />, ava
3434
</Link>
3535
)
3636
}
37+
3738
const ArrowRight = createIcon({
3839
viewBox: '0 0 16 16',
3940
path: (
4041
<path
41-
d="M3.38974 12.6633L9.42974 12.6633C9.86308 12.6633 10.2697 12.4567 10.5164 12.1033L13.1497 8.39C13.3164 8.15667 13.3164 7.85 13.1497 7.61667L10.5097 3.89667C10.2697 3.54334 9.86308 3.33667 9.42974 3.33667L3.38974 3.33667C2.84974 3.33667 2.53641 3.95667 2.84974 4.39667L5.42974 8.00334L2.84974 11.61C2.53641 12.05 2.84974 12.6633 3.38974 12.6633V12.6633Z"
42+
d="M3.38974 12.6633L9.42974 12.6633C9.86308 12.6633 10.2697 12.4567 10.5164 12.1033L13.1497 8.39C13.3164 8.15667
43+
13.3164 7.85 13.1497 7.61667L10.5097 3.89667C10.2697 3.54334 9.86308 3.33667 9.42974 3.33667L3.38974
44+
3.33667C2.84974 3.33667 2.53641 3.95667 2.84974 4.39667L5.42974 8.00334L2.84974 11.61C2.53641 12.05 2.84974
45+
12.6633 3.38974 12.6633V12.6633Z"
4246
fill="currentcolor"
4347
/>
4448
),
Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import { useContext } from 'react'
22
import { useDisclosure } from '@chakra-ui/hooks'
3-
import { Menu as ChakraMenu, MenuButton, MenuList, MenuItem,
4-
Spinner, Image, Text, Divider } from '@chakra-ui/react'
5-
import { SettingsIcon } from '@chakra-ui/icons'
3+
import { SettingsIcon, MoonIcon, SunIcon } from '@chakra-ui/icons'
64
import { FiPlusCircle, FiLogOut } from 'react-icons/fi'
75
import { UserInfo } from './user-info'
86
import NewTeam from '@components/teams/new-team'
9-
107
import { TeamContext } from '@components/contexts/team-context'
118

129
import { signOut } from 'next-auth/react'
10+
import { useUser } from '@util/hooks'
11+
12+
import {
13+
MenuButton, MenuList, Spinner, Image, Text, Divider, useColorMode, useColorModeValue,
14+
Menu as ChakraMenu, MenuItem as ChakraMenuItem
15+
} from '@chakra-ui/react'
1316

14-
export default function SidebarMenu({ user, teams, isTeamsLoading }) {
17+
export default function SidebarMenu({ teams, isTeamsLoading }) {
18+
const { user } = useUser()
1519
const { currentTeam, handleTeamSwitch } = useContext(TeamContext)
1620
const { isOpen, onOpen, onClose } = useDisclosure()
21+
22+
const menuItemSectionTextColor = useColorModeValue('gray.600', 'gray.100')
23+
const logoutIconColor = useColorModeValue('gray.600', 'gray.100')
1724

1825
return (
1926
<ChakraMenu>
@@ -26,9 +33,14 @@ export default function SidebarMenu({ user, teams, isTeamsLoading }) {
2633
<MenuList p="2" borderRadius="base" w="full" >
2734
{isTeamsLoading ? <Spinner /> : (
2835
<>
29-
<Text p="0 8px 4px 8px" color="gray.600">Personal</Text>
36+
<Text
37+
p="0 8px 4px 8px" color={menuItemSectionTextColor}
38+
>
39+
Personal
40+
</Text>
3041
<MenuItem
31-
as="button" onClick={() => handleTeamSwitch(user.ghLogin)} color="gray.700" borderRadius="base" p="8px"
42+
as="button" borderRadius="base" p="8px"
43+
onClick={() => handleTeamSwitch(user.ghLogin)}
3244
>
3345
<Image
3446
boxSize='20px'
@@ -37,55 +49,100 @@ export default function SidebarMenu({ user, teams, isTeamsLoading }) {
3749
alt={user.ghLogin}
3850
mr='12px'
3951
/>
40-
<Text fontWeight={user.ghLogin === currentTeam ? 'bold' : 'normal'}>{user.ghLogin}</Text>
52+
<Text fontWeight={user.ghLogin === currentTeam ? 'bold' : 'normal'}>
53+
{user.ghLogin}
54+
</Text>
4155
</MenuItem>
42-
<Text p="0 8px 4px 8px" color="gray.600">Teams</Text>
56+
57+
<Text p="0 8px 4px 8px" color={menuItemSectionTextColor}>
58+
Teams
59+
</Text>
4360
{teams.map(teamItem => (user.ghLogin != teamItem.name ?
44-
<MenuItem as="button" color="gray.700" borderRadius="base" p="8px" key={`${teamItem.name}-menu-item`}
45-
onClick={() => handleTeamSwitch(teamItem.name)}>
61+
<MenuItem
62+
as="button" borderRadius="base" p="8px"
63+
key={`${teamItem.name}-menu-item`}
64+
onClick={() => handleTeamSwitch(teamItem.name)}
65+
>
4666
<Image
4767
boxSize='20px'
4868
borderRadius='full'
4969
src={teamItem.avatarUrl ?? `https://avatars.dicebear.com/api/jdenticon/${teamItem.name}.svg`}
5070
alt={teamItem.name}
5171
mr='12px'
5272
/>
53-
<Text fontWeight={teamItem.name === currentTeam ? 'bold' : 'normal'}>{teamItem.name}</Text>
73+
<Text fontWeight={teamItem.name === currentTeam ? 'bold' : 'normal'}>
74+
{teamItem.name}
75+
</Text>
5476
</MenuItem>
5577
: null))}
5678
<MenuItem
57-
p="8px"
58-
color="gray.700"
59-
borderRadius="base"
60-
onClick={onOpen}
79+
p="8px"
80+
borderRadius="base"
81+
onClick={onOpen}
6182
icon={<FiPlusCircle size="20px" color="#2780ce"/>}
6283
>
6384
Create Team
6485
</MenuItem>
6586
<Divider my="2"/>
87+
88+
<ColorModeToggle />
6689
<MenuItem
6790
as="a"
68-
p="8px"
69-
color="gray.700"
70-
borderRadius="base"
71-
icon={<SettingsIcon boxSize="15px" color="gray.600" />}
91+
p="8px"
92+
borderRadius="base"
93+
icon={<SettingsIcon boxSize="15px" />}
7294
href={`/settings/teams/${currentTeam}`}
7395
>
7496
Team Settings
7597
</MenuItem>
7698
<MenuItem
77-
p="8px"
78-
color="gray.700"
79-
borderRadius="base"
99+
p="8px"
100+
borderRadius="base"
80101
onClick={() => signOut()}
81-
icon={<FiLogOut size="15px" color="#4A5568" />}
102+
icon={<FiLogOut size="15px" color={logoutIconColor} />}
82103
>
83104
Logout
84105
</MenuItem>
85106
</>
86107
)}
87108
</MenuList>
88-
<NewTeam isOpen={isOpen} onOpen={onOpen} onClose={onClose}/>
109+
<NewTeam isOpen={isOpen} onOpen={onOpen} onClose={onClose} />
89110
</ChakraMenu>
90111
)
91112
}
113+
114+
/**
115+
* Small wrapper for MenuItem's text color
116+
*/
117+
const MenuItem = ({ children, ...props }) => {
118+
const menuItemTextColor = useColorModeValue('gray.700', 'gray.200')
119+
return (
120+
<ChakraMenuItem color={menuItemTextColor} {...props}>
121+
{children}
122+
</ChakraMenuItem>
123+
)
124+
}
125+
126+
const ColorModeToggle = () => {
127+
const { colorMode, toggleColorMode } = useColorMode()
128+
const itemText = colorMode === 'light' ? 'Dark Mode' : 'Light Mode'
129+
const itemIcon = colorMode === 'light' ? <MoonIcon boxSize="15px" /> : <SunIcon boxSize="15px" />
130+
131+
// Delay in place because chakra toggles the entire UI and abruptly closes the menu
132+
const delayedToggle = () => {
133+
setTimeout(() => {
134+
toggleColorMode()
135+
}, 200)
136+
}
137+
138+
return (
139+
<MenuItem
140+
p="8px"
141+
borderRadius="base"
142+
onClick={delayedToggle}
143+
icon={itemIcon}
144+
>
145+
{itemText}
146+
</MenuItem>
147+
)
148+
}

src/components/sidebar/sidebar.jsx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,35 @@
11
import { useContext } from 'react'
2-
import { Avatar, Box, Stack, Flex, Spacer, VStack } from '@chakra-ui/react'
2+
import { Box, Stack, Flex, Spacer, VStack } from '@chakra-ui/react'
33
import { BsFillFolderFill } from 'react-icons/bs'
44
import { SettingsIcon } from '@chakra-ui/icons'
55
import { data } from '@data'
66

7+
import TeamPreview from './team-preview'
78
import { NavSectionTitle } from '@components/nav-section-title'
89
import SidebarLink from './sidebar-link'
910
import SidebarMenu from './sidebar-menu'
1011
import { ScrollArea } from '@components/scroll-area'
1112

12-
import { useTeammates, useTeams } from '@hooks'
13+
import { useTeammates, useTeams, useUser } from '@hooks'
1314
import { TeamContext } from '@components/contexts/team-context'
1415

1516
import { Book, Github, Mountain, Wrench } from 'lucide-react'
1617

17-
export default function Sidebar({ user }) {
18+
export default function Sidebar() {
1819
const { currentTeam } = useContext(TeamContext)
20+
const { user } = useUser()
1921

20-
const { teammates, isLoading: isTeammatesLoading, isError: isTeammatesError } = useTeammates(currentTeam)
22+
const { teammates, isLoading: _isTeammatesLoading, isError: isTeammatesError } = useTeammates(currentTeam)
2123
const { teams, isLoading: isTeamsLoading, isError: isTeamsError } = useTeams(user.ghLogin)
2224

2325
const error = isTeammatesError || isTeamsError
26+
const shouldRenderTeamPreview = !error && currentTeam !== user?.ghLogin
2427

2528
if (error) {
26-
console.error("🚀 ~ file: SideBar.jsx ~ line 35 ~ Sidebar ~ {isTeammatesError, isTeamsError}", {isTeammatesError, isTeamsError})
29+
// todo: what do here 🤔
30+
console.error("🚀 ~ file: SideBar.jsx ~ line 35 ~ Sidebar ~ {isTeammatesError, isTeamsError}", {
31+
isTeammatesError, isTeamsError
32+
})
2733
}
2834

2935
return (
@@ -32,7 +38,7 @@ export default function Sidebar({ user }) {
3238
color="gray.200" position="fixed" h="100vh"
3339
>
3440
<Flex fontSize="sm" lineHeight="tall" h="100%" flexDirection="column">
35-
<SidebarMenu user={user} teams={teams} isTeamsLoading={isTeamsLoading} />
41+
<SidebarMenu teams={teams} isTeamsLoading={isTeamsLoading} />
3642
<ScrollArea pt="5" pb="1" h="100%">
3743
<VStack h="full" align="left">
3844
<Box>
@@ -41,18 +47,9 @@ export default function Sidebar({ user }) {
4147
Projects
4248
</SidebarLink>
4349
</Stack>
44-
<Stack pb="6">
45-
<NavSectionTitle>{currentTeam}</NavSectionTitle>
46-
{teammates ? teammates.map((teammate, index) => {
47-
return (teammate.login != user.ghLogin ?
48-
<SidebarLink
49-
key={index}
50-
avatar={<Avatar bg="none" size="xs" name={teammate.name} src={teammate.avatarUrl} />}
51-
>
52-
{teammate.name}
53-
</SidebarLink>
54-
: null)}) : null}
55-
</Stack>
50+
{shouldRenderTeamPreview &&
51+
<TeamPreview teammates={teammates} currentTeam={currentTeam} />
52+
}
5653
<NavSection title="Explore">
5754
<Stack pb="6">
5855
<SidebarLink icon={<Book size={15} />}>

0 commit comments

Comments
 (0)