added project list page
This commit is contained in:
parent
f1c7948d0a
commit
27b05c2904
@ -31,7 +31,7 @@
|
|||||||
"eslint": "8.41.0",
|
"eslint": "8.41.0",
|
||||||
"eslint-config-next": "13.4.4",
|
"eslint-config-next": "13.4.4",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"postcss": "^8.4.24",
|
"postcss": "^8.4.31",
|
||||||
"postcss-preset-mantine": "1.8.0",
|
"postcss-preset-mantine": "1.8.0",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"prettier": "3.0.3",
|
"prettier": "3.0.3",
|
||||||
|
@ -50,7 +50,7 @@ devDependencies:
|
|||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.0.0(eslint@8.41.0)
|
version: 9.0.0(eslint@8.41.0)
|
||||||
postcss:
|
postcss:
|
||||||
specifier: ^8.4.24
|
specifier: ^8.4.31
|
||||||
version: 8.4.31
|
version: 8.4.31
|
||||||
postcss-preset-mantine:
|
postcss-preset-mantine:
|
||||||
specifier: 1.8.0
|
specifier: 1.8.0
|
||||||
@ -362,8 +362,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
|
resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/prop-types@15.7.8:
|
/@types/prop-types@15.7.9:
|
||||||
resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==}
|
resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==}
|
||||||
|
|
||||||
/@types/react-dom@18.2.4:
|
/@types/react-dom@18.2.4:
|
||||||
resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==}
|
resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==}
|
||||||
@ -374,12 +374,12 @@ packages:
|
|||||||
/@types/react@18.2.7:
|
/@types/react@18.2.7:
|
||||||
resolution: {integrity: sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==}
|
resolution: {integrity: sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/prop-types': 15.7.8
|
'@types/prop-types': 15.7.9
|
||||||
'@types/scheduler': 0.16.4
|
'@types/scheduler': 0.16.5
|
||||||
csstype: 3.1.2
|
csstype: 3.1.2
|
||||||
|
|
||||||
/@types/scheduler@0.16.4:
|
/@types/scheduler@0.16.5:
|
||||||
resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==}
|
resolution: {integrity: sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==}
|
||||||
|
|
||||||
/@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4):
|
/@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4):
|
||||||
resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
|
resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
|
||||||
@ -640,8 +640,8 @@ packages:
|
|||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-lite@1.0.30001547:
|
/caniuse-lite@1.0.30001551:
|
||||||
resolution: {integrity: sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==}
|
resolution: {integrity: sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/chalk@4.1.2:
|
/chalk@4.1.2:
|
||||||
@ -813,7 +813,7 @@ packages:
|
|||||||
is-string: 1.0.7
|
is-string: 1.0.7
|
||||||
is-typed-array: 1.1.12
|
is-typed-array: 1.1.12
|
||||||
is-weakref: 1.0.2
|
is-weakref: 1.0.2
|
||||||
object-inspect: 1.12.3
|
object-inspect: 1.13.0
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
object.assign: 4.1.4
|
object.assign: 4.1.4
|
||||||
regexp.prototype.flags: 1.5.1
|
regexp.prototype.flags: 1.5.1
|
||||||
@ -1778,7 +1778,7 @@ packages:
|
|||||||
'@next/env': 13.4.4
|
'@next/env': 13.4.4
|
||||||
'@swc/helpers': 0.5.1
|
'@swc/helpers': 0.5.1
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
caniuse-lite: 1.0.30001547
|
caniuse-lite: 1.0.30001551
|
||||||
postcss: 8.4.14
|
postcss: 8.4.14
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
@ -1803,8 +1803,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
/object-inspect@1.12.3:
|
/object-inspect@1.13.0:
|
||||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
resolution: {integrity: sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/object-keys@1.1.1:
|
/object-keys@1.1.1:
|
||||||
@ -2278,7 +2278,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
get-intrinsic: 1.2.1
|
get-intrinsic: 1.2.1
|
||||||
object-inspect: 1.12.3
|
object-inspect: 1.13.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/slash@3.0.0:
|
/slash@3.0.0:
|
||||||
|
BIN
public/assets/project-1.png
Normal file
BIN
public/assets/project-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 710 KiB |
BIN
public/assets/project-2.png
Normal file
BIN
public/assets/project-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
BIN
public/assets/project-3.png
Normal file
BIN
public/assets/project-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
@ -12,7 +12,10 @@ export const metadata: Metadata = {
|
|||||||
url: 'https://github.com/Refansa',
|
url: 'https://github.com/Refansa',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
title: 'Refansa - Software Developer',
|
title: {
|
||||||
|
template: 'Refansa - Software Developer | %s',
|
||||||
|
default: 'Refansa - Software Developer',
|
||||||
|
},
|
||||||
description: "Refansa's portfolio website",
|
description: "Refansa's portfolio website",
|
||||||
viewport: 'width=device-width, initial-scale=1',
|
viewport: 'width=device-width, initial-scale=1',
|
||||||
colorScheme: 'dark',
|
colorScheme: 'dark',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { AppShell, Container, Stack, rem } from '@mantine/core'
|
import { AppShell, Container, Stack } from '@mantine/core'
|
||||||
import Introduction from '../partials/Introduction'
|
import Introduction from '../partials/Introduction'
|
||||||
import About from '../partials/About'
|
import About from '../partials/About'
|
||||||
import Navbar from '../components/Navbar'
|
import Navbar from '../components/Navbar'
|
||||||
@ -19,7 +19,7 @@ export default function HomePage() {
|
|||||||
</AppShell.Header>
|
</AppShell.Header>
|
||||||
<AppShell.Main>
|
<AppShell.Main>
|
||||||
<Container
|
<Container
|
||||||
size={rem(1920)}
|
size={1920}
|
||||||
px={{ base: '0rem', lg: '4rem', xl: '8rem' }}>
|
px={{ base: '0rem', lg: '4rem', xl: '8rem' }}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Introduction />
|
<Introduction />
|
||||||
|
45
src/app/projects/page.tsx
Normal file
45
src/app/projects/page.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { Metadata } from 'next'
|
||||||
|
import Project from './project'
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Projects',
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectLists = [
|
||||||
|
{
|
||||||
|
title: 'AmanaTax',
|
||||||
|
description: 'A video course website to learn about taxes.',
|
||||||
|
imgSrc: '/assets/project-1.png',
|
||||||
|
alt: 'AmanaTax Homepage',
|
||||||
|
tags: ['internship project', 'laravel', 'completed'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Koperasi',
|
||||||
|
description: 'Cooperatives website that accept online transactions.',
|
||||||
|
codeSrc: 'https://github.com/Refansa/koperasi',
|
||||||
|
imgSrc: '/assets/project-2.png',
|
||||||
|
alt: 'Koperasi Homepage',
|
||||||
|
tags: ['school project', 'vue.js', 'completed'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'RMBG',
|
||||||
|
description:
|
||||||
|
'Easily remove background from an image with one simple click.',
|
||||||
|
codeSrc: 'https://github.com/Refansa/rmbg',
|
||||||
|
imgSrc: '/assets/project-3.png',
|
||||||
|
alt: 'RMBG Website',
|
||||||
|
tags: ['personal project', 'react.js', 'completed'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'This Portfolio',
|
||||||
|
description: 'My own portfolio!',
|
||||||
|
codeSrc: 'https://github.com/Refansa/portfolio',
|
||||||
|
imgSrc: '/assets/project-4.png',
|
||||||
|
alt: 'Portfolio Homepage',
|
||||||
|
tags: ['personal project', 'react', 'in progress'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ProjectLists() {
|
||||||
|
return <Project projectLists={projectLists} />
|
||||||
|
}
|
34
src/app/projects/project.tsx
Normal file
34
src/app/projects/project.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { AppShell, Container, Stack } from '@mantine/core'
|
||||||
|
import Footer from '../../components/Footer'
|
||||||
|
import Navbar from '../../components/Navbar'
|
||||||
|
import Projects, { ProjectItemProps } from '../../partials/Projects'
|
||||||
|
|
||||||
|
export default function Project({
|
||||||
|
projectLists,
|
||||||
|
}: {
|
||||||
|
projectLists: ProjectItemProps[]
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AppShell
|
||||||
|
withBorder={false}
|
||||||
|
padding={'xl'}
|
||||||
|
header={{ height: 72 }}
|
||||||
|
className={'shell-root'}>
|
||||||
|
<AppShell.Header>
|
||||||
|
<Navbar />
|
||||||
|
</AppShell.Header>
|
||||||
|
<AppShell.Main>
|
||||||
|
<Container
|
||||||
|
size={1920}
|
||||||
|
px={{ base: '0rem', lg: '4rem', xl: '8rem' }}>
|
||||||
|
<Stack>
|
||||||
|
<Projects projectLists={projectLists} />
|
||||||
|
<Footer />
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
|
)
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { Button } from '@mantine/core'
|
import { Button } from '@mantine/core'
|
||||||
|
import Link from 'next/link'
|
||||||
import { MouseEventHandler } from 'react'
|
import { MouseEventHandler } from 'react'
|
||||||
|
|
||||||
interface NavLinkProps {
|
interface NavLinkProps {
|
||||||
@ -10,7 +11,7 @@ interface NavLinkProps {
|
|||||||
function NavLink({ content, href, onClick }: NavLinkProps) {
|
function NavLink({ content, href, onClick }: NavLinkProps) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
component={'a'}
|
component={Link}
|
||||||
variant='subtle'
|
variant='subtle'
|
||||||
size='md'
|
size='md'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
@ -21,7 +21,7 @@ export default function NavMobile() {
|
|||||||
/>
|
/>
|
||||||
<NavLink
|
<NavLink
|
||||||
content='Projects'
|
content='Projects'
|
||||||
href='https://github.com/Refansa?tab=repositories'
|
href='/projects'
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
@ -14,11 +14,11 @@ function Navbar() {
|
|||||||
<Group visibleFrom='sm'>
|
<Group visibleFrom='sm'>
|
||||||
<NavLink
|
<NavLink
|
||||||
content='About'
|
content='About'
|
||||||
href='/#about'
|
href='/'
|
||||||
/>
|
/>
|
||||||
<NavLink
|
<NavLink
|
||||||
content='Projects'
|
content='Projects'
|
||||||
href='https://github.com/Refansa?tab=repositories'
|
href='/projects'
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<NavMobile />
|
<NavMobile />
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import { motion, useAnimation } from 'framer-motion'
|
import { motion, useAnimation } from 'framer-motion'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useInView } from 'react-intersection-observer'
|
import { useInView } from 'react-intersection-observer'
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
.grayscale {
|
.grayscale {
|
||||||
filter: grayscale(1);
|
filter: grayscale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Offset the anchor hashtag link
|
|
||||||
*/
|
|
||||||
.anchor:target::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
height: 160px;
|
|
||||||
margin-top: -160px;
|
|
||||||
}
|
|
||||||
|
@ -14,10 +14,7 @@ import SlideUpWhenVisible from '../hooks/slideUpWhenVisible'
|
|||||||
function About() {
|
function About() {
|
||||||
return (
|
return (
|
||||||
<SlideUpWhenVisible>
|
<SlideUpWhenVisible>
|
||||||
<Box
|
<Box my='xl'>
|
||||||
id='about'
|
|
||||||
className={classes.anchor}
|
|
||||||
my='xl'>
|
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
cols={{ base: 1, sm: 2 }}
|
cols={{ base: 1, sm: 2 }}
|
||||||
spacing={{ base: 'sm', md: 'md', lg: 'lg' }}>
|
spacing={{ base: 'sm', md: 'md', lg: 'lg' }}>
|
||||||
|
@ -24,7 +24,7 @@ interface ItemGridProps {
|
|||||||
text: string
|
text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemGrid = ({ icon, text }: ItemGridProps) => {
|
const Item = ({ icon, text }: ItemGridProps) => {
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
w={100}
|
w={100}
|
||||||
@ -64,67 +64,67 @@ export default function Experiences() {
|
|||||||
mt='xl'
|
mt='xl'
|
||||||
w={{ base: 240, sm: 480, md: 720, lg: 960 }}
|
w={{ base: 240, sm: 480, md: 720, lg: 960 }}
|
||||||
cols={{ base: 2, sm: 4, md: 6, lg: 8 }}>
|
cols={{ base: 2, sm: 4, md: 6, lg: 8 }}>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandHtml5 />}
|
icon={<IconBrandHtml5 />}
|
||||||
text='HTML'
|
text='HTML'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandCss3 />}
|
icon={<IconBrandCss3 />}
|
||||||
text='CSS'
|
text='CSS'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandJavascript />}
|
icon={<IconBrandJavascript />}
|
||||||
text='Javascript'
|
text='Javascript'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandPhp />}
|
icon={<IconBrandPhp />}
|
||||||
text='PHP'
|
text='PHP'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandPython />}
|
icon={<IconBrandPython />}
|
||||||
text='Python'
|
text='Python'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandMysql />}
|
icon={<IconBrandMysql />}
|
||||||
text='MySQL'
|
text='MySQL'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandLaravel />}
|
icon={<IconBrandLaravel />}
|
||||||
text='Laravel'
|
text='Laravel'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandNodejs />}
|
icon={<IconBrandNodejs />}
|
||||||
text='NodeJS'
|
text='NodeJS'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandReact />}
|
icon={<IconBrandReact />}
|
||||||
text='React'
|
text='React'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandVue />}
|
icon={<IconBrandVue />}
|
||||||
text='Vue'
|
text='Vue'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandTailwind />}
|
icon={<IconBrandTailwind />}
|
||||||
text='Tailwind'
|
text='Tailwind'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandSass />}
|
icon={<IconBrandSass />}
|
||||||
text='Sass'
|
text='Sass'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandMantine />}
|
icon={<IconBrandMantine />}
|
||||||
text='Mantine'
|
text='Mantine'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandNextjs />}
|
icon={<IconBrandNextjs />}
|
||||||
text='NextJS'
|
text='NextJS'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandPrisma />}
|
icon={<IconBrandPrisma />}
|
||||||
text='Prisma'
|
text='Prisma'
|
||||||
/>
|
/>
|
||||||
<ItemGrid
|
<Item
|
||||||
icon={<IconBrandSupabase />}
|
icon={<IconBrandSupabase />}
|
||||||
text='Supabase'
|
text='Supabase'
|
||||||
/>
|
/>
|
||||||
|
119
src/partials/Projects.tsx
Normal file
119
src/partials/Projects.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import {
|
||||||
|
AspectRatio,
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Group,
|
||||||
|
Image,
|
||||||
|
SimpleGrid,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import { IconSourceCode } from '@tabler/icons-react'
|
||||||
|
import SlideUpWhenVisible from '../hooks/slideUpWhenVisible'
|
||||||
|
|
||||||
|
export interface ProjectItemProps {
|
||||||
|
alt?: string
|
||||||
|
codeSrc?: any
|
||||||
|
description: string
|
||||||
|
imgSrc?: any
|
||||||
|
tags?: string[]
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectItem = ({
|
||||||
|
alt,
|
||||||
|
codeSrc,
|
||||||
|
description,
|
||||||
|
imgSrc,
|
||||||
|
tags,
|
||||||
|
title,
|
||||||
|
}: ProjectItemProps) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
shadow='sm'
|
||||||
|
padding='lg'>
|
||||||
|
<Card.Section>
|
||||||
|
<AspectRatio
|
||||||
|
ratio={1366 / 609}
|
||||||
|
mah={160}
|
||||||
|
my='auto'>
|
||||||
|
<Image
|
||||||
|
src={imgSrc}
|
||||||
|
alt={alt}
|
||||||
|
/>
|
||||||
|
</AspectRatio>
|
||||||
|
</Card.Section>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
fw='bold'
|
||||||
|
size='lg'
|
||||||
|
mt='md'>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
{tags ? (
|
||||||
|
<Group
|
||||||
|
gap='xs'
|
||||||
|
my='md'>
|
||||||
|
{tags.map((tag) => {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
key={title}
|
||||||
|
radius='xs'
|
||||||
|
variant='outline'>
|
||||||
|
{tag}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Group>
|
||||||
|
) : null}
|
||||||
|
<Text
|
||||||
|
size='sm'
|
||||||
|
c='dimmed'
|
||||||
|
mb='md'>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
{codeSrc ? (
|
||||||
|
<Button
|
||||||
|
mt='auto'
|
||||||
|
component='a'
|
||||||
|
href={codeSrc}
|
||||||
|
target='_blank'
|
||||||
|
leftSection={<IconSourceCode />}
|
||||||
|
variant='light'>
|
||||||
|
Source Code
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Projects({
|
||||||
|
projectLists,
|
||||||
|
}: {
|
||||||
|
projectLists: ProjectItemProps[]
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SlideUpWhenVisible>
|
||||||
|
<Box my='xl'>
|
||||||
|
<Stack align='center'>
|
||||||
|
<Title ta='center'>My Projects</Title>
|
||||||
|
<SimpleGrid
|
||||||
|
mt='xl'
|
||||||
|
cols={{ base: 1, sm: 2, md: 3 }}>
|
||||||
|
{projectLists.map((project) => {
|
||||||
|
return (
|
||||||
|
<ProjectItem
|
||||||
|
key={project.title}
|
||||||
|
{...project}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</SlideUpWhenVisible>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user