Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Gmail configuration
GMAIL_USER=info.sophat@gmail.com
GMAIL_APP_PASSWORD=generated_password_here

# Contact information
CONTACT_PHONE="+855 96918 3363"
CONTACT_LOCATION="Phnom Penh, Cambodia"
CONTACT_EMAIL="info.sophat@gmail.com"

# Personal information
PERSON_NAME="Leat Sophat"
PERSON_ALTERNATE_NAME="PPhat"
PERSON_JOB_TITLE="Senior Front-end Developer"
PERSON_IMAGE="/assets/avatars/hero.webp"

# Address information
ADDRESS_STREET="Street 123, Sangkat Kamboul"
ADDRESS_LOCALITY="Phnom Penh"
ADDRESS_REGION="Phnom Penh"
ADDRESS_POSTAL_CODE="120905"
ADDRESS_COUNTRY="KH"

# Company information
COMPANY_NAME="TURBOTECH CO., LTD"
COMPANY_URL="https://turbotech.com.kh/"

# Social media links
GITHUB_URL="https://github.com/pphatdev"
LINKEDIN_URL="https://kh.linkedin.com/in/pphatdev"
TWITTER_URL="https://x.com/pphatdev"
FIGMA_URL="https://figma.com/@PPhat"

# Education
UNIVERSITY_NAME="Cambodian University for Specialties (CUS)"
UNIVERSITY_URL="https://www.cus.edu.kh/"

# Application configuration
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_API_URL="http://api.localhost:3000"
IS_PRODUCTION="false"
NEXTJS_ENV=development

# SEO
GOOGLE_SITE_VERIFICATION=""
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*
.env

# vercel
.vercel
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ git clone https://github.com/pphatdev/v4.git && cd ./v4
Install dependencies and launch the development server:

```shell
npm install && npm run dev
cp .env.example .env && npm install && npm run dev
```

This will:
Expand Down
9 changes: 5 additions & 4 deletions src/app/(web)/(home)/sections/timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ExperienceCard } from "@/components/ui/experience-card";
import Image from "next/image";
import React from "react";
import { motion } from 'framer-motion';
import { cn } from "@/lib/utils";

export const AboutTimeline = () => {

Expand Down Expand Up @@ -110,7 +111,7 @@ export const AboutTimeline = () => {
}}
initial="hidden"
animate="visible"
className='z-50 relative max-sm:pt-0 bg-gradient-to-b from-background to-transparent backdrop-blur-[2px] w-full py-10'>
className='z-50 relative max-sm:py-0 bg-gradient-to-b from-background to-transparent backdrop-blur-[2px] w-full py-10'>

<div className="mx-auto max-w-6xl w-full sm:px-4 sm:my-10">
<h1 className="w-full py-3 px-4 bg-background/80 max-sm:border-b sticky z-50 top-0 max-md:text-3xl text-4xl text-start tracking-tighter font-bold font-sans">
Expand All @@ -120,11 +121,11 @@ export const AboutTimeline = () => {
<div className="w-full sm:pl-7">
<div className="relative max-sm:p-4 sm:pb-12 max-sm:border-t sm:ml-[calc(2rem+1px)] mt-10 md:ml-[calc(3.5rem+1px)] lg:ml-[max(calc(14.5rem+1px),calc(100%-68rem))]">
<div className="hidden absolute top-3 bottom-0 right-full mr-7 md:mr-[3.25rem] w-px bg-foreground/20 sm:block"> </div>
<div className="space-y-12">
<div className="gap-12">
{experiences.map(({ works, title, companyLogo }, index) => (
<div key={index} className="flex flex-col">
<div className="flex gap-2 items-center pb-10 md:-translate-x-16">
{companyLogo && <Image src={`/${companyLogo}`} alt="Avatar" className="object-cover size-6 bg-background border rounded-md" width={32} height={32} />}
<div className={cn(`flex gap-2 items-center pb-10 md:-translate-x-16`, index === 0 ? "max-sm:mt-0" : "max-sm:mt-10")}>
{companyLogo && <Image src={`/${companyLogo}`} alt="Avatar" className="object-cover size-6 bg-background border max-sm:rounded-none rounded-md" width={32} height={32} />}
<h2 className="text-xl font-bold text-foreground/80"> {title} </h2>
</div>
<div className="flex flex-col gap-5 space-y-12">
Expand Down
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function RootLayout({ children, }: Readonly<{ children: React.Rea
defaultTheme="system"
>
{children}
<div className="fixed bottom-5 right-5 z-50 bg-foreground/10 p-px ring ring-primary/50 rounded-full shadow-lg">
<div className="fixed bottom-5 right-5 z-50 bg-foreground/10 p-px ring ring-primary/50 max-sm:ring-2 max-sm:rounded-none rounded-full shadow-lg">
<MessageButton />
</div>
</ThemeProvider>
Expand Down
2 changes: 1 addition & 1 deletion src/components/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from "next/link";
export const Logo = () => {
return (
<div className="w-12">
<Link href="/" className="items-center flex flex-col z-50 justify-start border rounded-xl py-2 border-primary/50" aria-label="Home">
<Link href="/" className="items-center flex flex-col z-50 justify-start border max-sm:rounded-none max-sm:border-foreground/5 rounded-xl py-2 border-primary/50" aria-label="Home">
<Image width={32} height={32} src={'/assets/logo/logo-transparent-dark-mode.png'} alt={"Logo"} className="hidden dark:block" />
<Image width={32} height={32} src={'/assets/logo/logo-transparent-light-mode.png'} alt={"Logo"} className="dark:hidden" />
{/* <Badge className="py-0.5 pt-1 h-fit -translate-y-3 bg-background text-[8px] uppercase" variant={"outline"}>PPhat</Badge> */}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/avatar-circles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const AvatarCircles: React.FC<AvatarCirclesProps> = ({
avatarUrls,
}: AvatarCirclesProps) => {
return (
<div className={cn("z-10 flex -space-x-6 rtl:space-x-reverse", className)}>
<div className={cn("z-10 flex max-sm:space-x-1 -space-x-6 rtl:space-x-reverse", className)}>
{avatarUrls.map((url, index) => (
<Link
key={index}
Expand All @@ -33,7 +33,7 @@ const AvatarCircles: React.FC<AvatarCirclesProps> = ({
>
<div className="relative hover:z-50 transition-all hover:scale-110 h-10 w-10">
<Image
className="rounded-full border-2 border-foreground/10"
className="rounded-full max-sm:rounded-none border-2 border-foreground/10"
src={url.imageUrl}
fill
sizes="40px"
Expand Down
16 changes: 7 additions & 9 deletions src/components/ui/experience-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,19 @@ export const ExperienceCard = (

<p className="text-xs my-2 font-medium">+ Basic Skills</p>
<div className="flex gap-2 flex-wrap">
{skills.map(({ icon, title }, key) => {
return (
<div key={key} className="flex space-x-1.5 shrink-0 w-fit shadow-card pr-2 shadow-primary/5 bg-background rounded-full items-center justify-center border p-px text-foreground/5 bg-[size:8px_8px] bg-top-left bg-[image:repeating-linear-gradient(315deg,currentColor_0,currentColor_1px,transparent_0,transparent_50%)]">
{icon && (<Image src={icon} width={32} height={32} alt={title} className="size-6 p-0.5 border border-foreground/10 rounded-full" />)}
<span className="text-xs text-foreground">{title}</span>
</div>
)
})}
{skills.map(({ icon, title }, key) =>
<div key={key} className="flex space-x-1.5 shrink-0 w-fit shadow-card pr-2 shadow-primary/5 bg-background max-sm:rounded-none rounded-full items-center justify-center border p-px text-foreground/5 bg-[size:8px_8px] bg-top-left bg-[image:repeating-linear-gradient(315deg,currentColor_0,currentColor_1px,transparent_0,transparent_50%)]">
{icon && (<Image src={icon} width={32} height={32} alt={title} className="size-6 p-0.5 border border-foreground/10 max-sm:rounded-none rounded-full" />)}
<span className="text-xs text-foreground">{title}</span>
</div>
)}
</div>
</div>
</div>

<dl className="absolute max-sm:left-0 pointer-events-none -top-1 lg:-left-[calc(14.5rem)] lg:mr-[calc(6.5rem+1px)]">
<dt className="sr-only">Date</dt>
<dd className="max-sm:text-[8px] text-xs max-sm:leading-4 leading-6 max-sm:border px-1.5 rounded-t-lg font-medium sm:text-sm whitespace-nowrap text-foreground/50">
<dd className="max-sm:text-[8px] max-sm:rounded-none text-xs max-sm:leading-4 leading-6 max-sm:border px-1.5 rounded-t-lg font-medium sm:text-sm whitespace-nowrap text-foreground/50">
{date}
</dd>
</dl>
Expand Down
12 changes: 6 additions & 6 deletions src/components/ui/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ const footerLinks: FooterSection[] = [

export function Footer() {
return (
<footer className="md:rounded-t-6xl max-sm:rounded-none relative w-full z-50 mx-auto flex flex-col items-center justify-center rounded-t-4xl border-t bg-[radial-gradient(35%_128px_at_50%_0%,theme(backgroundColor.white/8%),transparent)] px-6 py-12 lg:py-16">
<footer className="md:rounded-t-6xl max-sm:rounded-none max-sm:mt-2 relative w-full z-50 mx-auto flex flex-col items-center justify-center rounded-t-4xl border-t bg-[radial-gradient(35%_128px_at_50%_0%,theme(backgroundColor.white/8%),transparent)]">
<div className="bg-foreground/20 absolute top-0 right-1/2 left-1/2 h-px w-1/3 -translate-x-1/2 -translate-y-1/2 rounded-full blur" />
<div className="grid w-full max-w-6xl gap-8 xl:grid-cols-3 xl:gap-8">
<AnimatedContainer className="space-y-4">
<div className="grid w-full max-w-6xl max-sm:gap-0 gap-8 xl:grid-cols-3 xl:gap-8">
<AnimatedContainer className="space-y-4 max-sm:order-last max-sm:py-10 py-12 max-sm:text-center lg:py-16 px-6 max-sm:flex flex-col items-center justify-center">
<Logo/>
<p className="text-muted-foreground mt-8 text-sm md:mt-0">
<p className="text-muted-foreground max-sm:mt-0 mt-8 text-sm md:mt-0">
© {new Date().getFullYear()} {APP_SHORT_NAME}. All rights reserved.
</p>
</AnimatedContainer>

<div className="mt-10 grid grid-cols-2 gap-8 sm:grid-cols-3 md:col-span-2 xl:mt-0">
<div className="sm:mt-10 grid grid-cols-2 max-sm:gap-0 gap-8 sm:grid-cols-3 md:col-span-2 xl:mt-0 max-sm:px-4 max-sm:border-b px-6 py-12 lg:py-16">
{footerLinks.map((section, index) => (
<AnimatedContainer key={section.label} delay={0.1 + index * 0.1}>
<div className="mb-10 md:mb-0">
<div className="max-sm:mb-5 mb-10 md:mb-0 divide divide-foreground/10">
<h3 className="text-xs">{section.label}</h3>
<ul className="text-muted-foreground mt-4 space-y-2 text-sm">
{section.links.map((link) => (
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function MessageButton() {
return (
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger asChild>
<Button variant="secondary" size={'icon'} className="rounded-full size-10 p-2 animate-ripple" aria-label="Open message dialog">
<Button variant="secondary" size={'icon'} className="rounded-sm size-10 p-2 animate-ripple" aria-label="Open message dialog">
<Mail className="size-5" />
</Button>
</DrawerTrigger>
Expand Down
8 changes: 4 additions & 4 deletions src/components/ui/project-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const ProjectCard: React.FC<{ project: Project, className?: string }> = ({ proje

<Image src={NEXT_PUBLIC_API_URL + project.image} width={512} height={512} alt={project.name || ""} className="object-cover w-full aspect-video border duration-300 transition-all ease-in-out max-sm:rounded-none rounded-4xl" />

<div className='bg-background/30 ring-1 w-fit absolute top-4 right-4 ml-auto ring-foreground/10 justify-end flex rounded-full p-1'>
<div className='bg-background/30 ring-1 w-fit absolute top-4 right-4 ml-auto ring-foreground/10 justify-end flex rounded-full sm:rounded-none p-1'>
{project.source.map((source, index) => (
<Link
key={index}
target="_blank"
href={source.url}
aria-label={source.type === 'demo' ? 'View live demo' : 'View source code'}
title={source.type === 'demo' ? 'View live demo' : 'View source code'}
className="flex rounded-full p-2 hover:ring ring-foreground/20 hover:bg-background/40 text-foreground/90 hover:text-foreground transition-all items-center justify-center">
className="flex max-sm:rounded-none rounded-full p-2 hover:ring ring-foreground/20 hover:bg-background/40 text-foreground/90 hover:text-foreground transition-all items-center justify-center">
{source.type === 'demo' && <ExternalLinkIcon className="size-4" />}
{source.type === 'source' && <GlobeIcon className="size-4" />}
</Link>
Expand All @@ -44,7 +44,7 @@ const ProjectCard: React.FC<{ project: Project, className?: string }> = ({ proje
<header className='mb-2 px-4 relative flex justify-between items-center'>
<div className="flex gap-2 items-center">
{(project?.tags ?? []).slice(0, 3).map((language, index) => (
<Badge key={index} variant={'outline'} className="font-aladin leading-5 rounded-full bg-foreground/5">{language}</Badge>
<Badge key={index} variant={'outline'} className="font-aladin leading-5 max-sm:rounded-none rounded-full bg-foreground/5">{language}</Badge>
))}
</div>
</header>
Expand All @@ -58,7 +58,7 @@ const ProjectCard: React.FC<{ project: Project, className?: string }> = ({ proje
<p className='font-normal px-4 z-10 line-clamp-2 text-foreground/80'>{project.description}</p>

<footer className="mt-auto px-4 pb-4 flex justify-between pt-2 z-10">
<div className='bg-foreground/5 ring-1 w-fit ring-foreground/10 justify-end flex ga rounded-full p-1'>
<div className='bg-foreground/5 ring-1 w-fit ring-foreground/10 justify-end flex max-sm:rounded-none rounded-full p-1'>
<AvatarCircles numPeople={avatars.length - 4} avatarUrls={avatars} />
</div>
</footer>
Expand Down
6 changes: 3 additions & 3 deletions src/components/ui/theme-switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
return (
<div
className={cn(
"flex w-16 h-8 p-1 rounded-full scale-3d scale-90 sm:scale-100 cursor-pointer sm:mt-5 transition-all duration-300",
"flex w-16 h-8 p-1 max-sm:rounded-none rounded-full scale-3d scale-90 sm:scale-100 cursor-pointer sm:mt-5 transition-all duration-300",
isDark ? "bg-background/20 border border-foreground/20" : "bg-background/10 border border-foreground/10",
className
)}
Expand All @@ -51,7 +51,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
<div className="flex justify-between items-center w-full">
<div
className={cn(
"flex justify-center items-center w-6 h-6 rounded-full transition-transform duration-300",
"flex justify-center items-center w-6 h-6 max-sm:rounded-none rounded-full transition-transform duration-300",
isDark
? "transform translate-x-0 bg-foreground/20 border border-foreground/10"
: "transform translate-x-8 bg-foreground/10 border border-foreground/10"
Expand All @@ -64,7 +64,7 @@ export function ThemeToggle({ className }: ThemeToggleProps) {
</div>
<div
className={cn(
"flex justify-center items-center w-6 h-6 rounded-full transition-transform duration-300",
"flex justify-center items-center w-6 h-6 max-sm:rounded-none rounded-full transition-transform duration-300",
isDark ? "bg-background/50" : "transform -translate-x-8"
)}
>
Expand Down