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
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# These are supported funding model platforms

github: L-Sophat
github: pphatdev
patreon: # Replace with a single Patreon username
open_collective: pphat
ko_fi: #sophat
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/contact/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { sendContactEmail } from '@lib/utils/email-service';
import { CONTACT_EMAIL } from '@lib/constants';
import { CONTACT_EMAIL } from '../../../lib/constants';

// Simple rate limiting for spam prevention
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
Expand Down
2 changes: 1 addition & 1 deletion src/app/contact/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Metadata } from "next";
import { appName, NEXT_PUBLIC_APP_URL } from "@lib/constants";
import { appName, NEXT_PUBLIC_APP_URL } from "../../lib/constants";
import { getOgImageMetadata } from "@lib/utils/og-image";

const contactDescription = "Get in touch with me. I'm always open to discussing new projects, creative ideas or opportunities to be part of your vision.";
Expand Down
23 changes: 12 additions & 11 deletions src/app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { BorderBeam } from "@components/ui/border-beam";
import { GridPattern } from "@components/ui/grid-pattern";
import { Ripple } from "@components/ui/ripple";
import { ContactHero } from "@components/heros/contact-hero";
import { cn } from "@lib/utils";

export default function ContactPage() {
// Form state
Expand Down Expand Up @@ -133,7 +134,7 @@ export default function ContactPage() {
<>
<NavigationBar/>
<ContactHero/>
<main className="min-h-screen relative pt-24 bg-gradient-to-b from-background via-muted/30 to-background">
<main className="relative pt-24 bg-gradient-to-b from-background via-muted/30 to-background">
<div className="absolute inset-0 pointer-events-none" aria-hidden="true">
<GridPattern
width={30}
Expand All @@ -147,20 +148,20 @@ export default function ContactPage() {
<div className="absolute overflow-hidden inset-0 pointer-events-none" aria-hidden="true">
<Ripple mainCircleSize={300} numCircles={10} className="opacity-30"/>
</div>
<BlurFade delay={0.7} className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<BlurFade delay={0.2} className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<div>
{/* Contact Form */}
<div className="translate-y-2">
<Card className="overflow-hidden bg-background/90 backdrop-blur-sm relative rounded-3xl border-border/50 shadow-lg shadow-primary/5">
<Card className="overflow-hidden bg-background/80 backdrop-blur-3xl relative rounded-4xl border-border/50 shadow-lg shadow-primary/5">
<CardContent className="p-6 sm:px-8">
{submitted ? (
<div className="text-center py-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-primary/10 rounded-full text-primary mb-4">
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<div className="text-center pb-12 pt-5">
<div className="inline-flex items-center justify-center size-20 bg-primary/10 rounded-full text-primary mb-4">
<svg className="size-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="text-2xl font-semibold mb-2">Message Sent!</h3>
<h3 className="text-2xl font-semibold mb-4">Message Sent!</h3>
<p className="text-muted-foreground mb-2">Thank you for reaching out. Your message has been sent to:</p>
<p className="font-medium text-primary mb-6">[email protected]</p>
<p className="text-sm text-muted-foreground mb-6">I'll get back to you as soon as possible.</p>
Expand All @@ -185,7 +186,7 @@ export default function ContactPage() {
required
value={formData.name}
onChange={handleChange}
className={formErrors.name ? "border-destructive ring-primary" : ""}
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
aria-invalid={Boolean(formErrors.name)}
/>
{formErrors.name && (
Expand All @@ -205,7 +206,7 @@ export default function ContactPage() {
required
value={formData.email}
onChange={handleChange}
className={formErrors.email ? "border-destructive ring-primary" : ""}
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
Copy link

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email input's error styling condition uses formErrors.name instead of formErrors.email, so it won't highlight email errors correctly. Update the condition to check formErrors.email.

Suggested change
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
className={cn(formErrors.email ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}

Copilot uses AI. Check for mistakes.
aria-invalid={Boolean(formErrors.email)}
/>
{formErrors.email && (
Expand All @@ -226,7 +227,7 @@ export default function ContactPage() {
required
value={formData.subject}
onChange={handleChange}
className={formErrors.subject ? "border-destructive ring-primary" : ""}
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
Copy link

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subject field's error styling condition is still checking formErrors.name instead of formErrors.subject. Change it to formErrors.subject to properly indicate errors.

Suggested change
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
className={cn(formErrors.subject ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}

Copilot uses AI. Check for mistakes.
aria-invalid={Boolean(formErrors.subject)}
/>
{formErrors.subject && (
Expand All @@ -247,7 +248,7 @@ export default function ContactPage() {
required
value={formData.message}
onChange={handleChange}
className={formErrors.message ? "border-destructive ring-primary" : ""}
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
Copy link

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message textarea's error condition references formErrors.name instead of formErrors.message. It should check formErrors.message to display validation errors correctly.

Suggested change
className={cn(formErrors.name ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}
className={cn(formErrors.message ? "border-destructive ring-primary" : "", "rounded-xl bg-transparent")}

Copilot uses AI. Check for mistakes.
aria-invalid={Boolean(formErrors.message)}
/>
{formErrors.message && (
Expand Down
2 changes: 0 additions & 2 deletions src/components/about-structured-data.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import React from 'react';
import {
appName,
Expand Down
2 changes: 0 additions & 2 deletions src/components/breadcrumb-structured-data.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import React from 'react';
import { NEXT_PUBLIC_APP_URL } from '@lib/constants';

Expand Down
4 changes: 1 addition & 3 deletions src/components/home-person-structured-data.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import React from 'react';
import {
NEXT_PUBLIC_APP_URL,
Expand Down Expand Up @@ -95,7 +93,7 @@ export default function HomePersonStructuredData() {
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.parse(JSON.stringify(structuredData))
__html: JSON.stringify(structuredData, null, 2)
}}
/>
);
Expand Down
2 changes: 0 additions & 2 deletions src/components/projects-structured-data.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import React from 'react';
import { appName, NEXT_PUBLIC_APP_URL } from '@lib/constants';

Expand Down
2 changes: 0 additions & 2 deletions src/components/website-structured-data.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import React from 'react';
import { NEXT_PUBLIC_APP_URL, PERSON_ALTERNATE_NAME, PERSON_JOB_TITLE, PERSON_NAME } from '@lib/constants';

Expand Down
2 changes: 1 addition & 1 deletion src/lib/meta/posts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { appName, NEXT_PUBLIC_APP_URL } from "@lib/constants";
import { appName, NEXT_PUBLIC_APP_URL } from "../../lib/constants";
import { icons } from "./icons";
import { keywords } from "./keywords";
import { Metadata } from "next";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils/og-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Utility functions for generating OpenGraph image URLs
*/

import { NEXT_PUBLIC_APP_URL } from "@lib/constants";
import { NEXT_PUBLIC_APP_URL } from "../../lib/constants";

type OgImageParams = {
title?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/test-structured-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ components.forEach((component: string) => {
const content: string = fs.readFileSync(componentPath, 'utf-8');

// Check if it imports from constants
if (content.includes('from "@lib/constants"') || content.includes('from \'@lib/constants\'')) {
if (content.includes('from "../lib/constants"') || content.includes('from \'../lib/constants\'')) {
Copy link

Copilot AI Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only checks for '../lib/constants' but many imports now use deeper paths (e.g., '../../lib/constants'). Consider matching both '../lib/constants' and '../../lib/constants' to avoid false negatives.

Suggested change
if (content.includes('from "../lib/constants"') || content.includes('from \'../lib/constants\'')) {
if (content.includes('from "../lib/constants"') || content.includes('from \'../lib/constants\'') ||
content.includes('from "../../lib/constants"') || content.includes('from \'../../lib/constants\'')) {

Copilot uses AI. Check for mistakes.
console.log(`\t${colors.green}✅ ${component} uses constants${colors.reset}`);
} else {
console.log(`\t${colors.red}❌ ${component} doesn't use constants${colors.reset}`);
Expand Down