Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cb2d82b
Merge pull request #103 from GDGoCINHA/develop
Ruthgyeul May 7, 2025
22f54ac
Merge pull request #113 from GDGoCINHA/develop
Ruthgyeul May 7, 2025
22918ff
Enhance: update error.js design #116
Ruthgyeul May 8, 2025
18cd397
Enhance: update loading.js design #116
Ruthgyeul May 8, 2025
7aa1cd7
Enhance: update not-found.js design #116
Ruthgyeul May 8, 2025
27a1106
Enhance: update error.js design #116
Ruthgyeul May 8, 2025
163fb1e
Feat: create loading.js #116
Ruthgyeul May 8, 2025
332fc12
Enhance: update layout.js to cover suspense and error handle and bett…
Ruthgyeul May 8, 2025
e5f31b6
Enhance: add react error boundary #116
Ruthgyeul May 8, 2025
b6c2bbf
Enhance: update error logic #116
Ruthgyeul May 8, 2025
7fe36fb
Enhance: add forbidden page #116
Ruthgyeul May 8, 2025
dd1c4a3
Enhance: update nor-found page #116
Ruthgyeul May 8, 2025
ec82516
Enhance: update manifest and metadata of layout.js #116
Ruthgyeul May 8, 2025
9ef54b0
Enhance: add unzuthorized page #116
Ruthgyeul May 8, 2025
13db44a
Fix: fix critical vulnerability #116
Ruthgyeul May 8, 2025
bf8588b
Fix: update size error
Ruthgyeul May 8, 2025
a7d1115
Fix: add default reset handler
Ruthgyeul May 8, 2025
cf4b1cd
Ref: move to services dir #96
Ruthgyeul May 8, 2025
04e3d28
Ref: refactored for clean code #96
Ruthgyeul May 8, 2025
18d4833
Update: add image upload API header #96
Ruthgyeul May 8, 2025
fe3fa90
Merge pull request #117 from GDGoCINHA/enhance116/specialComponents
Ruthgyeul May 8, 2025
72a8aa4
Merge pull request #118 from GDGoCINHA/fix96/studyAPI
Ruthgyeul May 8, 2025
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
412 changes: 222 additions & 190 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
"gsap": "^3.12.7",
"hangul-js": "^0.2.6",
"lucide-react": "^0.507.0",
"next": "15.0.2",
"next": ">=15.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^6.0.0",
"react-icons": "^5.4.0",
"type-hangul": "^0.2.4"
},
Expand Down
8 changes: 4 additions & 4 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "GDGoC INHA University",
"short_name": "GDGoC Inha Univ.",
"name": "GDGoC INHA Univ.",
"short_name": "GDGoC INHA",
"description": "Google Developer Group on Campus at Inha University",
"start_url": "/",
"display": "standalone",
Expand Down Expand Up @@ -34,7 +34,7 @@
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"sizes": "144x70",
"type": "image/png",
"purpose": "any maskable"
},
Expand Down Expand Up @@ -110,7 +110,7 @@
"sizes": "1280x720",
"type": "image/png",
"platform": "wide",
"label": "GDGoC INHA 홈페이지"
"label": "GDGoC INHA Home"
}
]
}
117 changes: 93 additions & 24 deletions src/app/error.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,118 @@
'use client';

import React from 'react';
import {Button} from "@nextui-org/react";
import {useRouter} from "next/navigation";
import { useState, useEffect } from 'react';
import { Button } from "@nextui-org/react";
import Image from 'next/image';

// components
import Forbidden from '@/app/forbidden';
import Unauthorized from '@/app/unauthorized';

// resource
import gdgocIcon from "@public/src/images/GDGoC_icon.png";

export default function Error({error, reset}) {
const router = useRouter();
const errorCode = error?.statusCode || error?.status || 'Internal Server';
/**
* @typedef {Object} ErrorProps
* @property {Error} error - The error object
* @property {() => void} reset - Function to reset the error state
*/
export default function Error({ error, reset = () => {} }) {
const [countdown, setCountdown] = useState(5);
const errorCode = error?.statusCode || error?.status || 500;
const errorTitle = error?.title || 'Internal Server Error';
const errorMessage = error?.message || 'Unknown Error has occurred';

useEffect(() => {
if (process.env.NODE_ENV === 'development') {
console.log(error || 'Unknown Error has occurred');
}
}, []);

useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
}
}, [countdown]);

const handleClick = () => {
reset();
window.location.reload();
};

// Handle specific error codes
if (errorCode === 401) {
return <Unauthorized />;
}

if (errorCode === 403) {
return <Forbidden />;
}

return (
<div className="flex flex-col items-center justify-center min-h-screen relative">
<div className="absolute inset-0 flex items-center justify-center opacity-35 pointer-events-none z-0">
<Image
src={gdgocIcon}
alt="GDGoC Icon"
width={500}
height={500}
/>
<div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-gray-900 to-black relative overflow-hidden">
{/* 아이콘 배경 */}
<div className="absolute inset-0 flex items-center justify-center opacity-10 pointer-events-none z-0" aria-hidden="true">
<div className="relative">
<Image
src={gdgocIcon}
alt="GDGoC Icon"
width={600}
height={600}
className="animate-pulse"
priority
/>
</div>
</div>

<div className="z-10">
<div className="flex flex-col items-center justify-center">
<h1 className="text-3xl font-bold text-white">{errorCode} Error</h1>
<p className="mt-2 text-lg text-white">페이지 로드 중 에러가 발생하였습니다.</p>
<p className="text-lg text-white">개발팀으로 연락 바랍니다!</p>
{/* only at development env */}
{/* 오류 박스 */}
<div className="z-10 bg-gray-800/70 backdrop-blur-sm p-4 sm:p-8 rounded-xl shadow-2xl border border-red-500/20 max-w-[90%] sm:max-w-md w-full mx-4">
<div className="flex flex-col items-center justify-center text-center">
{/* 오류 아이콘 */}
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-red-500/10 flex items-center justify-center mb-4 sm:mb-6" role="img" aria-label="Error icon">
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 sm:h-10 sm:w-10 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>

{/* 오류 내용 */}
<h1 className="text-3xl sm:text-4xl font-bold text-white mb-2">
<p className="text-red-500 text-2xl sm:text-3xl md:text-4xl lg:text-5xl">{errorCode}</p>
<span className="block text-white text-base sm:text-lg md:text-xl lg:text-2xl">{errorTitle}</span>
</h1>
<div className="w-12 sm:w-16 h-1 bg-gradient-to-r from-red-500 to-red-400 rounded-full mb-3 sm:mb-4" aria-hidden="true"></div>

<div className="bg-red-500/10 py-3 px-6 rounded-lg border border-red-500/20">
<p className="text-base sm:text-lg text-red-100">페이지 로드 중 에러가 발생하였습니다.</p>
<p className="text-base sm:text-lg text-red-100">Tech팀으로 연락 바랍니다!</p>
</div>

{/* 개발 환경에서만 표시 */}
{process.env.NODE_ENV === 'development' && (
<div className="mt-4 p-4 bg-[#1f1f1f] rounded-lg max-w-md">
<p className="text-yellow-500 text-sm font-mono break-words">{errorMessage}</p>
<div className="mt-2 p-3 sm:p-4 bg-red-500/5 rounded-lg border border-red-500/20 max-w-[90%] sm:max-w-md w-full overflow-auto">
<p className="text-yellow-400 text-xs sm:text-sm font-mono break-words">{errorMessage}</p>
</div>
)}

{/* 버튼 */}
<Button
onPress={handleClick}
className="mt-8 w-72 max-w-sm h-12 bg-red-500 text-white text-lg font-semibold rounded-lg"
className="mt-6 sm:mt-8 w-full max-w-[280px] h-10 sm:h-12 bg-gradient-to-r from-red-500 to-red-400 hover:from-red-400 hover:to-red-500 text-white text-base sm:text-lg font-semibold rounded-lg transition-all duration-300 shadow-lg hover:shadow-red-500/30"
aria-label={`다시 시도하기 ${countdown > 0 ? `(${countdown}초)` : ''}`}
>
다시 시도하기
다시 시도하기 {countdown > 0 && `(${countdown}초)`}
</Button>

{/* 작은 로고 */}
<div className="mt-4 sm:mt-6">
<Image
src={gdgocIcon}
alt="GDGoC Small Icon"
width={32}
height={32}
className="opacity-50"
/>
</div>
</div>
</div>
</div>
Expand Down
83 changes: 83 additions & 0 deletions src/app/forbidden.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// this is experimental page for nextjs
'use client';

import { useRouter } from "next/navigation";
import { Button } from "@nextui-org/react";
import Image from 'next/image';

// resource
import gdgocIcon from "@public/src/images/GDGoC_icon.png";

export default function Forbidden() {
const router = useRouter();

const handleClick = () => {
const sameOrigin = document.referrer.startsWith(window.location.origin);
if (sameOrigin) {
router.back();
} else {
router.push("/");
}
};

return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-gray-900 to-black relative overflow-hidden">
{/* 아이콘 배경 */}
<div className="absolute inset-0 flex items-center justify-center opacity-10 pointer-events-none z-0" aria-hidden="true">
<div className="relative">
<Image
src={gdgocIcon}
alt="GDGoC Icon"
width={600}
height={600}
className="animate-pulse"
priority
/>
</div>
</div>

{/* 403 에러 박스 */}
<div className="z-10 bg-gray-800/70 backdrop-blur-sm p-4 sm:p-8 rounded-xl shadow-2xl border border-red-500/20 max-w-[90%] sm:max-w-md w-full mx-4">
<div className="flex flex-col items-center justify-center text-center">
{/* 403 아이콘 */}
<div className="w-16 h-16 sm:w-20 sm:h-20 rounded-full bg-red-500/10 flex items-center justify-center mb-4 sm:mb-6" role="img" aria-label="403 icon">
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 sm:h-10 sm:w-10 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>

{/* 403 내용 */}
<h1 className="text-3xl sm:text-4xl font-bold text-white mb-2">
<span className="text-red-500">403</span> Forbidden
</h1>
<div className="w-12 sm:w-16 h-1 bg-gradient-to-r from-red-500 to-red-400 rounded-full mb-3 sm:mb-4" aria-hidden="true"></div>

<div className="bg-red-500/10 py-3 px-6 rounded-lg border border-red-500/20">
<p className="text-base sm:text-lg text-red-100">접근 권한이 없는 페이지입니다.</p>
<p className="text-base sm:text-lg text-red-100">이전 페이지로 이동해주세요.</p>
</div>

{/* 버튼 */}
<Button
onPress={handleClick}
className="mt-6 sm:mt-8 w-full max-w-[280px] h-10 sm:h-12 bg-gradient-to-r from-red-500 to-red-400 hover:from-red-400 hover:to-red-500 text-white text-base sm:text-lg font-semibold rounded-lg transition-all duration-300 shadow-lg hover:shadow-red-500/30"
aria-label="이전 페이지로 이동"
>
이전 페이지로 이동
</Button>

{/* 작은 로고 */}
<div className="mt-4 sm:mt-6">
<Image
src={gdgocIcon}
alt="GDGoC Small Icon"
width={32}
height={32}
className="opacity-50"
/>
</div>
</div>
</div>
</div>
);
}
Loading