Skip to content

Commit c0fdaf7

Browse files
JaviMunitaJaviera MunitaJavieraMunitaEclass
authored
feat(PDYE-1116): componentes menu dropdown calendario (#661)
* feat(PDYE-1116): componentes menu calendario * feat(PDYE-1116): formato hora * fix(PDYE-1116): avance dropdown ui-kit y prueba con mock data * fix(PDYE-1116): botón que abre menú * fix(PDYE-1116): posición menu desktop * fix(PDYE-1116): types componente Events * fix(PDYE-1116): corrección anotaciones * feat(PDYE-1119): format * feat(PDYE-1119): format y package-lock * feat(PDYE-1119): fix errores typescript --------- Co-authored-by: Javiera Munita <javieramunita@MacBook-Air-de-Javiera.local> Co-authored-by: Javiera Munita <javiera.munita@eclass.cl>
1 parent 63bc345 commit c0fdaf7

File tree

16 files changed

+2126
-2812
lines changed

16 files changed

+2126
-2812
lines changed

package-lock.json

Lines changed: 1424 additions & 2812 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"@chakra-ui/react": "1.8.9",
118118
"@emotion/react": "11.8.2",
119119
"@emotion/styled": "11.8.1",
120+
"date-fns": "^4.1.0",
120121
"framer-motion": "6.2.8",
121122
"react-hot-toast": "2.4.1",
122123
"react-ripples": "2.2.1"

src/atoms/Icons/Calendar.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Base, BaseProps } from './Base'
2+
export function Calendar(props: BaseProps): JSX.Element {
3+
return (
4+
<Base {...props} title="Calendar">
5+
<g fill="#B0CFE0">
6+
<path
7+
fill="#B0CFE0"
8+
d="M15 2h-2V0h-2v2H9V0H7v2H5V0H3v2H1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zm-1 12H2V5h12v9z"
9+
/>
10+
<path d="M4 7h2v2H4zM7 7h2v2H7zM4 10h2v2H4zM7 10h2v2H7zM10 7h2v2h-2zM10 10h2v2h-2z" />
11+
</g>
12+
</Base>
13+
)
14+
}
15+
16+
Calendar.displayName = 'Calendar'

src/atoms/Icons/Clock.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Base, BaseProps } from './Base'
2+
export function Clock(props: BaseProps): JSX.Element {
3+
return (
4+
<Base {...props} title="Clock">
5+
<path
6+
fill="#B0CFE0"
7+
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 9H7V4h2v3h3v2z"
8+
/>
9+
</Base>
10+
)
11+
}
12+
13+
Clock.displayName = 'Clock'
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { CalendarDropdownContainer } from './CalendarDropdown/CalendarDropdownContainer'
2+
import { ICalendarDropdown } from './types'
3+
4+
export const CalendarDropdown = ({
5+
redirectToCalendar,
6+
t,
7+
courseColors,
8+
now,
9+
events,
10+
loading,
11+
}: ICalendarDropdown): JSX.Element => {
12+
const date = new Date(now)
13+
const isoDate = date.toISOString()
14+
15+
return (
16+
<CalendarDropdownContainer
17+
events={events}
18+
loading={loading}
19+
now={isoDate}
20+
courseColors={courseColors}
21+
redirectToCalendar={redirectToCalendar}
22+
t={t}
23+
/>
24+
)
25+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { useEffect, useState } from 'react'
2+
import { Box, Menu, MenuList, useMediaQuery } from '@chakra-ui/react'
3+
4+
import { useParseEvents } from './services/parseEvents'
5+
import { GoToCalendar } from './Components/GoToCalendar'
6+
import { Header } from './Components/Header'
7+
import { Empty } from './Components/Empty'
8+
import { Events } from './Components/Events'
9+
import { ICalendarDropdown } from '../types'
10+
11+
export const CalendarDropdownContainer = ({
12+
events,
13+
loading,
14+
t,
15+
now,
16+
redirectToCalendar,
17+
courseColors: colors,
18+
}: ICalendarDropdown): JSX.Element => {
19+
const [isMobile] = useMediaQuery('(max-width: 577px)')
20+
const { closeAndMarkSeen, empty, hasNew, ...all } = useParseEvents(events, now)
21+
22+
const [isTooltipDisabled, setTooltipDisabled] = useState(false)
23+
const [isMenuOpen, setMenuOpen] = useState(false)
24+
25+
const text = {
26+
buttonCalendar: t('CalendarGoto'),
27+
course: t('CalendarCourse'),
28+
empty: t('CalendarDontEvent'),
29+
events: {
30+
today: t('CalendarToday'),
31+
tomorrow: t('CalendarTomorrow'),
32+
next: t('CalendarNext'),
33+
},
34+
header: t('CalendarNextDates'),
35+
loading: t('Cargando'),
36+
tooltip: t('Calendar'),
37+
}
38+
39+
// Resuelve tooltip que se mantiene visible al cerrar el menu
40+
useEffect(() => {
41+
if (isMenuOpen) {
42+
setTooltipDisabled(true)
43+
} else {
44+
const timer = setTimeout(() => setTooltipDisabled(false), 300)
45+
return () => clearTimeout(timer)
46+
}
47+
}, [isMenuOpen])
48+
49+
const onClose = (): void => {
50+
closeAndMarkSeen()
51+
setMenuOpen(false)
52+
}
53+
54+
return (
55+
<Box
56+
zIndex={4}
57+
className="calendarDropdown"
58+
mr="24px"
59+
position="relative"
60+
sx={{
61+
'>div': {
62+
transform: 'translate3d(-409px, 38px, 0px) !important',
63+
},
64+
'.chakra-menu__menu-list': {
65+
borderRadius: isMobile ? '0' : '10px',
66+
boxShadow: isMobile ? 'none' : 'rgba(47, 47, 47, 0.2) -1px 6px 40px 0px',
67+
width: isMobile ? '100vw' : '500px',
68+
height: isMobile || empty ? 'auto' : '560px',
69+
animation: 'none !important',
70+
transition: 'none !important',
71+
transform: 'none !important',
72+
opacity: '1 !important',
73+
position: 'absolute',
74+
},
75+
'.chakra-menu__group__title': {
76+
fontSize: '18px',
77+
lineHeight: '31px',
78+
margin: '32px 0 0',
79+
padding: '0 0 8px 24px',
80+
},
81+
'.react-ripples': {
82+
width: 'inherit',
83+
},
84+
}}
85+
>
86+
<Menu autoSelect={false} onOpen={() => setMenuOpen(true)} onClose={onClose}>
87+
<>
88+
<GoToCalendar
89+
hasNew={hasNew ?? false}
90+
text={text.tooltip}
91+
tooltipDisabled={isTooltipDisabled}
92+
/>
93+
<MenuList>
94+
<Header text={text.header} isMobile={isMobile} />
95+
{loading ? (
96+
<Loading text={text.loading} />
97+
) : events.length === 0 || empty ? (
98+
<Empty text={text.empty} />
99+
) : (
100+
<Events
101+
colors={colors}
102+
events={all}
103+
text={text}
104+
redirecToCalendar={redirectToCalendar}
105+
isMobile={isMobile}
106+
/>
107+
)}
108+
</MenuList>
109+
</>
110+
</Menu>
111+
</Box>
112+
)
113+
}
114+
115+
const Loading = ({ text }: { text?: string }): JSX.Element => {
116+
return <div>{text ?? 'Loading...'}</div>
117+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Box } from '@chakra-ui/react'
2+
3+
import { NoEventsIcon } from '../Icons/NoEventsIcon'
4+
5+
export const Empty = ({ text }: { text?: string }): JSX.Element => {
6+
return (
7+
<Box
8+
alignItems="center"
9+
display="flex"
10+
flexDirection="column"
11+
gap="24px"
12+
padding="104px 0px 64px"
13+
>
14+
<NoEventsIcon />
15+
<Box fontSize="20px" fontWeight={700} color="#2F2F2F">
16+
{text ?? 'Aún no tienes eventos en tu calendario'}
17+
</Box>
18+
</Box>
19+
)
20+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { BtnSecondary } from '@/molecules'
2+
import { Box, MenuGroup } from '@chakra-ui/react'
3+
import { Key } from 'react'
4+
5+
import { EventsList } from '../../../EventsList/EventsList'
6+
7+
interface EventsProps {
8+
events: {
9+
today: Event[]
10+
tomorrow: Event[]
11+
next: Event[]
12+
}
13+
text: any
14+
redirecToCalendar: () => void
15+
isMobile: boolean
16+
colors: any
17+
}
18+
19+
export const Events = ({
20+
events,
21+
text,
22+
redirecToCalendar,
23+
isMobile,
24+
colors,
25+
}: EventsProps): JSX.Element => {
26+
const { today, tomorrow, next } = events
27+
return (
28+
<Box
29+
borderRadius="10px"
30+
color="#1C1818"
31+
h={isMobile ? 'auto' : '552px'}
32+
overflowY="scroll"
33+
pb="32px"
34+
sx={{
35+
'.calendar-events-group': {
36+
borderBottom: '1px solid #E8E8E8',
37+
},
38+
}}
39+
>
40+
{redirecToCalendar && (
41+
<Box
42+
sx={{
43+
button: {
44+
p: '8px ',
45+
fontSize: '14px',
46+
lineHeight: '14px',
47+
minW: 'fit-content',
48+
minH: 'fit-content',
49+
},
50+
}}
51+
>
52+
<BtnSecondary onClick={redirecToCalendar} m="72px 0 0 24px">
53+
{text.buttonCalendar ?? 'Ir a Mi Calendario'}
54+
</BtnSecondary>
55+
</Box>
56+
)}
57+
<EventsGroup
58+
colors={colors}
59+
text={text}
60+
title={text?.events?.today ?? 'Hoy'}
61+
events={today}
62+
/>
63+
<EventsGroup
64+
colors={colors}
65+
text={text}
66+
title={text?.events?.tomorrow ?? 'Mañana'}
67+
events={tomorrow}
68+
/>
69+
<EventsGroup
70+
colors={colors}
71+
text={text}
72+
title={text?.events?.next ?? 'Próximos'}
73+
events={next}
74+
/>
75+
</Box>
76+
)
77+
}
78+
interface Event {
79+
id: Key | null | undefined
80+
associated_resource: { name: any }
81+
course: { name: string | undefined }
82+
formatedDate: { start: string; hours: string }
83+
course_id: string | number
84+
isNew: boolean | undefined
85+
}
86+
87+
interface EventsGroupProps {
88+
title: string
89+
events: Event[]
90+
text: any
91+
colors: any
92+
}
93+
94+
const EventsGroup = ({ title, events, text, colors }: EventsGroupProps): JSX.Element => {
95+
if (!events || (events && events.length === 0)) return <></>
96+
return (
97+
<Box
98+
className="calendar-events-group"
99+
_focus={{
100+
background: 'none !important',
101+
border: '1px solid #0189FF',
102+
}}
103+
sx={{
104+
'.chakra-menu__menuitem > div': {
105+
w: '100%',
106+
},
107+
}}
108+
>
109+
<MenuGroup title={title}>
110+
{events.map(
111+
(event: {
112+
id: Key | null | undefined
113+
associated_resource: { name: any }
114+
course: { name: string | undefined }
115+
formatedDate: { start: string; hours: string }
116+
course_id: string | number
117+
isNew: boolean | undefined
118+
}) => {
119+
return (
120+
<Box // Una vez que el evento se comporte como link, se debe cambiar Box a MenuItem y aplicar el efecto de focus
121+
bg="#FFFFFF"
122+
border="none"
123+
cursor="default"
124+
padding="0"
125+
key={event.id}
126+
_hover={{
127+
boxShadow: 'none !important',
128+
cursor: 'default !important',
129+
bg: 'none !important',
130+
}}
131+
_focus={{
132+
background: 'none !important',
133+
boxShadow: 'none !important',
134+
}}
135+
// _focus={{
136+
// background: 'none !important',
137+
// boxShadow: `inset 0px 0.5px 0px 3px ${vars('colors-icon-deepSkyBlue')}`,
138+
// }}
139+
>
140+
<EventsList
141+
key={event.id}
142+
name={event.associated_resource.name || ''}
143+
courseName={event.course.name}
144+
date={event.formatedDate.start}
145+
hours={event.formatedDate.hours}
146+
color={event.course_id && colors?.[event.course_id]}
147+
text={text.events.course}
148+
hasNotification={event.isNew}
149+
isDropdown
150+
/>
151+
</Box>
152+
)
153+
}
154+
)}
155+
</MenuGroup>
156+
</Box>
157+
)
158+
}

0 commit comments

Comments
 (0)