Merge pull request #476 from m-tartari/fix/external_links

🐛 fix: update dead links to external resources
This commit is contained in:
2025-05-23 16:27:01 +02:00
committed by GitHub
19 changed files with 266 additions and 298 deletions
@@ -1,9 +0,0 @@
import { IconButton as MuiIconButton, styled } from '@mui/material'
const IconButton = styled(MuiIconButton)(({ theme }) => ({
':hover': {
backgroundColor: 'transparent',
color: theme.palette.primary.main,
},
}))
export default IconButton
-1
View File
@@ -1 +0,0 @@
export { default } from './IconButton'
@@ -1,9 +1,8 @@
// IconLinks.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as IconLinks from '.'
import * as IconLinks from './index.js'
const labels: { [key: string]: string } = {
DescriptionIconLink: 'Doc-button',
@@ -18,8 +17,6 @@ const labels: { [key: string]: string } = {
vi.stubGlobal('open', vi.fn())
describe('IconLinks', () => {
const open = vi.spyOn(window, 'open')
const defaultProps = {
href: 'https://test.com',
fontSize: 'large' as const,
@@ -28,20 +25,10 @@ describe('IconLinks', () => {
describe.each(Object.entries(IconLinks))('%s', (name, Icon) => {
test('renders correctly and without crashing', async () => {
render(<Icon {...defaultProps} />)
await userEvent.click(screen.getByLabelText(labels[name]))
expect(open).toHaveBeenCalledTimes(1)
expect(open).toHaveBeenLastCalledWith(defaultProps.href, '_blank')
})
test('stops propagation when the icon button is clicked', async () => {
const onClick = vi.fn()
render(
<div onClick={onClick}>
<Icon {...defaultProps} />
</div>
)
await userEvent.click(screen.getByLabelText(labels[name]))
expect(onClick).not.toHaveBeenCalled()
const link = screen.getByLabelText(labels[name])
expect(link).toHaveAttribute('href', defaultProps.href)
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
test("renders the badge when 'index' is provided", () => {
-1
View File
@@ -1 +0,0 @@
export * from './IconLinks'
@@ -1,10 +1,7 @@
import { Email, LinkedIn, GitHub, Description, OndemandVideo, School, OpenInNew, OpenInBrowser } from '@mui/icons-material'
import { Badge, IconButtonProps, SvgIcon } from '@mui/material'
import { Badge, Link, LinkProps, SvgIcon } from '@mui/material'
import IconButton from '../IconButton'
interface IconLinkProps extends IconButtonProps {
href: string
interface IconLinkProps extends LinkProps {
fontSize?: 'small' | 'large' | 'medium' | 'inherit'
index?: number
}
@@ -13,25 +10,46 @@ interface IconWrapperProps extends IconLinkProps {
Icon: typeof SvgIcon
}
const IconWrapper = ({ index, href: to, fontSize, Icon, ...props }: IconWrapperProps) => {
const IconWrapper: React.FC<IconWrapperProps> = ({ index, fontSize, Icon, sx, ...props }) => {
if (typeof index === 'undefined') {
return (
<IconButton
onClick={e => {
e.stopPropagation()
window.open(to, '_blank')
}}
<Link
color="inherit"
rel="noopener noreferrer"
target="_blank"
underline="hover"
sx={theme => ({
color: 'inherit',
':hover': {
backgroundColor: 'transparent',
color: theme.palette.primary.main,
},
...sx,
})}
{...props}>
<Icon fontSize={fontSize} />
</IconButton>
</Link>
)
}
return (
<IconButton onClick={() => window.open(to, '_blank')} {...props}>
<Link
color="inherit"
rel="noopener noreferrer"
target="_blank"
underline="hover"
sx={theme => ({
color: 'inherit',
':hover': {
backgroundColor: 'transparent',
color: theme.palette.primary.main,
},
...sx,
})}
{...props}>
<Badge badgeContent={index}>
<Icon fontSize={fontSize} />
</Badge>
</IconButton>
</Link>
)
}
@@ -1,11 +1,9 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Box } from '@mui/material'
import Publication from '.'
import Publication from './index.js'
vi.stubGlobal('open', vi.fn())
const open = vi.spyOn(window, 'open')
describe('Publication component', () => {
const defaultProps = {
@@ -13,10 +11,10 @@ describe('Publication component', () => {
authors: 'Test Author',
title: 'Test Title',
location: 'Test Location',
link: 'https://test.com',
href: 'https://test.com',
}
test('renders without crashing and displays the correct year, authors, title, and location', () => {
render(<Publication {...defaultProps} link={undefined} />)
render(<Publication {...defaultProps} href={undefined} />)
expect(screen.getByText(defaultProps.year)).toBeInTheDocument()
expect(screen.getByText(defaultProps.title)).toBeInTheDocument()
expect(screen.getByText(defaultProps.authors, { exact: false })).toBeInTheDocument()
@@ -26,8 +24,10 @@ describe('Publication component', () => {
test('opens the correct link when the link button is clicked', async () => {
render(<Publication {...defaultProps} />)
await userEvent.click(screen.getByRole('button'))
expect(open).toHaveBeenCalledWith(defaultProps.link, '_blank')
const link = screen.getByRole('link')
expect(link).toHaveAttribute('href', defaultProps.href)
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
test('renders correctly with a custom base component', () => {
@@ -1,36 +0,0 @@
import React from 'react'
import { Box, SxProps, Theme, Typography, TypographyProps } from '@mui/material'
import { ExternalIconLink } from '..'
type AwardEntryProps = {
year: string | number
title: string
authors: React.ReactNode
location: string
link?: string
titleProps?: TypographyProps
Component?: React.ElementType
sx?: SxProps<Theme>
}
const Publication = ({ Component = Box, ...props }: AwardEntryProps) => {
return (
<Component sx={{ pl: 2, ...props.sx }}>
<Box display="flex" gap={1} alignItems="flex-start">
<Typography variant="subtitle1" color="primary.main">
{props.year}
</Typography>
<Typography color="text.secondary">
{props.authors},{' '}
<Typography component="span" color="text.primary">
<b>{props.title}</b>
</Typography>
, In: {props.location}
{typeof props.link !== 'undefined' && <ExternalIconLink size="small" fontSize="small" href={props.link} sx={{ color: 'text.secondary' }} />}
</Typography>
</Box>
</Component>
)
}
export default Publication
+37 -1
View File
@@ -1 +1,37 @@
export { default } from './Publication'
import React from 'react'
import { Box, SxProps, Theme, Typography, TypographyProps } from '@mui/material'
import { ExternalIconLink } from 'components/atoms/IconLinks/index.js'
type AwardEntryProps = {
year: string | number
title: string
authors: React.ReactNode
location: string
href?: string
titleProps?: TypographyProps
Component?: React.ElementType
sx?: SxProps<Theme>
}
const Publication = ({ Component = Box, ...props }: AwardEntryProps) => {
return (
<Component sx={{ pl: 2, ...props.sx }}>
<Box display="flex" gap={1} alignItems="flex-start">
<Typography variant="subtitle1" color="primary.main">
{props.year}
</Typography>
<Typography color="text.secondary">
{props.authors},{' '}
<Typography component="span" color="text.primary">
<b>{props.title}</b>
</Typography>
, In: {props.location}
{typeof props.href !== 'undefined' && <ExternalIconLink fontSize="inherit" href={props.href} sx={{ color: 'text.secondary' }} />}
</Typography>
</Box>
</Component>
)
}
export default Publication
-1
View File
@@ -2,5 +2,4 @@ export { default as Award } from './Award'
export { default as Extracurricular } from './Extracurricular'
export { default as Publication } from './Publication'
export { default as IconLinks } from './IconButton'
export * from './IconLinks'
+4 -4
View File
@@ -6,10 +6,10 @@ const Footer = () => {
const year = new Date().getFullYear()
return (
<Box sx={{ bgcolor: 'background.paper', p: 6, display: 'flex', alignItems: 'center', flexDirection: 'column' }} component="footer">
<Stack direction="row">
<LinkedInIconLink size="large" fontSize="large" href="https://www.linkedin.com/in/m-tartari/" />
<GitHubIconLink size="large" fontSize="large" href="https://www.github.com/m-tartari/" />
<EmailIconLink size="large" fontSize="large" href="mailto:info@m-tartari.eu" />
<Stack direction="row" gap={2}>
<LinkedInIconLink fontSize="large" href="https://www.linkedin.com/in/m-tartari/" />
<GitHubIconLink fontSize="large" href="https://www.github.com/m-tartari/" />
<EmailIconLink fontSize="large" href="mailto:info@m-tartari.eu" />
</Stack>
<Typography variant="subtitle2" align="center" color="text.secondary" component="em" mt={3}>
&copy; {year} Michele Tartari. All rights reserved.
+8 -22
View File
@@ -44,28 +44,14 @@ const Hero = (props: { scrollTarget?: React.ForwardedRef<HTMLSelectElement> }) =
},
})}>
<Box>
{/*
<h2>Michele Tartari</h2>
<h3>Robotics Engineer</h3>
*/}
<Typography
// sx={{ fontSize: '2.5em',margin: 0 }}
variant="h3">
Robotics Engineer
</Typography>
<Typography
color="text.secondary"
sx={{
// margin: { xs: '0.5em 1em', md: '0.5em 0 0 0' },
margin: '0.5em 0 0 0',
top: 0,
}}>
<Typography variant="h3">Robotics Engineer</Typography>
<Typography color="text.secondary" sx={{ margin: '0.5em 0 0 0', top: 0 }}>
See some of my works below. Send a message to get in touch.
</Typography>
<Stack direction="row" sx={{ display: 'inline-block' }}>
<LinkedInIconLink size="large" sx={{ color: 'text.secondary' }} fontSize="large" href="https://www.linkedin.com/in/m-tartari/" />
<GitHubIconLink size="large" sx={{ color: 'text.secondary' }} fontSize="large" href="https://www.github.com/m-tartari/" />
<EmailIconLink size="large" sx={{ color: 'text.secondary' }} fontSize="large" href="mailto:info@m-tartari.eu" />
<Stack direction="row" gap={2} mt={2} justifyContent={{ xs: 'center', sm: 'end' }}>
<LinkedInIconLink sx={{ color: 'text.secondary' }} fontSize="large" href="https://www.linkedin.com/in/m-tartari/" />
<GitHubIconLink sx={{ color: 'text.secondary' }} fontSize="large" href="https://www.github.com/m-tartari/" />
<EmailIconLink sx={{ color: 'text.secondary' }} fontSize="large" href="mailto:info@m-tartari.eu" />
</Stack>
</Box>
<Avatar
@@ -83,8 +69,8 @@ const Hero = (props: { scrollTarget?: React.ForwardedRef<HTMLSelectElement> }) =
{props.scrollTarget && (
<IconButton
size="large"
onClick={() => (props.scrollTarget as React.MutableRefObject<HTMLSelectElement | null>).current?.scrollIntoView()}
sx={{ display: { xs: 'none', md: 'block' }, position: 'absolute', p: 1, bottom: 3 }}>
onClick={() => (props.scrollTarget as React.RefObject<HTMLElement | null>).current?.scrollIntoView()}
sx={{ position: 'absolute', p: 1, bottom: 2, left: '50%', transform: 'translate(-50%, 0)' }}>
<ExpandMore fontSize="large" />
</IconButton>
)}
+4 -2
View File
@@ -1,5 +1,5 @@
import React from 'react'
import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@mui/material'
import { Accordion, AccordionDetails, AccordionSummary, Stack, Typography } from '@mui/material'
import { ExpandMore as ExpandIcon } from '@mui/icons-material'
interface ProjectProps {
@@ -54,7 +54,9 @@ const Project = (props: ProjectProps) => {
<Typography variant="h6">
<b>{props.title}</b>
</Typography>
{props.subtitle}
<Stack direction="row" gap={1} alignItems="center">
{props.subtitle}
</Stack>
</div>
</AccordionSummary>
<AccordionDetails>{props.children}</AccordionDetails>
@@ -153,7 +153,7 @@ const DesktopComponent = React.forwardRef<HTMLSelectElement, Props>((props, ref)
{props.scrollTarget && (
<IconButton
size="large"
onClick={() => (props.scrollTarget as React.MutableRefObject<HTMLSelectElement | null>).current?.scrollIntoView()}
onClick={() => (props.scrollTarget as React.RefObject<HTMLSelectElement | null>).current?.scrollIntoView()}
sx={{ position: 'absolute', p: 1, bottom: 2, left: '50%', transform: 'translate(-50%, 0)' }}>
<ExpandMore fontSize="large" />
</IconButton>
@@ -1,41 +1,37 @@
import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWithRouter as render } from 'components/utils/tests'
import ContactPage from '.'
import { renderWithRouter as render } from 'components/utils/tests/index.js'
import ContactPage from './index.js'
vi.stubGlobal('open', vi.fn())
describe('ContactPage', () => {
const open = vi.spyOn(window, 'open')
test('renders without crashing and displays the correct contact methods', () => {
render(<ContactPage />)
expect(screen.getAllByText('Contacts')).toHaveLength(2) // Page title and the Appbar drawer link
expect(screen.getByLabelText('Email button')).toBeInTheDocument()
expect(screen.getByLabelText('LinkedIn button')).toBeInTheDocument()
expect(screen.getByLabelText('GitHub button')).toBeInTheDocument()
expect(screen.getByLabelText('Email link')).toBeInTheDocument()
expect(screen.getByLabelText('LinkedIn link')).toBeInTheDocument()
expect(screen.getByLabelText('GitHub link')).toBeInTheDocument()
})
test('opens the correct link when the email button is clicked', async () => {
test.each([
{
label: 'Email link',
href: 'mailto:info@m-tartari.eu',
},
// {
// label: 'LinkedIn link',
// href: 'https://www.linkedin.com/in/m-tartari/',
// },
// {
// label: 'GitHub link',
// href: 'https://www.github.com/m-tartari/',
// },
])('leads to the right place when the $label is clicked', ({ label, href }) => {
render(<ContactPage />)
await userEvent.click(screen.getByLabelText('Email button'))
expect(open).toHaveBeenCalledWith('mailto:info@m-tartari.eu', '_blank')
})
test('opens the correct link when the LinkedIn button is clicked', async () => {
render(<ContactPage />)
await userEvent.click(screen.getByLabelText('LinkedIn button'))
expect(open).toHaveBeenCalledWith('https://www.linkedin.com/in/m-tartari/', '_blank')
})
test('opens the correct link when the GitHub button is clicked', async () => {
render(<ContactPage />)
await userEvent.click(screen.getByLabelText('GitHub button'))
expect(open).toHaveBeenCalledWith('https://www.github.com/m-tartari/', '_blank')
const link = screen.getByLabelText(label)
expect(link).toHaveAttribute('href', href)
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
})
@@ -1,20 +1,41 @@
import { List, ListItem, ListItemButton as MuiListItemButton, Typography, styled } from '@mui/material'
import { Drafts as DraftsIcon, LinkedIn as LinkedInIcon, GitHub as GitHubIcon } from '@mui/icons-material'
import { Link, List, ListItem, Stack, Typography } from '@mui/material'
import DraftsIcon from '@mui/icons-material/Drafts'
import LinkedInIcon from '@mui/icons-material/LinkedIn'
import GitHubIcon from '@mui/icons-material/GitHub'
import { Page } from 'components/templates'
const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({
h6: {
color: theme.palette.text.secondary,
},
':hover': {
backgroundColor: 'transparent',
color: theme.palette.primary.main,
h6: {
color: theme.palette.primary.dark,
},
},
}))
interface ListItemProps {
children?: React.ReactNode
href?: string
}
const ListItemLink: React.FC<ListItemProps> = ({ children, ...props }) => (
<ListItem>
<Link
{...props}
rel="noopener noreferrer"
target="_blank"
underline="hover"
sx={theme => ({
color: 'inherit',
h6: {
color: theme.palette.text.secondary,
},
':hover': {
backgroundColor: 'transparent',
color: theme.palette.primary.main,
h6: {
color: theme.palette.primary.dark,
},
},
})}>
<Stack direction="row" alignItems="center">
{children}
</Stack>
</Link>
</ListItem>
)
const ContactPage = () => {
return (
@@ -23,32 +44,26 @@ const ContactPage = () => {
Feel free to contact me using any of the following:
</Typography>
<List>
<ListItem disablePadding>
<ListItemButton aria-label="Email button" onClick={() => window.open('mailto:info@m-tartari.eu', '_blank')}>
<DraftsIcon fontSize="large" />
<Typography variant="h6" ml={2}>
info
<wbr />
@m-tartari.eu
</Typography>
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton aria-label="LinkedIn button" onClick={() => window.open('https://www.linkedin.com/in/m-tartari/', '_blank')}>
<LinkedInIcon fontSize="large" />
<Typography variant="h6" ml={2}>
m-tartari
</Typography>
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton aria-label="GitHub button" onClick={() => window.open('https://www.github.com/m-tartari/', '_blank')}>
<GitHubIcon fontSize="large" />
<Typography variant="h6" ml={2}>
m-tartari
</Typography>
</ListItemButton>
</ListItem>
<ListItemLink href="mailto:info@m-tartari.eu" aria-label="Email link">
<DraftsIcon fontSize="large" />
<Typography variant="h6" ml={2}>
info
<wbr />
@m-tartari.eu
</Typography>
</ListItemLink>
<ListItemLink aria-label="LinkedIn link" href="https://www.linkedin.com/in/m-tartari/">
<LinkedInIcon fontSize="large" />
<Typography variant="h6" ml={2}>
m-tartari
</Typography>
</ListItemLink>
<ListItemLink aria-label="GitHub link" href="https://www.github.com/m-tartari/">
<GitHubIcon fontSize="large" />
<Typography variant="h6" ml={2}>
m-tartari
</Typography>
</ListItemLink>
</List>
</Page>
)
@@ -29,7 +29,12 @@ const Curriculum = () => {
<CurriculumItem title="Business Partner & Technical Lead" dates="03/23 - 12/24" company="MindHub Ferrara" location="Ferrara, Italy">
<Typography>
Co-founded{' '}
<Link target="_blank" rel="noopener noreferrer" href="https://www.facebook.com/profile.php?id=61550278936416">
<Link
target="_blank"
rel="noopener noreferrer"
// href="https://www.facebook.com/profile.php?id=61550278936416"
// href="https://mind-hub.it/contact-hall/mindhub-ferrara/"
href="https://www.linkedin.com/company/mindhub-ferrara/">
MindHub Ferrara
</Link>
, the first programming school for children in Ferrara, under the{' '}
@@ -132,9 +137,9 @@ const Curriculum = () => {
</Typography>
{/* id="WUT" */}
<CurriculumItem title="IBM Full Stack Software Developer Professional Certificate" dates="03/24">
<Stack direction="row" alignItems="flex-end">
<Stack direction="row" alignItems="center" gap={0.5}>
<Typography>Credential ID: EZ34GWQLRHEG</Typography>
<ExternalIconLink size="small" fontSize="small" href="https://www.coursera.org/account/accomplishments/professional-cert/EZ34GWQLRHEG" />
<ExternalIconLink fontSize="inherit" href="https://www.coursera.org/account/accomplishments/professional-cert/EZ34GWQLRHEG" />
</Stack>
</CurriculumItem>
@@ -414,7 +419,7 @@ const Curriculum = () => {
}
location="ROMANSY 23 - Robot Design, Dynamics and Control. CISM International Centre for Mechanical Sciences (Courses and Lectures), vol
601. Springer, Cham."
link="https://doi.org/10.1007/978-3-030-58380-4_7"
href="https://doi.org/10.1007/978-3-030-58380-4_7"
/>
</Box>
+19 -32
View File
@@ -1,5 +1,5 @@
import React from 'react'
import { Box, Typography } from '@mui/material'
import { Box, Stack, Typography } from '@mui/material'
import { ViewportProvider } from 'components/utils/viewport'
import { AcademyIconLink, DescriptionIconLink, ExternalAppIconLink, GitHubIconLink, VideoIconLink } from 'components/atoms'
@@ -28,12 +28,10 @@ const MainPage = () => {
image="images/IIT_Linfa.webp"
title="LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots"
caption={
<>
<VideoIconLink size="small" fontSize="small" href="https://www.linkedin.com/feed/update/urn:li:activity:7034480841569284096/" />
<Typography component="span" color="text.secondary" ml={1}>
Software Engineer @ IIT
</Typography>
</>
<Stack direction="row" gap={1} alignItems="center">
<VideoIconLink fontSize="small" pt={0.5} href="https://www.linkedin.com/feed/update/urn:li:activity:7034480841569284096/" />
<Typography color="text.secondary">Software Engineer @ IIT</Typography>
</Stack>
}>
This project aims to simplify the control, monitoring, and programming of reconfigurable robots through an intuitive block-programming
interface. It includes a front-end web app, built with TypeScript and React, allowing users to effortlessly change robot operating modes and
@@ -52,13 +50,11 @@ const MainPage = () => {
mobileImage="images/IIT_Modular_Mobile.webp"
title="Revamping HHCM's Modular: Swift Generation of Modular Robot Models"
caption={
<>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/ADVRHumanoids/motor_calibration" />
<ExternalAppIconLink size="small" fontSize="small" href="https://modular.alberobotics.it" />
<Typography component="span" color="text.secondary" ml={1}>
Software Engineer @ IIT
</Typography>
</>
<Stack direction="row" gap={1} alignItems="center">
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/ADVRHumanoids/modular_hhcm" />
<ExternalAppIconLink pt={0.5} fontSize="small" href="https://modular.alberobotics.it" />
<Typography color="text.secondary">Software Engineer @ IIT</Typography>
</Stack>
}>
This project focused on overhauling HHCM's Modular, enhancing its graphical interface, API, and deployment pipeline. Modular serves as a web
application for the swift generation of modular robot models, with an offline approach (Building Mode) enabling users to set up robots in a
@@ -75,12 +71,10 @@ const MainPage = () => {
image="images/IIT_Modules.webp"
title="Automated Actuators and Robotic Modules Calibration"
caption={
<>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/ADVRHumanoids/motor_calibration" />
<Typography component="span" color="text.secondary" ml={1}>
Embedded Engineer @ IIT
</Typography>
</>
<Stack direction="row" gap={1} alignItems="center">
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/ADVRHumanoids/motor_calibration" />
<Typography color="text.secondary">Embedded Engineer @ IIT</Typography>
</Stack>
}>
This project focused on automating the calibration of EtherCAT actuators and robotic modules, aiming for a fully automated procedure for
newly assembled actuators. The process involved testing the PDO, conducting phase angle calibration, torque sensor calibration, ripple and
@@ -96,18 +90,11 @@ const MainPage = () => {
mobileImage="images/thesis.webp"
title="Voice Controlled Mobile Robot for Indoor Applications"
caption={
<>
<AcademyIconLink size="small" fontSize="small" href="https://doi.org/10.1007/978-3-030-58380-4_7" />
<DescriptionIconLink
size="small"
fontSize="small"
href="https://drive.google.com/file/d/1wBfvAzy9N8uChNxNj9Kd_hcvI0Qq3uQj/view?usp=sharing"
/>
<Typography component="span" color="text.secondary" ml={1}>
Master Thesis @ WUT
</Typography>
</>
<Stack direction="row" gap={1} alignItems="center">
<AcademyIconLink pt={0.5} fontSize="small" href="https://doi.org/10.1007/978-3-030-58380-4_7" />
<DescriptionIconLink pt={0.5} href="https://drive.google.com/drive/folders/1Y7TJJN8z1llCkSTnpVHMELD3PqsX8VF9?usp=sharing" />
<Typography color="text.secondary">Master Thesis @ WUT</Typography>
</Stack>
}>
This thesis proposes a design for a small mobile robot controlled by voice. Firstly a preliminary analysis of the state of art of
human-robot interaction has been conducted. Then, an analysis of the equipment selected took place, and, later, appropriate methodologies
@@ -1,12 +1,9 @@
import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWithRouter as render } from 'components/utils/tests'
import { renderWithRouter as render } from 'components/utils/tests/index.js'
import ProjectsPage from '.'
vi.stubGlobal('open', vi.fn())
const open = vi.spyOn(window, 'open')
describe('ProjectsPage component', () => {
test('renders without crashing and displays the correct project titles', () => {
render(<ProjectsPage />)
@@ -25,8 +22,10 @@ describe('ProjectsPage component', () => {
test('opens the correct links when the link buttons are clicked', async () => {
render(<ProjectsPage />)
// test one of the many links
await userEvent.click(screen.getByLabelText('Video-button'))
expect(open).toHaveBeenLastCalledWith('https://www.linkedin.com/feed/update/urn:li:activity:7034480841569284096/', '_blank')
// inspect one of the many links
const link = screen.getByLabelText('Video-button')
expect(link).toHaveAttribute('href', 'https://www.linkedin.com/feed/update/urn:li:activity:7034480841569284096/')
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
})
@@ -22,13 +22,13 @@ const ProjectsPage = () => {
Software Engineer @ IIT
</Typography>
<VideoIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://www.linkedin.com/feed/update/urn:li:activity:7034480841569284096/"
sx={{ color: 'text.secondary' }}
/>
{/* <Typography color="text.secondary" display="inline-block"> */}
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Software Engineer @ IIT
</Typography>
</>
@@ -49,9 +49,9 @@ const ProjectsPage = () => {
<Typography color="text.secondary" display={{ sm: 'none' }}>
Software Engineer @ IIT
</Typography>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/ADVRHumanoids/modular_hhcm/" sx={{ color: 'text.secondary' }} />
<ExternalAppIconLink size="small" fontSize="small" href="https://modular.alberobotics.it" sx={{ color: 'text.secondary' }} />
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/ADVRHumanoids/modular_hhcm/" sx={{ color: 'text.secondary' }} />
<ExternalAppIconLink pt={0.5} fontSize="small" href="https://modular.alberobotics.it" sx={{ color: 'text.secondary' }} />
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Software Engineer @ IIT
</Typography>
</>
@@ -70,14 +70,14 @@ const ProjectsPage = () => {
<Typography color="text.secondary" display={{ sm: 'none' }}>
Master Thesis @ WUT
</Typography>
<AcademyIconLink size="small" fontSize="small" href="https://doi.org/10.1007/978-3-030-58380-4_7" sx={{ color: 'text.secondary' }} />
<AcademyIconLink pt={0.5} fontSize="small" href="https://doi.org/10.1007/978-3-030-58380-4_7" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/1wBfvAzy9N8uChNxNj9Kd_hcvI0Qq3uQj/view?usp=sharing"
href="https://drive.google.com/drive/folders/1Y7TJJN8z1llCkSTnpVHMELD3PqsX8VF9?usp=sharing"
sx={{ color: 'text.secondary' }}
/>
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Master Thesis @ WUT
</Typography>
</>
@@ -93,15 +93,12 @@ const ProjectsPage = () => {
<Project
title="Recharging Electric Cars with a Mobile Robot"
subtitle={
<>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/ekf_matlab" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
fontSize="small"
href="https://drive.google.com/file/d/1BgPj-fgtYDDEsFHmDk2fGVm7DCPFWEeN/view?usp=sharing"
sx={{ color: 'text.secondary' }}
/>
</>
<DescriptionIconLink
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/1wBfvAzy9N8uChNxNj9Kd_hcvI0Qq3uQj/view?usp=sharing"
sx={{ color: 'text.secondary' }}
/>
}>
<Typography>
The aim of this project was to prove the possibility to design an autonomous mobile robot whose task was to recharge electric cars in a
@@ -112,11 +109,11 @@ const ProjectsPage = () => {
title="Mobile Robot Self-Localization"
subtitle={
<>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/firebot" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/ekf_matlab" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/open?id=1a_ZvgaQjzvedeo296zrDpdvNL_VJDWTX"
href="https://drive.google.com/file/d/1BgPj-fgtYDDEsFHmDk2fGVm7DCPFWEeN/view?usp=sharing"
sx={{ color: 'text.secondary' }}
/>
</>
@@ -130,16 +127,11 @@ const ProjectsPage = () => {
title="Onychophora-Inspired Firefighting Robot"
subtitle={
<>
<GitHubIconLink
size="small"
fontSize="small"
href="https://github.com/m-tartari/3RRR_planar_parallel_manipulator"
sx={{ color: 'text.secondary' }}
/>
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/firebot" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/open?id=11lCJM3EjVsua379BKD5NcEDHQLL4jdQn"
href="https://drive.google.com/open?id=1a_ZvgaQjzvedeo296zrDpdvNL_VJDWTX"
sx={{ color: 'text.secondary' }}
/>
</>
@@ -155,13 +147,13 @@ const ProjectsPage = () => {
<>
{/* */}
<GitHubIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://github.com/m-tartari/simple_trajectory_optimization"
sx={{ color: 'text.secondary' }}
/>
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/open?id=11m7r7fLda_jpDEZ-9qAWvnfRN2kLkhlA"
sx={{ color: 'text.secondary' }}
@@ -176,12 +168,20 @@ const ProjectsPage = () => {
<Project
title="3RRR Planar Parallel Manipulator"
subtitle={
<GitHubIconLink
size="small"
fontSize="small"
href="https://github.com/m-tartari/3RRR_planar_parallel_manipulator"
sx={{ color: 'text.secondary' }}
/>
<>
<GitHubIconLink
pt={0.5}
fontSize="small"
href="https://github.com/m-tartari/3RRR_planar_parallel_manipulator"
sx={{ color: 'text.secondary' }}
/>
<DescriptionIconLink
pt={0.5}
fontSize="small"
href="https://drive.google.com/open?id=11lCJM3EjVsua379BKD5NcEDHQLL4jdQn"
sx={{ color: 'text.secondary' }}
/>
</>
}>
<Typography>This project concerns the design and optimization of a 3RRR planar parallel manipulator using MATLAB and CATIA</Typography>
</Project>
@@ -189,7 +189,7 @@ const ProjectsPage = () => {
title="Simple Swarm"
subtitle={
<>
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/simple_swarm" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/simple_swarm" sx={{ color: 'text.secondary' }} />
</>
}>
<Typography>
@@ -201,7 +201,7 @@ const ProjectsPage = () => {
subtitle={
<>
<GitHubIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://github.com/m-tartari/industrial_robot_programming"
sx={{ color: 'text.secondary' }}
@@ -215,7 +215,7 @@ const ProjectsPage = () => {
subtitle={
<>
<GitHubIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://github.com/m-tartari/modelling_and_control_of_manipulators"
sx={{ color: 'text.secondary' }}
@@ -240,13 +240,8 @@ const ProjectsPage = () => {
<Typography color="text.secondary" display={{ sm: 'none' }}>
Embedded Engineer @ IIT
</Typography>
<GitHubIconLink
size="small"
fontSize="small"
href="https://github.com/ADVRHumanoids/motor_calibration"
sx={{ color: 'text.secondary' }}
/>
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/ADVRHumanoids/motor_calibration" sx={{ color: 'text.secondary' }} />
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Embedded Engineer @ IIT
</Typography>
</>
@@ -268,12 +263,12 @@ const ProjectsPage = () => {
Bachelor Thesis @ TONGJI
</Typography>
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/0B1z42QMR-0UtYzBYSVhRV3ZPWk0/view?usp=sharing"
href="https://drive.google.com/file/d/0B1z42QMR-0UtYzBYSVhRV3ZPWk0/view?resourcekey=0-3O0frJlyho7TRKwNcy8f4g"
sx={{ color: 'text.secondary' }}
/>
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Bachelor Thesis @ TONGJI
</Typography>
</>
@@ -290,12 +285,12 @@ const ProjectsPage = () => {
Bachelor Thesis @ UNIBO
</Typography>
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/0B1z42QMR-0UtSkxRVzJPTk1IX0k/view?usp=sharing"
href="https://drive.google.com/file/d/0B1z42QMR-0UtSkxRVzJPTk1IX0k/view?resourcekey=0-LN4KALZlLWc_lKekqqQbIw"
sx={{ color: 'text.secondary' }}
/>
<Typography color="text.secondary" ml={1} display={{ xs: 'none', sm: 'inline' }}>
<Typography color="text.secondary" display={{ xs: 'none', sm: 'inline' }}>
Bachelor Thesis @ UNIBO
</Typography>
</>
@@ -308,14 +303,9 @@ const ProjectsPage = () => {
title="Control of a Nonlinear System"
subtitle={
<>
<GitHubIconLink
size="small"
fontSize="small"
href="https://github.com/m-tartari/modelling_and_control_of_manipulators"
sx={{ color: 'text.secondary' }}
/>
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/nonlinear_control" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/1TE3TWkXZjVq8OuBkLl497c_MQSs3MiLQ/view?usp=sharing"
sx={{ color: 'text.secondary' }}
@@ -330,14 +320,9 @@ const ProjectsPage = () => {
title="Multi-Body System's Kinematic Analysis"
subtitle={
<>
<GitHubIconLink
size="small"
fontSize="small"
href="https://github.com/m-tartari/multi-body-kinematics"
sx={{ color: 'text.secondary' }}
/>
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/multi-body-kinematics" sx={{ color: 'text.secondary' }} />
<DescriptionIconLink
size="small"
pt={0.5}
fontSize="small"
href="https://drive.google.com/file/d/15yrDU2blkb34Jy2GXkCIe4qNmDRmp31w/view?usp=sharing"
sx={{ color: 'text.secondary' }}
@@ -352,14 +337,14 @@ const ProjectsPage = () => {
<Project
title="Signal Processing"
subtitle={
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/signal_processing" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/signal_processing" sx={{ color: 'text.secondary' }} />
}>
<Typography>Several Signal processing projects developed using MATLAB.</Typography>
</Project>
<Project
title="Real Time Systems"
subtitle={
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/real_time_systems" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/real_time_systems" sx={{ color: 'text.secondary' }} />
}>
<Typography>Several real-time systems' projects developed using C++ and OIL, and run using Trampoline RTOS on a Teensy board.</Typography>
</Project>
@@ -373,7 +358,7 @@ const ProjectsPage = () => {
<Project
title="Medieval Furniture"
subtitle={
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/medieval_furniture" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/medieval_furniture" sx={{ color: 'text.secondary' }} />
}>
<Typography>
A collection of pdf and CAD models to create late XIV century furniture and medieval camp equipment. These objects are being realized by{' '}
@@ -386,7 +371,7 @@ const ProjectsPage = () => {
<Project
title="Pinterest Pinner"
subtitle={
<GitHubIconLink size="small" fontSize="small" href="https://github.com/m-tartari/pinterest_pinner" sx={{ color: 'text.secondary' }} />
<GitHubIconLink pt={0.5} fontSize="small" href="https://github.com/m-tartari/pinterest_pinner" sx={{ color: 'text.secondary' }} />
}>
<Typography>
Publishes all photos from a{' '}