Merge pull request #476 from m-tartari/fix/external_links
🐛 fix: update dead links to external resources
This commit is contained in:
@@ -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 +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 +0,0 @@
|
||||
export * from './IconLinks'
|
||||
+32
-14
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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}>
|
||||
© {year} Michele Tartari. All rights reserved.
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
<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,9 +1,24 @@
|
||||
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 }) => ({
|
||||
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,
|
||||
},
|
||||
@@ -14,7 +29,13 @@ const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({
|
||||
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')}>
|
||||
<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>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton aria-label="LinkedIn button" onClick={() => window.open('https://www.linkedin.com/in/m-tartari/', '_blank')}>
|
||||
</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>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton aria-label="GitHub button" onClick={() => window.open('https://www.github.com/m-tartari/', '_blank')}>
|
||||
</ListItemLink>
|
||||
<ListItemLink aria-label="GitHub link" href="https://www.github.com/m-tartari/">
|
||||
<GitHubIcon fontSize="large" />
|
||||
<Typography variant="h6" ml={2}>
|
||||
m-tartari
|
||||
</Typography>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
pt={0.5}
|
||||
fontSize="small"
|
||||
href="https://drive.google.com/file/d/1BgPj-fgtYDDEsFHmDk2fGVm7DCPFWEeN/view?usp=sharing"
|
||||
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"
|
||||
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{' '}
|
||||
|
||||
Reference in New Issue
Block a user