added project list page

This commit is contained in:
Refansa 2023-10-19 20:02:20 +07:00
parent f1c7948d0a
commit 27b05c2904
17 changed files with 244 additions and 52 deletions

View File

@ -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",

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -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',

View File

@ -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
View 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} />
}

View 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>
)
}

View File

@ -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}

View File

@ -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>

View File

@ -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 />

View File

@ -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'

View File

@ -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;
}

View File

@ -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' }}>

View File

@ -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
View 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>
)
}