수정
This commit is contained in:
@@ -1,109 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface Feature {
|
|
||||||
icon: React.ReactNode;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Features: React.FC = () => {
|
|
||||||
const features: Feature[] = [
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '보안 결제',
|
|
||||||
description: '최고 수준의 암호화 기술로 고객의 결제 정보를 안전하게 보호합니다.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '빠른 처리',
|
|
||||||
description: '실시간 결제 처리로 빠르고 효율적인 거래 경험을 제공합니다.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '모바일 최적화',
|
|
||||||
description: '하이브리드 앱으로 iOS, Android 모든 기기에서 완벽하게 동작합니다.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '24/7 지원',
|
|
||||||
description: '언제든지 도움이 필요할 때 전문가 팀이 즉시 지원해드립니다.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '신뢰성',
|
|
||||||
description: '15년간 축적된 노하우로 안정적이고 신뢰할 수 있는 서비스를 제공합니다.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: '맞춤 설정',
|
|
||||||
description: '비즈니스 요구사항에 맞게 유연하게 설정할 수 있는 결제 솔루션입니다.'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="py-20 bg-gray-50">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="text-center mb-16">
|
|
||||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
|
||||||
왜 나이스페이먼츠인가요?
|
|
||||||
</h2>
|
|
||||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
|
|
||||||
고객의 성공이 우리의 성공입니다. 최고의 기술력과 서비스로 비즈니스 성장을 지원합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
||||||
{features.map((feature, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="bg-white p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2"
|
|
||||||
>
|
|
||||||
<div className="w-16 h-16 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg flex items-center justify-center text-white mb-6">
|
|
||||||
{feature.icon}
|
|
||||||
</div>
|
|
||||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
|
||||||
{feature.title}
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-600 leading-relaxed">
|
|
||||||
{feature.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center mt-16">
|
|
||||||
<button className="bg-blue-600 text-white px-8 py-4 rounded-lg font-semibold text-lg hover:bg-blue-700 transition-all transform hover:scale-105 shadow-lg">
|
|
||||||
더 많은 기능 보기
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Features;
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Link } from '@tanstack/react-router';
|
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<footer className="bg-gray-900 text-white">
|
|
||||||
<div className="container mx-auto px-4 py-12">
|
|
||||||
<div className="grid md:grid-cols-4 gap-8">
|
|
||||||
{/* Company Info */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-8 h-8 bg-blue-600 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white font-bold text-sm">NP</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-xl font-bold">NicePayments</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-gray-400 leading-relaxed">
|
|
||||||
15년 경험의 결제 전문 기업으로, 안전하고 편리한 결제 서비스를 제공합니다.
|
|
||||||
</p>
|
|
||||||
<div className="flex space-x-4">
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
|
||||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
|
||||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
|
||||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
|
||||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24.009 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641.001 12.017.001z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Services */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="text-lg font-semibold">서비스</h3>
|
|
||||||
<ul className="space-y-2 text-gray-400">
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">카드 결제</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">계좌이체</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">모바일 결제</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">가상계좌</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">API 문서</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Company */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="text-lg font-semibold">회사</h3>
|
|
||||||
<ul className="space-y-2 text-gray-400">
|
|
||||||
<li><Link to="/about" className="hover:text-white transition-colors">회사소개</Link></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">채용정보</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">보도자료</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">파트너십</a></li>
|
|
||||||
<li><Link to="/contact" className="hover:text-white transition-colors">문의하기</Link></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Support */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="text-lg font-semibold">고객지원</h3>
|
|
||||||
<ul className="space-y-2 text-gray-400">
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">FAQ</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">개발자 가이드</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">상태 페이지</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">보안센터</a></li>
|
|
||||||
<li><a href="#" className="hover:text-white transition-colors">1:1 문의</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Contact Info */}
|
|
||||||
<div className="border-t border-gray-800 mt-8 pt-8">
|
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-semibold mb-2">고객센터</h4>
|
|
||||||
<p className="text-gray-400">전화: 02-1234-5678</p>
|
|
||||||
<p className="text-gray-400">이메일: contact@nicepayments.com</p>
|
|
||||||
<p className="text-gray-400">운영시간: 평일 09:00 - 18:00</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4 className="font-semibold mb-2">회사정보</h4>
|
|
||||||
<p className="text-gray-400">주소: 서울시 강남구 테헤란로 123</p>
|
|
||||||
<p className="text-gray-400">사업자등록번호: 123-45-67890</p>
|
|
||||||
<p className="text-gray-400">대표: 홍길동</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Copyright */}
|
|
||||||
<div className="border-t border-gray-800 mt-8 pt-8 flex flex-col md:flex-row justify-between items-center">
|
|
||||||
<p className="text-gray-400">
|
|
||||||
© 2023 NicePayments. All rights reserved.
|
|
||||||
</p>
|
|
||||||
<div className="flex space-x-6 mt-4 md:mt-0">
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
|
||||||
개인정보처리방침
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
|
||||||
이용약관
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
|
||||||
쿠키정책
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Footer;
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { Link, useRouterState } from '@tanstack/react-router';
|
|
||||||
import { Toasts } from '@/shared/ui/toasts/toasts';
|
|
||||||
import { ToastContainer,toast } from 'react-toastify';
|
|
||||||
|
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
|
||||||
const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(true);
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
const router = useRouterState();
|
|
||||||
|
|
||||||
const isActive = (path: string) => {
|
|
||||||
return router.location.pathname === path;
|
|
||||||
};
|
|
||||||
const toastTest = (str: string) => {
|
|
||||||
//alert(str)
|
|
||||||
toast(str);
|
|
||||||
//notiBar(str)
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const openBottomSheet = () => {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<header
|
|
||||||
className="fixed left-0 right-0 bg-white shadow-lg z-50"
|
|
||||||
style={{
|
|
||||||
top: `env(safe-area-inset-top, 0px)`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Toasts />
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="flex justify-between items-center py-4">
|
|
||||||
{/* Logo */}
|
|
||||||
<Link to="/" className="flex items-center space-x-2">
|
|
||||||
<div className="w-8 h-8 bg-blue-600 rounded-md flex items-center justify-center">
|
|
||||||
<span className="text-white font-bold text-sm">NP</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-xl font-bold text-gray-900">
|
|
||||||
NicePayments
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Desktop Navigation */}
|
|
||||||
<nav className="hidden md:flex items-center space-x-8">
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/')
|
|
||||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
|
||||||
: 'text-gray-700 hover:text-blue-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
홈
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/about"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/about')
|
|
||||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
|
||||||
: 'text-gray-700 hover:text-blue-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
회사소개
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/contact"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/contact')
|
|
||||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
|
||||||
: 'text-gray-700 hover:text-blue-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
문의하기
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/test"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/test')
|
|
||||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
|
||||||
: 'text-gray-700 hover:text-blue-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
테스트
|
|
||||||
</Link>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{/* User Menu */}
|
|
||||||
<div className="hidden md:flex items-center space-x-4">
|
|
||||||
<div className="space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={() => toastTest('로그인')}
|
|
||||||
className="text-gray-700 px-4 py-2 rounded-md hover:bg-gray-100 transition-colors">
|
|
||||||
로그인
|
|
||||||
</button>
|
|
||||||
<button className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors">
|
|
||||||
회원가입
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
|
||||||
<button
|
|
||||||
className="md:hidden flex items-center justify-center w-8 h-8"
|
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="w-6 h-6"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
{isMenuOpen ? (
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M4 6h16M4 12h16M4 18h16"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Mobile Menu */}
|
|
||||||
{isMenuOpen && (
|
|
||||||
<div className="md:hidden py-4 border-t border-gray-200">
|
|
||||||
<nav className="flex flex-col space-y-4">
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/') ? 'text-blue-600' : 'text-gray-700'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
홈
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/about"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/about') ? 'text-blue-600' : 'text-gray-700'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
회사소개
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/contact"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/contact') ? 'text-blue-600' : 'text-gray-700'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
문의하기
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/test"
|
|
||||||
className={`font-medium transition-colors ${
|
|
||||||
isActive('/test') ? 'text-blue-600' : 'text-gray-700'
|
|
||||||
}`}
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
테스트
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<div className="pt-4 border-t border-gray-200">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<button
|
|
||||||
className="w-full text-left text-gray-700 hover:text-blue-600 transition-colors"
|
|
||||||
onClick={() => toastTest('로그인')}
|
|
||||||
>
|
|
||||||
로그인
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-full text-left bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors"
|
|
||||||
onClick={() => setIsMenuOpen(false)}
|
|
||||||
>
|
|
||||||
회원가입
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
const Hero: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<section className="bg-gradient-to-r from-blue-600 to-purple-600 text-white">
|
|
||||||
<div className="container mx-auto px-4 py-20">
|
|
||||||
<div className="max-w-4xl mx-auto text-center">
|
|
||||||
<h1 className="text-4xl md:text-6xl font-bold mb-6">
|
|
||||||
안전하고 편리한
|
|
||||||
<br />
|
|
||||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-pink-400">
|
|
||||||
결제 서비스
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-xl md:text-2xl mb-8 opacity-90">
|
|
||||||
나이스페이먼츠와 함께 더 나은 결제 경험을 시작하세요.
|
|
||||||
<br />
|
|
||||||
하이브리드 앱으로 언제 어디서나 간편하게!
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
||||||
<button className="bg-white text-blue-600 px-8 py-4 rounded-lg font-semibold text-lg hover:bg-gray-100 transition-all transform hover:scale-105 shadow-lg">
|
|
||||||
서비스 시작하기
|
|
||||||
</button>
|
|
||||||
<button className="border-2 border-white text-white px-8 py-4 rounded-lg font-semibold text-lg hover:bg-white hover:text-blue-600 transition-all transform hover:scale-105">
|
|
||||||
더 알아보기
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating Elements */}
|
|
||||||
<div className="absolute top-20 left-10 w-20 h-20 bg-white opacity-10 rounded-full animate-bounce"></div>
|
|
||||||
<div className="absolute top-40 right-20 w-16 h-16 bg-yellow-400 opacity-20 rounded-full animate-pulse"></div>
|
|
||||||
<div className="absolute bottom-20 left-1/4 w-12 h-12 bg-pink-400 opacity-20 rounded-full animate-bounce delay-1000"></div>
|
|
||||||
|
|
||||||
{/* Wave Bottom */}
|
|
||||||
<div className="relative">
|
|
||||||
<svg
|
|
||||||
className="w-full h-20 fill-current text-white"
|
|
||||||
viewBox="0 0 1200 120"
|
|
||||||
preserveAspectRatio="none"
|
|
||||||
>
|
|
||||||
<path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z"
|
|
||||||
opacity=".25"
|
|
||||||
></path>
|
|
||||||
<path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z"
|
|
||||||
opacity=".5"
|
|
||||||
></path>
|
|
||||||
<path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Hero;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { appBridge } from '@/utils/appBridge';
|
|
||||||
|
|
||||||
const LanguageSwitcher: React.FC = () => {
|
|
||||||
const { i18n, t } = useTranslation();
|
|
||||||
|
|
||||||
const changeLanguage = async (language: string) => {
|
|
||||||
// 웹 언어 변경
|
|
||||||
i18n.changeLanguage(language);
|
|
||||||
localStorage.setItem('i18nextLng', language);
|
|
||||||
|
|
||||||
// 네이티브 환경에서 네이티브 언어도 변경
|
|
||||||
if (appBridge.isNativeEnvironment()) {
|
|
||||||
try {
|
|
||||||
await appBridge.setLanguage(language);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to set native language:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<span className="text-sm text-gray-600">{t('common.language')}</span>
|
|
||||||
<select
|
|
||||||
value={i18n.language}
|
|
||||||
onChange={(e) => changeLanguage(e.target.value)}
|
|
||||||
className="text-sm border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="ko">한국어</option>
|
|
||||||
<option value="en">English</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageSwitcher;
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface SearchFilterButtonProps {
|
|
||||||
displayText: string;
|
|
||||||
isActive: boolean;
|
|
||||||
onClick: () => void;
|
|
||||||
className?: string;
|
|
||||||
showIcon?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SearchFilterButton: React.FC<SearchFilterButtonProps> = ({
|
|
||||||
displayText,
|
|
||||||
isActive,
|
|
||||||
onClick,
|
|
||||||
className = '',
|
|
||||||
showIcon = true,
|
|
||||||
disabled = false
|
|
||||||
}) => {
|
|
||||||
const baseClasses = "inline-flex items-center px-4 py-2 text-sm font-medium rounded-lg border transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500";
|
|
||||||
|
|
||||||
const activeClasses = isActive
|
|
||||||
? "bg-blue-50 border-blue-500 text-blue-700 hover:bg-blue-100"
|
|
||||||
: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50 hover:border-gray-400";
|
|
||||||
|
|
||||||
const disabledClasses = disabled
|
|
||||||
? "opacity-50 cursor-not-allowed"
|
|
||||||
: "cursor-pointer";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
className={`${baseClasses} ${activeClasses} ${disabledClasses} ${className}`}
|
|
||||||
>
|
|
||||||
{showIcon && (
|
|
||||||
<svg
|
|
||||||
className="w-4 h-4 mr-2"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.414A1 1 0 013 6.707V4z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
<span className="truncate">{displayText}</span>
|
|
||||||
{isActive && (
|
|
||||||
<div className="ml-2 w-2 h-2 bg-blue-500 rounded-full flex-shrink-0" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SearchFilterButton;
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface Service {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
features: string[];
|
|
||||||
icon: React.ReactNode;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Services: React.FC = () => {
|
|
||||||
const services: Service[] = [
|
|
||||||
{
|
|
||||||
title: '카드 결제',
|
|
||||||
description: '국내외 모든 카드사와 연동된 안전한 카드 결제 서비스',
|
|
||||||
features: [
|
|
||||||
'국내외 주요 카드사 지원',
|
|
||||||
'무이자 할부 서비스',
|
|
||||||
'실시간 승인 처리',
|
|
||||||
'3D Secure 보안 인증'
|
|
||||||
],
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
color: 'from-blue-500 to-blue-600'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '계좌이체',
|
|
||||||
description: '은행 계좌를 통한 직접 이체 결제 서비스',
|
|
||||||
features: [
|
|
||||||
'전 은행 실시간 이체',
|
|
||||||
'낮은 수수료율',
|
|
||||||
'자동 입금 확인',
|
|
||||||
'환불 처리 자동화'
|
|
||||||
],
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
color: 'from-green-500 to-green-600'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '모바일 결제',
|
|
||||||
description: '카카오페이, 네이버페이 등 간편결제 서비스',
|
|
||||||
features: [
|
|
||||||
'주요 간편결제 연동',
|
|
||||||
'원터치 결제',
|
|
||||||
'포인트 사용 가능',
|
|
||||||
'멤버십 혜택 적용'
|
|
||||||
],
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
color: 'from-purple-500 to-purple-600'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '가상계좌',
|
|
||||||
description: '개인별 전용 가상계좌를 통한 무통장 입금 서비스',
|
|
||||||
features: [
|
|
||||||
'실시간 계좌 발급',
|
|
||||||
'자동 입금 확인',
|
|
||||||
'다중 은행 지원',
|
|
||||||
'입금 알림 서비스'
|
|
||||||
],
|
|
||||||
icon: (
|
|
||||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
color: 'from-orange-500 to-orange-600'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="py-20 bg-white">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="text-center mb-16">
|
|
||||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
|
||||||
다양한 결제 서비스
|
|
||||||
</h2>
|
|
||||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
|
|
||||||
고객의 다양한 결제 니즈에 맞춰 최적화된 결제 솔루션을 제공합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-8">
|
|
||||||
{services.map((service, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="group bg-white border-2 border-gray-100 rounded-2xl p-8 hover:border-transparent hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-1"
|
|
||||||
>
|
|
||||||
<div className={`w-16 h-16 bg-gradient-to-r ${service.color} rounded-xl flex items-center justify-center text-white mb-6 group-hover:scale-110 transition-transform duration-300`}>
|
|
||||||
{service.icon}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
|
||||||
{service.title}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<p className="text-gray-600 mb-6 leading-relaxed">
|
|
||||||
{service.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-3">주요 기능</h4>
|
|
||||||
{service.features.map((feature, featureIndex) => (
|
|
||||||
<div key={featureIndex} className="flex items-center space-x-3">
|
|
||||||
<div className="w-2 h-2 bg-blue-500 rounded-full flex-shrink-0"></div>
|
|
||||||
<span className="text-gray-700">{feature}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button className="mt-8 w-full bg-gray-50 text-gray-700 py-3 px-6 rounded-lg font-semibold hover:bg-blue-600 hover:text-white transition-all duration-300">
|
|
||||||
자세히 보기
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center mt-16">
|
|
||||||
<div className="bg-gradient-to-r from-blue-50 to-purple-50 rounded-2xl p-8">
|
|
||||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
|
||||||
맞춤형 결제 솔루션이 필요하신가요?
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-600 mb-6 max-w-2xl mx-auto">
|
|
||||||
비즈니스 규모와 업종에 맞는 최적의 결제 솔루션을 제안해드립니다.
|
|
||||||
전문 컨설턴트와 상담을 통해 최고의 결제 환경을 구축하세요.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
||||||
<button className="bg-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors">
|
|
||||||
무료 상담 신청
|
|
||||||
</button>
|
|
||||||
<button className="border-2 border-blue-600 text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-600 hover:text-white transition-colors">
|
|
||||||
서비스 가이드 다운로드
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Services;
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
interface SlideMenuProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SlideMenu: React.FC<SlideMenuProps> = ({ isOpen, onClose }) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
// ESC 키로 메뉴 닫기
|
|
||||||
useEffect(() => {
|
|
||||||
const handleEscape = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isOpen) {
|
|
||||||
document.addEventListener('keydown', handleEscape);
|
|
||||||
// 배경 스크롤 방지
|
|
||||||
document.body.style.overflow = 'hidden';
|
|
||||||
} else {
|
|
||||||
document.body.style.overflow = 'unset';
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleEscape);
|
|
||||||
document.body.style.overflow = 'unset';
|
|
||||||
};
|
|
||||||
}, [isOpen, onClose]);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* 오버레이 */}
|
|
||||||
{isOpen && (
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 bg-black bg-opacity-50 z-[60] transition-opacity duration-300"
|
|
||||||
onClick={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 슬라이드 메뉴 */}
|
|
||||||
<div
|
|
||||||
className={`fixed top-0 right-0 h-full w-full bg-white z-[60] transform transition-transform duration-300 ease-in-out flex flex-col ${
|
|
||||||
isOpen ? 'translate-x-0' : 'translate-x-full'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{/* 헤더 */}
|
|
||||||
<div
|
|
||||||
className="flex items-center justify-between px-4 py-4 border-b border-gray-200 bg-white sticky top-0 z-10"
|
|
||||||
style={{
|
|
||||||
paddingTop: `calc(1rem + env(safe-area-inset-top, 0px))`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">{t('slideMenu.title')}</h2>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="p-2 rounded-md hover:bg-gray-100 transition-colors"
|
|
||||||
>
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 메뉴 콘텐츠 */}
|
|
||||||
<div className="p-4 flex-1 overflow-y-scroll pb-16 scrollbar-hide">
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="border border-gray-200 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">{t('slideMenu.accountManagement')}</h3>
|
|
||||||
<ul className="space-y-3">
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.profileSettings')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.changePassword')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-5 5v-5zM8 17H3l5 5v-5zM12 3v3m6.364-.636l-2.121 2.121M21 12h-3M18.364 18.364l-2.121-2.121M12 21v-3m-6.364.636l2.121-2.121M3 12h3M5.636 5.636l2.121 2.121" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.notificationSettings')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border border-gray-200 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">{t('slideMenu.paymentService')}</h3>
|
|
||||||
<ul className="space-y-3">
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.paymentHistory')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.refundRequest')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.paymentMethodManagement')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border border-gray-200 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">{t('slideMenu.customerSupport')}</h3>
|
|
||||||
<ul className="space-y-3">
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.faq')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.inquiry')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.termsOfService')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border border-gray-200 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">{t('slideMenu.other')}</h3>
|
|
||||||
<ul className="space-y-3">
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.appInfo')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.developerInfo')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
|
||||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
|
||||||
</svg>
|
|
||||||
{t('slideMenu.logout')}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SlideMenu;
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { Link, useRouterState } from '@tanstack/react-router';
|
|
||||||
import { useScrollDirection } from '../hooks/useScrollDirection';
|
|
||||||
import SlideMenu from './SlideMenu';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const TabBar: React.FC = () => {
|
|
||||||
const router = useRouterState();
|
|
||||||
const { translateY, isScrollingState } = useScrollDirection();
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const isActive = (path: string) => {
|
|
||||||
return router.location.pathname === path;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
label: t('menu.home'),
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/about',
|
|
||||||
label: t('menu.about'),
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/contact',
|
|
||||||
label: t('menu.contact'),
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/test',
|
|
||||||
label: t('menu.test'),
|
|
||||||
icon: (
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="fixed bottom-0 left-0 right-0 bg-white shadow-lg border-t border-gray-200 z-50 tabbar-scroll-effect"
|
|
||||||
style={{
|
|
||||||
transform: `translateY(${translateY}px)`,
|
|
||||||
paddingBottom: `env(safe-area-inset-bottom, 0px)`,
|
|
||||||
transition: isScrollingState
|
|
||||||
? 'transform 0.05s linear'
|
|
||||||
: 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="flex justify-around items-center py-1">
|
|
||||||
{tabs.map((tab) => (
|
|
||||||
<Link
|
|
||||||
key={tab.path}
|
|
||||||
to={tab.path}
|
|
||||||
className={`flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-colors min-w-[60px] ${
|
|
||||||
isActive(tab.path)
|
|
||||||
? 'text-blue-600 bg-blue-50'
|
|
||||||
: 'text-gray-600 hover:text-blue-600 hover:bg-gray-50'
|
|
||||||
}`}
|
|
||||||
preload="intent"
|
|
||||||
>
|
|
||||||
<div className="mb-1">
|
|
||||||
{tab.icon}
|
|
||||||
</div>
|
|
||||||
<span className="text-xs font-medium">{tab.label}</span>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* 메뉴 탭 */}
|
|
||||||
<button
|
|
||||||
onClick={() => setIsMenuOpen(true)}
|
|
||||||
className={`flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-colors min-w-[60px] ${
|
|
||||||
isMenuOpen
|
|
||||||
? 'text-blue-600 bg-blue-50'
|
|
||||||
: 'text-gray-600 hover:text-blue-600 hover:bg-gray-50'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="mb-1">
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs font-medium">{t('menu.menu')}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 슬라이드 메뉴 */}
|
|
||||||
<SlideMenu isOpen={isMenuOpen} onClose={() => setIsMenuOpen(false)} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TabBar;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export { default as Header } from './Header';
|
|
||||||
export { default as Footer } from './Footer';
|
|
||||||
export { default as Hero } from './Hero';
|
|
||||||
export { default as Features } from './Features';
|
|
||||||
export { default as Services } from './Services';
|
|
||||||
export { default as TabBar } from './TabBar';
|
|
||||||
export { default as SlideMenu } from './SlideMenu';
|
|
||||||
export { default as SearchFilterButton } from './SearchFilterButton';
|
|
||||||
export { default as LanguageSwitcher } from './LanguageSwitcher';
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
subject: string;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FormErrors {
|
|
||||||
name?: string;
|
|
||||||
email?: string;
|
|
||||||
phone?: string;
|
|
||||||
subject?: string;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Contact: React.FC = () => {
|
|
||||||
const [formData, setFormData] = useState<FormData>({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
subject: '',
|
|
||||||
message: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const [errors, setErrors] = useState<FormErrors>({});
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
||||||
|
|
||||||
const validateForm = (): boolean => {
|
|
||||||
const newErrors: FormErrors = {};
|
|
||||||
|
|
||||||
if (!formData.name.trim()) {
|
|
||||||
newErrors.name = '이름을 입력해주세요.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.email.trim()) {
|
|
||||||
newErrors.email = '이메일을 입력해주세요.';
|
|
||||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
|
||||||
newErrors.email = '올바른 이메일 형식을 입력해주세요.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.phone.trim()) {
|
|
||||||
newErrors.phone = '전화번호를 입력해주세요.';
|
|
||||||
} else if (!/^[0-9-+\s()]+$/.test(formData.phone)) {
|
|
||||||
newErrors.phone = '올바른 전화번호 형식을 입력해주세요.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.subject.trim()) {
|
|
||||||
newErrors.subject = '문의 제목을 입력해주세요.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!formData.message.trim()) {
|
|
||||||
newErrors.message = '문의 내용을 입력해주세요.';
|
|
||||||
} else if (formData.message.length < 10) {
|
|
||||||
newErrors.message = '문의 내용을 최소 10자 이상 입력해주세요.';
|
|
||||||
}
|
|
||||||
|
|
||||||
setErrors(newErrors);
|
|
||||||
return Object.keys(newErrors).length === 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
||||||
const { name, value } = e.target;
|
|
||||||
setFormData(prev => ({
|
|
||||||
...prev,
|
|
||||||
[name]: value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (errors[name as keyof FormErrors]) {
|
|
||||||
setErrors(prev => ({
|
|
||||||
...prev,
|
|
||||||
[name]: undefined,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!validateForm()) return;
|
|
||||||
|
|
||||||
setIsSubmitting(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 실제 구현에서는 API 호출
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
setIsSubmitted(true);
|
|
||||||
setFormData({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
subject: '',
|
|
||||||
message: '',
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Form submission error:', error);
|
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isSubmitted) {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full mx-4">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
||||||
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
|
||||||
문의가 접수되었습니다
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600 mb-6">
|
|
||||||
빠른 시일 내에 답변 드리겠습니다.
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsSubmitted(false)}
|
|
||||||
className="bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition-colors"
|
|
||||||
>
|
|
||||||
새 문의하기
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-50 py-12">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="max-w-4xl mx-auto">
|
|
||||||
<div className="text-center mb-12">
|
|
||||||
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
|
||||||
문의하기
|
|
||||||
</h1>
|
|
||||||
<p className="text-lg text-gray-600">
|
|
||||||
궁금한 사항이나 문의사항이 있으시면 언제든지 연락주세요.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-12">
|
|
||||||
{/* Contact Information */}
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
|
||||||
연락처 정보
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-start space-x-3">
|
|
||||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
|
||||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="font-semibold text-gray-900">주소</div>
|
|
||||||
<div className="text-gray-600">서울시 강남구 테헤란로 123</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start space-x-3">
|
|
||||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
|
||||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="font-semibold text-gray-900">이메일</div>
|
|
||||||
<div className="text-gray-600">contact@nicepayments.com</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start space-x-3">
|
|
||||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
|
||||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="font-semibold text-gray-900">전화번호</div>
|
|
||||||
<div className="text-gray-600">02-1234-5678</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start space-x-3">
|
|
||||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
|
||||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="font-semibold text-gray-900">운영시간</div>
|
|
||||||
<div className="text-gray-600">평일 09:00 - 18:00</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Contact Form */}
|
|
||||||
<div className="bg-white p-8 rounded-lg shadow-lg">
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
|
||||||
문의 양식
|
|
||||||
</h2>
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
이름 *
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="name"
|
|
||||||
name="name"
|
|
||||||
value={formData.name}
|
|
||||||
onChange={handleChange}
|
|
||||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|
||||||
errors.name ? 'border-red-500' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
placeholder="이름을 입력하세요"
|
|
||||||
/>
|
|
||||||
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
이메일 *
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
value={formData.email}
|
|
||||||
onChange={handleChange}
|
|
||||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|
||||||
errors.email ? 'border-red-500' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
placeholder="이메일을 입력하세요"
|
|
||||||
/>
|
|
||||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
전화번호 *
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
value={formData.phone}
|
|
||||||
onChange={handleChange}
|
|
||||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|
||||||
errors.phone ? 'border-red-500' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
placeholder="전화번호를 입력하세요"
|
|
||||||
/>
|
|
||||||
{errors.phone && <p className="mt-1 text-sm text-red-600">{errors.phone}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="subject" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
문의 제목 *
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="subject"
|
|
||||||
name="subject"
|
|
||||||
value={formData.subject}
|
|
||||||
onChange={handleChange}
|
|
||||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|
||||||
errors.subject ? 'border-red-500' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<option value="">문의 유형을 선택하세요</option>
|
|
||||||
<option value="service">서비스 문의</option>
|
|
||||||
<option value="technical">기술 지원</option>
|
|
||||||
<option value="billing">결제/요금 문의</option>
|
|
||||||
<option value="partnership">제휴 문의</option>
|
|
||||||
<option value="other">기타</option>
|
|
||||||
</select>
|
|
||||||
{errors.subject && <p className="mt-1 text-sm text-red-600">{errors.subject}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
문의 내용 *
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="message"
|
|
||||||
name="message"
|
|
||||||
value={formData.message}
|
|
||||||
onChange={handleChange}
|
|
||||||
rows={5}
|
|
||||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|
||||||
errors.message ? 'border-red-500' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
placeholder="문의 내용을 자세히 입력하세요"
|
|
||||||
/>
|
|
||||||
{errors.message && <p className="mt-1 text-sm text-red-600">{errors.message}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
||||||
>
|
|
||||||
{isSubmitting ? '전송 중...' : '문의 보내기'}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Contact;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import Hero from '@/components/Hero';
|
|
||||||
import Features from '@/components/Features';
|
|
||||||
import Services from '@/components/Services';
|
|
||||||
|
|
||||||
import { BottomSheet } from '@/shared/ui/bottom-sheets/bottom-sheet';
|
|
||||||
const Home: React.FC = () => {
|
|
||||||
const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(true);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Hero />
|
|
||||||
<Features />
|
|
||||||
<Services />
|
|
||||||
<BottomSheet
|
|
||||||
afterLeave={() => null}
|
|
||||||
open={isOpenBottomSheet}
|
|
||||||
onClose={() => {}}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
<div>test</div>
|
|
||||||
</div>
|
|
||||||
</BottomSheet>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Home;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default as Home } from './Home';
|
|
||||||
export { default as Contact } from './Contact';
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
|
||||||
// import { TanStackRouterDevtools } from '@tanstack/router-devtools'
|
|
||||||
import { TanStackRouterDevtoolsInProd } from '@tanstack/react-router-devtools'
|
|
||||||
import { Header, Footer, TabBar } from '@/components'
|
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
|
||||||
component: () => (
|
|
||||||
<div className="min-h-screen flex flex-col">
|
|
||||||
<Header />
|
|
||||||
<main
|
|
||||||
className="flex-grow"
|
|
||||||
style={{
|
|
||||||
paddingTop: `calc(5rem + env(safe-area-inset-top, 0px))`,
|
|
||||||
paddingBottom: `calc(4rem + env(safe-area-inset-bottom, 0px))`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Outlet />
|
|
||||||
</main>
|
|
||||||
<Footer />
|
|
||||||
<TabBar />
|
|
||||||
<TanStackRouterDevtoolsInProd initialIsOpen={false}/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
})
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/account')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/account"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
import { Contact } from '@/pages'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/contact')({
|
|
||||||
component: Contact,
|
|
||||||
})
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
import { Home } from '@/pages'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/')({
|
|
||||||
component: Home,
|
|
||||||
})
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/merchant')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/merchant"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/notice')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/notice"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/transaction/billing')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/transaction/billing"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/transaction/escrow')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/transaction/escrow"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/transaction/receipt')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return <div>Hello "/transaction/receipt"!</div>
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/transaction')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Transaction</h1>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -8,4 +8,4 @@ export * from './use-location-permission';
|
|||||||
export * from './use-window-focus-change';
|
export * from './use-window-focus-change';
|
||||||
export * from './use-router-listener';
|
export * from './use-router-listener';
|
||||||
export * from './use-scroll-to-top';
|
export * from './use-scroll-to-top';
|
||||||
export * from './use-app-page-speed';
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { useEffectOnce } from 'react-use';
|
|
||||||
|
|
||||||
import useLocalStorageState from 'use-local-storage-state';
|
|
||||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
|
||||||
import { setAfterPageRendered } from '@/shared/lib';
|
|
||||||
|
|
||||||
export const useAppPagingSpeed = () => {
|
|
||||||
return useLocalStorageState(StorageKeys.AppPagingSpeed, { defaultValue: '250' });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useEffectOnceAfterPageRendered = (callback: () => void) => {
|
|
||||||
useEffectOnce(() => {
|
|
||||||
setAfterPageRendered(callback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { useBlocker } from '@use-blocker';
|
|
||||||
|
|
||||||
export const useBlockBack = (blockPaths: string[]) => {
|
|
||||||
useBlocker(({ nextLocation }) => {
|
|
||||||
if (blockPaths.includes(nextLocation.pathname)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -437,4 +437,7 @@ main.home-main{
|
|||||||
|
|
||||||
.link-payment-detail-button button {
|
.link-payment-detail-button button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
.main.pop{
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { useRef, useState } from 'react';
|
|
||||||
import { useClickAway } from 'react-use';
|
|
||||||
|
|
||||||
interface MoreMenuProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
export const MoreMenu = ({ children }: MoreMenuProps) => {
|
|
||||||
const [isOnMoreMenu, setIsOnMoreMenu] = useState(false);
|
|
||||||
const ref = useRef(null);
|
|
||||||
useClickAway(ref, () => {
|
|
||||||
setIsOnMoreMenu(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={ref}>
|
|
||||||
<button type="button" className="btn top-more-btn" onClick={() => setIsOnMoreMenu((prev) => !prev)}>
|
|
||||||
<span>더보기 버튼</span>
|
|
||||||
</button>
|
|
||||||
{isOnMoreMenu && <div className="more-menu on">{children}</div>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user