첫 커밋
This commit is contained in:
318
src/pages/Contact.tsx
Normal file
318
src/pages/Contact.tsx
Normal file
@@ -0,0 +1,318 @@
|
||||
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;
|
||||
34
src/pages/Home.tsx
Normal file
34
src/pages/Home.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
532
src/pages/Test.tsx
Normal file
532
src/pages/Test.tsx
Normal file
@@ -0,0 +1,532 @@
|
||||
import React, { useState } from 'react';
|
||||
import { appBridge } from '../utils/appBridge';
|
||||
import { SearchFilterExample, LanguageSwitcher } from '../components';
|
||||
import { SearchFilter } from '../types/filter';
|
||||
|
||||
interface TestResult {
|
||||
method: string;
|
||||
success: boolean;
|
||||
result?: any;
|
||||
error?: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
const Test: React.FC = () => {
|
||||
const [results, setResults] = useState<TestResult[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<string | null>(null);
|
||||
const [messageCount, setMessageCount] = useState<number>(0);
|
||||
const [searchData, setSearchData] = useState<Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
amount: number;
|
||||
date: Date;
|
||||
status: string;
|
||||
description: string;
|
||||
}>>([]);
|
||||
|
||||
const addResult = (method: string, success: boolean, result?: any, error?: string) => {
|
||||
const newResult: TestResult = {
|
||||
method,
|
||||
success,
|
||||
result,
|
||||
error,
|
||||
timestamp: new Date().toLocaleTimeString()
|
||||
};
|
||||
setResults(prev => [newResult, ...prev]);
|
||||
};
|
||||
|
||||
const executeTest = async (method: string, testFn: () => Promise<any>) => {
|
||||
setIsLoading(method);
|
||||
try {
|
||||
const result = await testFn();
|
||||
addResult(method, true, result);
|
||||
} catch (error) {
|
||||
addResult(method, false, undefined, error instanceof Error ? error.message : String(error));
|
||||
} finally {
|
||||
setIsLoading(null);
|
||||
}
|
||||
};
|
||||
|
||||
const clearResults = () => {
|
||||
setResults([]);
|
||||
};
|
||||
|
||||
// 앱 정보 테스트
|
||||
const testGetAppInfo = () => executeTest('getAppInfo', () => appBridge.getAppInfo());
|
||||
const testGetDeviceInfo = () => executeTest('getDeviceInfo', () => appBridge.getDeviceInfo());
|
||||
|
||||
// 네비게이션 테스트
|
||||
const testNavigateBack = () => executeTest('navigateBack', () => appBridge.navigateBack());
|
||||
const testNavigateTo = () => executeTest('navigateTo', () => appBridge.navigateTo('/home'));
|
||||
const testCloseWebView = () => executeTest('closeWebView', () => appBridge.closeWebView());
|
||||
const testNavigateToLogin = () => executeTest('navigateToLogin', () => appBridge.navigateToLogin());
|
||||
|
||||
// 알림 테스트
|
||||
const testShowToast = () => executeTest('showToast', () => appBridge.showToast('테스트 토스트 메시지', 3000));
|
||||
const testShowAlert = () => executeTest('showAlert', () => appBridge.showAlert('테스트 알림', '이것은 테스트 알림입니다.'));
|
||||
const testShowConfirm = () => executeTest('showConfirm', () => appBridge.showConfirm('확인', '계속하시겠습니까?'));
|
||||
|
||||
// 저장소 테스트
|
||||
const testSetStorage = () => executeTest('setStorage', () => appBridge.setStorage('test_key', { message: '테스트 데이터', timestamp: new Date().toISOString() }));
|
||||
const testGetStorage = () => executeTest('getStorage', () => appBridge.getStorage('test_key'));
|
||||
const testRemoveStorage = () => executeTest('removeStorage', () => appBridge.removeStorage('test_key'));
|
||||
|
||||
// 공유 테스트
|
||||
const testShareContent = () => executeTest('shareContent', () => appBridge.shareContent({
|
||||
title: '테스트 공유',
|
||||
text: '이것은 테스트 공유 내용입니다.',
|
||||
url: 'https://nicepayments.com'
|
||||
}));
|
||||
|
||||
// 인증 테스트
|
||||
const testLogin = () => executeTest('login', () => appBridge.login({ email: 'test@example.com', password: 'test123' }));
|
||||
const testLogout = () => executeTest('logout', () => appBridge.logout());
|
||||
|
||||
// 안전한 호출 테스트
|
||||
const testSafeCall = () => executeTest('safeCall', () => appBridge.safeCall(
|
||||
() => appBridge.getAppInfo(),
|
||||
{ version: 'fallback', buildNumber: 'fallback' },
|
||||
(error) => console.error('Safe call error:', error)
|
||||
));
|
||||
|
||||
// 타임아웃 테스트
|
||||
const testCallWithTimeout = () => executeTest('callWithTimeout', () => appBridge.callWithTimeout(
|
||||
() => appBridge.getDeviceInfo(),
|
||||
2000
|
||||
));
|
||||
|
||||
// 메시지 카운트 업데이트 테스트
|
||||
const testUpdateMessageCount = () => executeTest('updateMessageCount', () => appBridge.updateMessageCount(messageCount));
|
||||
|
||||
// 조회조건 변경 핸들러
|
||||
const handleFilterChange = (filter: SearchFilter) => {
|
||||
addResult('filterChange', true, filter);
|
||||
|
||||
// 가상 데이터로 검색 결과 시뮬레이션
|
||||
const mockData: any = generateMockData(filter);
|
||||
setSearchData(mockData);
|
||||
};
|
||||
|
||||
// 가상 거래 데이터 생성
|
||||
const generateMockData = (filter: SearchFilter) => {
|
||||
const transactionTypes = ['deposit', 'withdrawal'];
|
||||
const data = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const type = filter.transactionType === 'all'
|
||||
? transactionTypes[Math.floor(Math.random() * transactionTypes.length)]
|
||||
: filter.transactionType;
|
||||
|
||||
const amount = Math.floor(Math.random() * 1000000) + 10000;
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - Math.floor(Math.random() * 180));
|
||||
|
||||
data.push({
|
||||
id: `TXN${String(i + 1).padStart(3, '0')}`,
|
||||
type: type,
|
||||
amount: amount,
|
||||
date: date,
|
||||
status: Math.random() > 0.1 ? 'completed' : 'failed',
|
||||
description: type === 'deposit' ? '입금' : '출금'
|
||||
});
|
||||
}
|
||||
|
||||
// 정렬 적용
|
||||
data.sort((a, b) => {
|
||||
if (filter.sortOrder === 'latest') {
|
||||
return b.date.getTime() - a.date.getTime();
|
||||
} else {
|
||||
return a.date.getTime() - b.date.getTime();
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900">AppBridge 테스트</h1>
|
||||
<div className="flex space-x-4">
|
||||
<LanguageSwitcher />
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||
appBridge.isNativeEnvironment()
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}>
|
||||
{appBridge.isNativeEnvironment() ? '네이티브 환경' : '웹 환경'}
|
||||
</span>
|
||||
{appBridge.isNativeEnvironment() && (
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||
appBridge.isAndroid()
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}>
|
||||
{appBridge.isAndroid() ? 'Android' : appBridge.isIOS() ? 'iOS' : 'Unknown'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!appBridge.isNativeEnvironment() && (
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-md p-4 mb-6">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<svg className="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-yellow-800">
|
||||
웹 환경에서 실행 중
|
||||
</h3>
|
||||
<div className="mt-2 text-sm text-yellow-700">
|
||||
<p>대부분의 AppBridge 기능은 네이티브 환경에서만 작동합니다. 실제 테스트를 위해서는 모바일 앱에서 실행해주세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 조회조건 테스트 섹션 */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">조회조건 테스트</h2>
|
||||
<SearchFilterExample onFilterChange={handleFilterChange} />
|
||||
</div>
|
||||
|
||||
{/* 검색 결과 표시 */}
|
||||
{searchData.length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
검색 결과 ({searchData.length}건)
|
||||
</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
거래ID
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
구분
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
금액
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
날짜
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
상태
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{searchData.map((item, index) => (
|
||||
<tr key={index} className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{item.id}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
item.type === 'deposit'
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'
|
||||
}`}>
|
||||
{item.description}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{item.amount.toLocaleString()}원
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{item.date.toLocaleDateString('ko-KR')}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
item.status === 'completed'
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}>
|
||||
{item.status === 'completed' ? '완료' : '실패'}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* 테스트 버튼들 */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">테스트 기능</h2>
|
||||
|
||||
{/* 앱 정보 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">앱 정보</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testGetAppInfo}
|
||||
disabled={isLoading === 'getAppInfo'}
|
||||
className="bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'getAppInfo' ? '실행 중...' : '앱 정보'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testGetDeviceInfo}
|
||||
disabled={isLoading === 'getDeviceInfo'}
|
||||
className="bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'getDeviceInfo' ? '실행 중...' : '디바이스 정보'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 네비게이션 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">네비게이션</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testNavigateBack}
|
||||
disabled={isLoading === 'navigateBack'}
|
||||
className="bg-green-500 hover:bg-green-600 disabled:bg-green-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'navigateBack' ? '실행 중...' : '뒤로 가기'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testNavigateTo}
|
||||
disabled={isLoading === 'navigateTo'}
|
||||
className="bg-green-500 hover:bg-green-600 disabled:bg-green-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'navigateTo' ? '실행 중...' : '페이지 이동'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testNavigateToLogin}
|
||||
disabled={isLoading === 'navigateToLogin'}
|
||||
className="bg-green-500 hover:bg-green-600 disabled:bg-green-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'navigateTo' ? '실행 중...' : '로그인 화면'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testCloseWebView}
|
||||
disabled={isLoading === 'closeWebView'}
|
||||
className="bg-red-500 hover:bg-red-600 disabled:bg-red-300 text-white px-4 py-2 rounded text-sm col-span-2"
|
||||
>
|
||||
{isLoading === 'closeWebView' ? '실행 중...' : '웹뷰 닫기'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 알림 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">알림</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testShowToast}
|
||||
disabled={isLoading === 'showToast'}
|
||||
className="bg-yellow-500 hover:bg-yellow-600 disabled:bg-yellow-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'showToast' ? '실행 중...' : '토스트'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testShowAlert}
|
||||
disabled={isLoading === 'showAlert'}
|
||||
className="bg-yellow-500 hover:bg-yellow-600 disabled:bg-yellow-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'showAlert' ? '실행 중...' : '알림'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testShowConfirm}
|
||||
disabled={isLoading === 'showConfirm'}
|
||||
className="bg-yellow-500 hover:bg-yellow-600 disabled:bg-yellow-300 text-white px-4 py-2 rounded text-sm col-span-2"
|
||||
>
|
||||
{isLoading === 'showConfirm' ? '실행 중...' : '확인 대화상자'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 저장소 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">저장소</h3>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<button
|
||||
onClick={testSetStorage}
|
||||
disabled={isLoading === 'setStorage'}
|
||||
className="bg-purple-500 hover:bg-purple-600 disabled:bg-purple-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'setStorage' ? '실행 중...' : '저장'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testGetStorage}
|
||||
disabled={isLoading === 'getStorage'}
|
||||
className="bg-purple-500 hover:bg-purple-600 disabled:bg-purple-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'getStorage' ? '실행 중...' : '읽기'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testRemoveStorage}
|
||||
disabled={isLoading === 'removeStorage'}
|
||||
className="bg-purple-500 hover:bg-purple-600 disabled:bg-purple-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'removeStorage' ? '실행 중...' : '삭제'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 결제 및 인증 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">결제 & 인증</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testLogin}
|
||||
disabled={isLoading === 'login'}
|
||||
className="bg-green-600 hover:bg-green-700 disabled:bg-green-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'login' ? '실행 중...' : '로그인'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testLogout}
|
||||
disabled={isLoading === 'logout'}
|
||||
className="bg-red-600 hover:bg-red-700 disabled:bg-red-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'logout' ? '실행 중...' : '로그아웃'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 공유 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">공유</h3>
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
onClick={testShareContent}
|
||||
disabled={isLoading === 'shareContent'}
|
||||
className="bg-cyan-500 hover:bg-cyan-600 disabled:bg-cyan-300 text-white px-4 py-2 rounded text-sm w-full"
|
||||
>
|
||||
{isLoading === 'shareContent' ? '실행 중...' : '공유'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 메시지 카운트 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">메시지 카운트</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="number"
|
||||
value={messageCount}
|
||||
onChange={(e) => setMessageCount(parseInt(e.target.value) || 0)}
|
||||
min="0"
|
||||
className="border border-gray-300 rounded px-3 py-1 text-sm w-20"
|
||||
placeholder="0"
|
||||
/>
|
||||
<button
|
||||
onClick={testUpdateMessageCount}
|
||||
disabled={isLoading === 'updateMessageCount'}
|
||||
className="bg-orange-500 hover:bg-orange-600 disabled:bg-orange-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'updateMessageCount' ? '실행 중...' : '배지 업데이트'}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600">
|
||||
iOS: 앱 아이콘 배지 / Android: 로그 출력
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 고급 테스트 */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-2">고급 테스트</h3>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testSafeCall}
|
||||
disabled={isLoading === 'safeCall'}
|
||||
className="bg-gray-500 hover:bg-gray-600 disabled:bg-gray-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'safeCall' ? '실행 중...' : '안전한 호출'}
|
||||
</button>
|
||||
<button
|
||||
onClick={testCallWithTimeout}
|
||||
disabled={isLoading === 'callWithTimeout'}
|
||||
className="bg-gray-500 hover:bg-gray-600 disabled:bg-gray-300 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{isLoading === 'callWithTimeout' ? '실행 중...' : '타임아웃 호출'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 테스트 결과 */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-semibold text-gray-900">테스트 결과</h2>
|
||||
<button
|
||||
onClick={clearResults}
|
||||
className="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
결과 지우기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 max-h-96 overflow-y-auto">
|
||||
{results.length === 0 ? (
|
||||
<p className="text-gray-500 text-center py-8">
|
||||
테스트 버튼을 클릭하여 결과를 확인하세요.
|
||||
</p>
|
||||
) : (
|
||||
results.map((result, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-3 rounded-lg border ${
|
||||
result.success
|
||||
? 'bg-green-50 border-green-200'
|
||||
: 'bg-red-50 border-red-200'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium text-gray-900">
|
||||
{result.method}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
{result.timestamp}
|
||||
</span>
|
||||
</div>
|
||||
{result.success ? (
|
||||
<div className="mt-2">
|
||||
<span className="text-green-800 text-sm">✅ 성공</span>
|
||||
{result.result && (
|
||||
<pre className="mt-1 text-xs bg-green-100 p-2 rounded overflow-x-auto">
|
||||
{JSON.stringify(result.result, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2">
|
||||
<span className="text-red-800 text-sm">❌ 실패</span>
|
||||
<p className="mt-1 text-xs text-red-700 bg-red-100 p-2 rounded">
|
||||
{result.error}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
31
src/pages/account/account-pages.tsx
Normal file
31
src/pages/account/account-pages.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { UserManagePage } from './user/manage-page';
|
||||
import { UserLoginAuthInfoPage } from './user/login-auth-info-page';
|
||||
import { UserAccountAuthPage } from './user/account-auth-page';
|
||||
import { UserMenuAuthPage } from './user/menu-auth-page';
|
||||
import { UserAddAccountPage } from './user/add-account-page';
|
||||
import { PasswordManagePage } from './password/manage-page';
|
||||
import { PasswordModifyLoginPasswordPage } from './password/modify-login-password-page';
|
||||
|
||||
export const AccountPages = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.account.user.base}>
|
||||
<Route path={ROUTE_NAMES.account.user.manage} element={<UserManagePage />} />
|
||||
<Route path={ROUTE_NAMES.account.user.loginAuthInfo} element={<UserLoginAuthInfoPage />} />
|
||||
<Route path={ROUTE_NAMES.account.user.accountAuth} element={<UserAccountAuthPage />} />
|
||||
<Route path={ROUTE_NAMES.account.user.menuAuth} element={<UserMenuAuthPage />} />
|
||||
<Route path={ROUTE_NAMES.account.user.addAccount} element={<UserAddAccountPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.account.password.base}>
|
||||
<Route path={ROUTE_NAMES.account.password.manage} element={<PasswordManagePage />} />
|
||||
<Route path={ROUTE_NAMES.account.password.modifyLoginPassword} element={<PasswordModifyLoginPasswordPage />} />
|
||||
</Route>
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
38
src/pages/account/password/manage-page.tsx
Normal file
38
src/pages/account/password/manage-page.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AccountTab } from '@/entities/account/ui/account-tab';
|
||||
import { PasswordManageWrap } from '@/entities/account/ui/password-manage-wrap';
|
||||
import { AccountTabKeys } from '@/entities/account/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const PasswordManagePage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<AccountTabKeys>(AccountTabKeys.PasswordManage);
|
||||
useSetHeaderTitle('계정 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<AccountTab activeTab={ activeTab }></AccountTab>
|
||||
<PasswordManageWrap></PasswordManageWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
72
src/pages/account/password/modify-login-password-page.tsx
Normal file
72
src/pages/account/password/modify-login-password-page.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const PasswordModifyLoginPasswordPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderTitle('로그인 비밀번호 변경');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.account.password.manage);
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="ing-list add">
|
||||
<div className="user-add">
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">가맹점 <span className="red">*</span></div>
|
||||
<select className="wid-100">
|
||||
<option>nictest01g</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">기존 비밀번호 <span className="red">*</span></div>
|
||||
<input
|
||||
className="wid-100"
|
||||
type="password"
|
||||
placeholder=""
|
||||
/>
|
||||
</div>
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">변경 비밀번호 <span className="red">*</span></div>
|
||||
<input
|
||||
className="wid-100 error"
|
||||
type="password"
|
||||
placeholder=""
|
||||
/>
|
||||
</div>
|
||||
<div className="ua-help error">입력 정보 불일치</div>
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">변경 비밀번호 재입력 <span className="red">*</span></div>
|
||||
<input
|
||||
className="wid-100 error"
|
||||
type="password"
|
||||
placeholder=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
>저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
50
src/pages/account/user/account-auth-page.tsx
Normal file
50
src/pages/account/user/account-auth-page.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AccountUserTab } from '@/entities/account/ui/account-user-tab';
|
||||
import { UserAccountAuthWrap } from '@/entities/account/ui/user-account-auth-wrap';
|
||||
import { AccountUserTabKeys } from '@/entities/account/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const UserAccountAuthPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const location = useLocation();
|
||||
const [tid, setTid] = useState<string>(location?.state.tid);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<AccountUserTabKeys>(AccountUserTabKeys.AccountAuth);
|
||||
useSetHeaderTitle('사용자 설정');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.account.user.manage);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log('tid : ', tid);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<AccountUserTab
|
||||
activeTab={ activeTab }
|
||||
tid={ tid }
|
||||
></AccountUserTab>
|
||||
<UserAccountAuthWrap
|
||||
tid={ tid }
|
||||
></UserAccountAuthWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
120
src/pages/account/user/add-account-page.tsx
Normal file
120
src/pages/account/user/add-account-page.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
export const UserAddAccountPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderTitle('사용자 추가');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.account.user.manage);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="ing-list add">
|
||||
<div className="user-add">
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">사용자ID <span className="red">*</span></div>
|
||||
<input
|
||||
className="wid-100 error"
|
||||
type="text"
|
||||
placeholder="ID를 입력해 주세요"
|
||||
/>
|
||||
</div>
|
||||
<div className="ua-help error">동일한 ID가 이미 존재합니다.</div>
|
||||
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">비밀번호 <span className="red">*</span></div>
|
||||
<input
|
||||
className="wid-100 error"
|
||||
type="password"
|
||||
placeholder="8자리 이상 입력해 주세요"
|
||||
/>
|
||||
</div>
|
||||
<div className="ua-help error">(오류 결과 메시지)</div>
|
||||
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">로그인 범위</div>
|
||||
<select className="wid-100">
|
||||
<option>MID + GID</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="info-divider"></div>
|
||||
<div className="user-add info">
|
||||
<div className="ua-desc">
|
||||
<div className="ua-title">본인인증용 정보 입력</div>
|
||||
<p className="ua-note">입력하신 정보는 이후 로그인 및 가맹점 관련 자료 발송에 이용됩니다.</p>
|
||||
</div>
|
||||
|
||||
<div className="ua-group">
|
||||
<div className="ua-group-header">
|
||||
<div className="ua-group-title">이메일 주소</div>
|
||||
<button
|
||||
className="ic20 plus"
|
||||
type="button"
|
||||
aria-label="이메일 추가"
|
||||
></button>
|
||||
</div>
|
||||
<div className="ua-input-row">
|
||||
<input
|
||||
className="wid-100"
|
||||
type="text"
|
||||
placeholder="example@domain.com"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ua-group">
|
||||
<div className="ua-group-header">
|
||||
<div className="ua-group-title">휴대폰 번호</div>
|
||||
<button
|
||||
className="ic20 plus"
|
||||
type="button"
|
||||
aria-label="휴대폰 추가"
|
||||
></button>
|
||||
</div>
|
||||
<div className="ua-input-row">
|
||||
<input
|
||||
className="wid-100"
|
||||
type="text"
|
||||
placeholder="01012345678"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
>저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
50
src/pages/account/user/login-auth-info-page.tsx
Normal file
50
src/pages/account/user/login-auth-info-page.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AccountUserTab } from '@/entities/account/ui/account-user-tab';
|
||||
import { UserLoginAuthInfoWrap } from '@/entities/account/ui/user-login-auth-info-wrap';
|
||||
import { AccountUserTabKeys } from '@/entities/account/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const UserLoginAuthInfoPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const location = useLocation();
|
||||
const [tid, setTid] = useState<string>(location?.state.tid);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<AccountUserTabKeys>(AccountUserTabKeys.LoginAuthInfo);
|
||||
useSetHeaderTitle('사용자 설정');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.account.user.manage);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log('tid : ', tid);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<AccountUserTab
|
||||
activeTab={ activeTab }
|
||||
tid={ tid }
|
||||
></AccountUserTab>
|
||||
<UserLoginAuthInfoWrap
|
||||
tid={ tid }
|
||||
></UserLoginAuthInfoWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
41
src/pages/account/user/manage-page.tsx
Normal file
41
src/pages/account/user/manage-page.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AccountTab } from '@/entities/account/ui/account-tab';
|
||||
import { UserManageWrap } from '@/entities/account/ui/user-manage-wrap';
|
||||
import { AccountTabKeys } from '@/entities/account/model/types';
|
||||
import { FooterItemActiveKey } from '@/entities/common/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetFooterCurrentPage,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const UserManagePage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<AccountTabKeys>(AccountTabKeys.UserManage);
|
||||
useSetHeaderTitle('계정 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetFooterCurrentPage(FooterItemActiveKey.Account);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<AccountTab activeTab={ activeTab }></AccountTab>
|
||||
<UserManageWrap></UserManageWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
134
src/pages/account/user/menu-auth-page.tsx
Normal file
134
src/pages/account/user/menu-auth-page.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const UserMenuAuthPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const location = useLocation();
|
||||
const [tid, setTid] = useState<string>(location?.state.tid);
|
||||
const [menuId, setMenuId] = useState<string>(location?.state.menuId);
|
||||
|
||||
useSetHeaderTitle('사용자 설정');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.account.user.accountAuth, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log('tid : ', tid);
|
||||
console.log('menuId : ', menuId);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="ing-list sev">
|
||||
<div className="desc service-tip">메뉴별 사용 권한을 설정해 주세요.</div>
|
||||
<div className="desc service-tip">선택한 권한에 따라 기능 이용이 제한됩니다.</div>
|
||||
|
||||
<div className="settings-section nopadding">
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title bd-style">거래내역 조회</div>
|
||||
<label className="settings-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="set-divider"></div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-title bd-sub dot">등록</span>
|
||||
<label className="settings-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-title bd-sub dot">수정</span>
|
||||
<label className="settings-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-title bd-sub dot">삭제</span>
|
||||
<label className="settings-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-title bd-sub dot">다운로드</span>
|
||||
<label className="settings-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ht-20"></div>
|
||||
<div className="settings-section">
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title bd-style">현금영수증 발행</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title bd-style">에스크로</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title bd-style">빌링</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
>저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const AccountHolderAuthPage = () => {
|
||||
useSetHeaderTitle('계좌점유인증');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const AccountHolderSearchPage = () => {
|
||||
useSetHeaderTitle('계좌성명조회');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
41
src/pages/additional-service/additional-service-pages.tsx
Normal file
41
src/pages/additional-service/additional-service-pages.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { IntroPage } from './intro/intro-page';
|
||||
import { ArsCardPaymentListPage } from './ars-card-payment/list-page';
|
||||
import { ArsCardPaymentRequestPage } from './ars-card-payment/request-page';
|
||||
import { ArsCardPaymentRequestSuccessPage } from './ars-card-payment/request-success-page';
|
||||
import { KeyInPaymentPage } from './key-in-payment/key-in-payment-page';
|
||||
import { SmsPaymentNotificationPage } from './sms-payment-notification/sms-payment-notification-page';
|
||||
import { AccountHolderSearchPage } from './account-holder-search/account-holder-search-page';
|
||||
import { AccountHolderAuthPage } from './account-holder-auth/account-holder-auth-page';
|
||||
import { LinkPaymentPage } from './link-payment/link-payment-page';
|
||||
import { KakaoPaymentNotificationPage } from './kakao-payment-notification/kakao-payment-notification-page';
|
||||
import { FundTransferPage } from './fund-transfer/fund-transfer-page';
|
||||
import { SettlementAgencyPage } from './settlement-agency/settlement-agency-page';
|
||||
import { PaymentAgencyPage } from './payment-agency/payment-agency-page';
|
||||
|
||||
export const AdditionalServicePages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.additionalService.intro} element={<IntroPage />} />
|
||||
|
||||
<Route path={ROUTE_NAMES.additionalService.arsCardPayment.base}>
|
||||
<Route path={ROUTE_NAMES.additionalService.arsCardPayment.list} element={<ArsCardPaymentListPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.arsCardPayment.request} element={<ArsCardPaymentRequestPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.arsCardPayment.requestSuccess} element={<ArsCardPaymentRequestSuccessPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.additionalService.keyInPayment} element={<KeyInPaymentPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.smsPaymentNotification} element={<SmsPaymentNotificationPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.accountHolderSearch} element={<AccountHolderSearchPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.accountHolderAuth} element={<AccountHolderAuthPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.linkPayment} element={<LinkPaymentPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.kakaoPaymentNotification} element={<KakaoPaymentNotificationPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.fundTransfer} element={<FundTransferPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.settlementAgency} element={<SettlementAgencyPage />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.paymentAgency} element={<PaymentAgencyPage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
217
src/pages/additional-service/ars-card-payment/list-page.tsx
Normal file
217
src/pages/additional-service/ars-card-payment/list-page.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const ArsCardPaymentListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderTitle('신용카드 ARS 결제');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
const onClickToNavigation = () => {
|
||||
navigate(PATHS.additionalService.arsCardPayment.request);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<section className="summary-section">
|
||||
<div className="credit-controls">
|
||||
<div>
|
||||
<input
|
||||
className="credit-period"
|
||||
type="text"
|
||||
value="2025.06.01 ~ 2025.06.30"
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button
|
||||
className="filter-btn"
|
||||
aria-label="필터"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="download-btn"
|
||||
aria-label="다운로드"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT +'/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="filter-section">
|
||||
<div className="sort-options">
|
||||
<button className="sort-btn active">최신순</button>
|
||||
<span className="sort-divider">|</span>
|
||||
<button className="sort-btn">고액순</button>
|
||||
</div>
|
||||
<div className="excrow">
|
||||
<div className="full-menu-keywords no-padding">
|
||||
<span className="keyword-tag active">전체</span>
|
||||
<span className="keyword-tag">결제완료</span>
|
||||
<span className="keyword-tag">배송등록</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="transaction-list">
|
||||
<div className="date-group">
|
||||
<div className="date-header">25.06.08(일)</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">김*환(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>20:00ㅣ미결제ㅣ결제대기ㅣSMS</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">5,254,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">최*길(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>20:00ㅣ결제완료ㅣ결제성공ㅣ호전환</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">23,845,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">박*준(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>20:00ㅣ결제완료ㅣ결제성공ㅣ호전환</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">534,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">이*신(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>20:00ㅣ미결제ㅣ취소완료ㅣSMS</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">4,254,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">김*환(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>20:00ㅣ미결제ㅣ기간만료ㅣSMS</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">948,000원</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="date-group">
|
||||
<div className="date-header">25.06.08(일)</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">신용카드(카카오머니, 카카오)</div>
|
||||
<div className="transaction-details">
|
||||
<span>승인ㅣ20:00ㅣcroquis01m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">3,583,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">신용카드(현대카드., 토스)</div>
|
||||
<div className="transaction-details">
|
||||
<span>환불ㅣ20:00ㅣcroquis01m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">874,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">계좌간편결제(국민은행, PAYU)</div>
|
||||
<div className="transaction-details">
|
||||
<span>승인ㅣ20:00ㅣcroquis01m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">23,562,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">휴대폰</div>
|
||||
<div className="transaction-details">
|
||||
<span>환불ㅣ20:00ㅣcroquis01m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">783,000원</div>
|
||||
</div>
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">SSG 은행계좌(농협중앙회)</div>
|
||||
<div className="transaction-details">
|
||||
<span>승인ㅣ20:00ㅣcroquis01m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-amount">3,923,000원</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToNavigation() }
|
||||
>결제 신청</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
163
src/pages/additional-service/ars-card-payment/request-page.tsx
Normal file
163
src/pages/additional-service/ars-card-payment/request-page.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useExtensionArsApplyMutation } from '@/entities/additional-service/api/use-extension-ars-apply-mutation';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const ArsCardPaymentRequestPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const { mutateAsync: arsApply } = useExtensionArsApplyMutation();
|
||||
|
||||
useSetHeaderTitle('결제 신청');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.additionalService.arsCardPayment.list);
|
||||
});
|
||||
|
||||
const callArsCardPaymentRequest = () => {
|
||||
let arsApplyParams = {
|
||||
mid: 'string',
|
||||
moid: 'string',
|
||||
goodsName: 'string',
|
||||
amount: 0,
|
||||
instmntMonth: '00',
|
||||
buyerName: 'string',
|
||||
phoneNumber: 'string',
|
||||
email: 'string',
|
||||
arsPaymentMethod: 'SMS',
|
||||
};
|
||||
arsApply(arsApplyParams).then((rs) => {
|
||||
navigate(PATHS.additionalService.arsCardPayment.requestSuccess);
|
||||
console.log(rs)
|
||||
}).catch(() => {
|
||||
|
||||
}).finally(() => {
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const onClickToRequest = () => {
|
||||
callArsCardPaymentRequest();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="billing-form gap-16">
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">가맹점 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="nictest001m"
|
||||
readOnly={ true }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">주문번호 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="wadizcop0g2025062"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">상품명 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="123456"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">금액 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="1000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">할부기간 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<select disabled>
|
||||
<option selected>일시불</option>
|
||||
<option>일시불</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">구매자명 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="김테스트"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">휴대폰 번호 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="01012345678"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">이메일</div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="NICE@NAVER.COM"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">결제 방식 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<div className="seg-buttons">
|
||||
<button className="btn-36 btn-blue light">SMS</button>
|
||||
<button className="btn-36 btn-white light">호전환</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToRequest() }
|
||||
>결제 신청</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
export const ArsCardPaymentRequestSuccessPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderType(HeaderType.NoHeader);
|
||||
useSetFooterMode(false);
|
||||
|
||||
const onClickToNavigate = () => {
|
||||
navigate(PATHS.additionalService.arsCardPayment.list);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="success-page" >
|
||||
<div className="success-body">
|
||||
<div
|
||||
className="success-icon"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
<h1 className="success-title">
|
||||
<span>신용카드 ARS</span><br/>
|
||||
<span>결제 신청이 완료되었습니다.</span>
|
||||
</h1>
|
||||
<div className="success-result">
|
||||
<p className="result-text">결과 : [0000] ARS 요청 처리 완료</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToNavigate() }
|
||||
>확인</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const FundTransferPage = () => {
|
||||
useSetHeaderTitle('자금이체');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
124
src/pages/additional-service/intro/intro-page.tsx
Normal file
124
src/pages/additional-service/intro/intro-page.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { IntroListItem } from '@/entities/additional-service/ui/intro-list-item';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const IntroPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderTitle('부가서비스 소개');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
const usingList = [
|
||||
{
|
||||
className: 'list-wrap01', serviceName: 'SMS 결제 통보', serviceDesc: '입금 요청부터 완료까지 SMS 자동 전송',
|
||||
icon: IMAGE_ROOT + '/icon_ing03.svg', path: PATHS.additionalService.smsPaymentNotification
|
||||
},
|
||||
{
|
||||
className: 'list-wrap01', serviceName: '신용카드 ARS 결제', serviceDesc: '전화 한 통으로 결제 성공 편리하고 안전한 서비스',
|
||||
icon: IMAGE_ROOT + '/icon_ing01.svg', path: PATHS.additionalService.arsCardPayment.list
|
||||
},
|
||||
{
|
||||
className: 'list-wrap01', serviceName: 'KEY-IN 결제', serviceDesc: '상담 중 카드정보 입력으로 간편한 결제 지원',
|
||||
icon: IMAGE_ROOT + '/icon_ing02.svg', path: PATHS.additionalService.keyInPayment
|
||||
},
|
||||
{
|
||||
className: 'list-wrap01', serviceName: '계좌성명조회', serviceDesc: '예금주 정보 입력으로 즉시 예금주 확인',
|
||||
icon: IMAGE_ROOT + '/icon_ing04.svg', path: PATHS.additionalService.accountHolderSearch
|
||||
},
|
||||
];
|
||||
const requestList = [
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '지급대행', serviceDesc: '하위 가맹점에 빠른 정산금 지급 지급대행 서비스',
|
||||
icon: IMAGE_ROOT + '/icon_ing05.svg', path: PATHS.additionalService.paymentAgency
|
||||
},
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '정산대행', serviceDesc: '하위 가맹점 정산금 계산부터 지급까지 자동 해결 서비스',
|
||||
icon: IMAGE_ROOT + '/icon_ing06.svg', path: PATHS.additionalService.settlementAgency
|
||||
},
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '링크 결제', serviceDesc: '결제 링크 전송만으로 어디서든 결제 가능 서비스',
|
||||
icon: IMAGE_ROOT + '/icon_ing07.svg', path: PATHS.additionalService.linkPayment
|
||||
},
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '자금이체', serviceDesc: '예치금으로 즉시 송금, 파일 등록만으로 다중 송금 가능',
|
||||
icon: IMAGE_ROOT + '/icon_ing08.svg', path: PATHS.additionalService.fundTransfer
|
||||
},
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '계좌점유인증', serviceDesc: '1원 송금으로 실제 계좌 점유 확인 여부',
|
||||
icon: IMAGE_ROOT + '/icon_ing09.svg', path: PATHS.additionalService.accountHolderAuth
|
||||
},
|
||||
{
|
||||
className: 'list-wrap02', serviceName: '알림톡 결제통보', serviceDesc: '결제 상태를 알림톡으로 쉽고 빠른 안내',
|
||||
icon: IMAGE_ROOT + '/icon_ing10.svg', path: PATHS.additionalService.kakaoPaymentNotification
|
||||
},
|
||||
];
|
||||
|
||||
const getUsingList = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<usingList.length;i++){
|
||||
rs.push(
|
||||
<IntroListItem
|
||||
key={ 'key-using-' + i }
|
||||
className={ usingList[i]?.className }
|
||||
serviceName={ usingList[i]?.serviceName }
|
||||
serviceDesc={ usingList[i]?.serviceDesc }
|
||||
icon={ usingList[i]?.icon }
|
||||
path={ usingList[i]?.path }
|
||||
></IntroListItem>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
const getRequestList = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<requestList.length;i++){
|
||||
rs.push(
|
||||
<IntroListItem
|
||||
key={ 'key-request-' + i }
|
||||
className={ requestList[i]?.className }
|
||||
serviceName={ requestList[i]?.serviceName }
|
||||
serviceDesc={ requestList[i]?.serviceDesc }
|
||||
icon={ requestList[i]?.icon }
|
||||
path={ requestList[i]?.path }
|
||||
></IntroListItem>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="ing-list">
|
||||
<div className="input-wrapper top-select">
|
||||
<select>
|
||||
<option value="1">nicetest00g</option>
|
||||
<option value="2">nicetest00g</option>
|
||||
<option value="3">nicetest00g</option>
|
||||
</select>
|
||||
</div>
|
||||
<h3 className="ing-title">사용중인 서비스</h3>
|
||||
{ getUsingList() }
|
||||
<h3 className="ing-title">신청 가능한 서비스</h3>
|
||||
{ getRequestList() }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const KakaoPaymentNotificationPage = () => {
|
||||
useSetHeaderTitle('알림톡 결제통보');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const KeyInPaymentPage = () => {
|
||||
useSetHeaderTitle('KEY-IN 결제');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const LinkPaymentPage = () => {
|
||||
useSetHeaderTitle('링크결제');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const PaymentAgencyPage = () => {
|
||||
useSetHeaderTitle('지급대행');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const SettlementAgencyPage = () => {
|
||||
useSetHeaderTitle('정산대행');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
import { useState } from 'react';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { SmsPaymentDetailResend } from '@/entities/additional-service/ui/sms-payment-detail-resend';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const SmsPaymentNotificationPage = () => {
|
||||
const [bottomSmsPaymentDetailResendOn, setBottomSmsPaymentDetailResendOn] = useState<boolean>(false)
|
||||
|
||||
useSetHeaderTitle('SMS 결제 통보');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
const onClickToShowDetail = () => {
|
||||
setBottomSmsPaymentDetailResendOn(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<section className="summary-section no-border">
|
||||
<div className="credit-controls">
|
||||
<div>
|
||||
<input
|
||||
className="credit-period"
|
||||
type="text"
|
||||
value="2025.06.01 ~ 2025.06.31"
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button
|
||||
className="filter-btn"
|
||||
aria-label="필터"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="download-btn"
|
||||
aria-label="다운로드"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="transaction-list">
|
||||
<div className="date-group">
|
||||
<div className="date-header">25.06.08(일)</div>
|
||||
<div
|
||||
className="transaction-item approved"
|
||||
onClick={ () => onClickToShowDetail() }
|
||||
>
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">김*환(7000)</div>
|
||||
<div className="transaction-details">
|
||||
<span>nictest01m</span>
|
||||
<span className="separator">ㅣ</span>
|
||||
<span>가상계좌 요청</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-label">재발송</div>
|
||||
</div>
|
||||
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">최*길(010333*****)</div>
|
||||
<div className="transaction-details">
|
||||
<span>nictest01m</span>
|
||||
<span className="separator">ㅣ</span>
|
||||
<span>가상계좌 요청+입금</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-label">재발송</div>
|
||||
</div>
|
||||
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">박*준(010333*****)</div>
|
||||
<div className="transaction-details">
|
||||
<span>nictest01m</span>
|
||||
<span className="separator">ㅣ</span>
|
||||
<span>가상계좌 요청+입금</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-label">재발송</div>
|
||||
</div>
|
||||
|
||||
<div className="transaction-item refund">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot gray"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">이*신(010333*****)</div>
|
||||
<div className="transaction-details">
|
||||
<span>nictest01m</span>
|
||||
<span className="separator">ㅣ</span>
|
||||
<span>가상계좌 요청</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-label">재발송</div>
|
||||
</div>
|
||||
|
||||
<div className="transaction-item approved">
|
||||
<div className="transaction-status">
|
||||
<div className="status-dot blue"></div>
|
||||
</div>
|
||||
<div className="transaction-content">
|
||||
<div className="transaction-title">김*환(010333*****)</div>
|
||||
<div className="transaction-details">
|
||||
<span>nictest01m</span>
|
||||
<span className="separator">|</span>
|
||||
<span>가상계좌 요청</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-label">재발송</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SmsPaymentDetailResend
|
||||
bottomSmsPaymentDetailResendOn={ bottomSmsPaymentDetailResendOn }
|
||||
setBottomSmsPaymentDetailResendOn={ setBottomSmsPaymentDetailResendOn }
|
||||
></SmsPaymentDetailResend>
|
||||
</>
|
||||
);
|
||||
};
|
||||
14
src/pages/alarm/alarm-pages.tsx
Normal file
14
src/pages/alarm/alarm-pages.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { ListPage } from './list/list-page';
|
||||
|
||||
export const AlarmPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.alarm.list} element={<ListPage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
28
src/pages/alarm/list/list-page.tsx
Normal file
28
src/pages/alarm/list/list-page.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { AlarmList } from '@/entities/alarm/ui/alarm-list';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const ListPage = () => {
|
||||
useSetHeaderTitle('알림함');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="pop">
|
||||
<div className="sub-wrap">
|
||||
<div className="notice-tabs">
|
||||
<button className="tab36 on">전체</button>
|
||||
<button className="tab36">혜택/이벤트</button>
|
||||
<button className="tab36">공지사항</button>
|
||||
</div>
|
||||
<AlarmList></AlarmList>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
16
src/pages/business-member/business-member-pages.tsx
Normal file
16
src/pages/business-member/business-member-pages.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { InfoPage } from './info/info-page';
|
||||
import { RegistrationStatusPage } from './registration-status/registration-status-page';
|
||||
|
||||
export const BusinessMemberPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.businessMember.info} element={<InfoPage />} />
|
||||
<Route path={ROUTE_NAMES.businessMember.registrationStatus} element={<RegistrationStatusPage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
39
src/pages/business-member/info/info-page.tsx
Normal file
39
src/pages/business-member/info/info-page.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { BusinessMemberTab } from '@/entities/business-member/ui/business-member-tab';
|
||||
import { InfoWrap } from '@/entities/business-member/ui/info-wrap';
|
||||
import { BusinessMemberTabKeys } from '@/entities/business-member/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const InfoPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<BusinessMemberTabKeys>(BusinessMemberTabKeys.Info);
|
||||
|
||||
useSetHeaderTitle('가맹점 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<BusinessMemberTab activeTab={ activeTab }></BusinessMemberTab>
|
||||
<InfoWrap></InfoWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { BusinessMemberTab } from '@/entities/business-member/ui/business-member-tab';
|
||||
import { RegistrationStatusWrap } from '@/entities/business-member/ui/registration-status-wrap';
|
||||
import { BusinessMemberTabKeys } from '@/entities/business-member/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const RegistrationStatusPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<BusinessMemberTabKeys>(BusinessMemberTabKeys.RegistrationStatus);
|
||||
|
||||
useSetHeaderTitle('가맹점 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<BusinessMemberTab activeTab={ activeTab }></BusinessMemberTab>
|
||||
<RegistrationStatusWrap></RegistrationStatusWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
109
src/pages/home/home-page.tsx
Normal file
109
src/pages/home/home-page.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { getLocalStorage } from '@/shared/lib';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { useUserInfo } from '@/entities/user/lib/use-user-info';
|
||||
import { FavoriteWrapper } from '@/entities/home/ui/favorite-wrapper';
|
||||
import { DayStatusBox } from '@/entities/home/ui/day-status-box';
|
||||
import { HomeBottomBanner } from '@/entities/home/ui/home-bottom-banner';
|
||||
import { AuthRegister } from '@/entities/home/ui/auth-reguster';
|
||||
import { FooterItemActiveKey } from '@/entities/common/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetFooterCurrentPage
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
import moment from 'moment';
|
||||
|
||||
export const HomePage = () => {
|
||||
useSetHeaderTitle('');
|
||||
useSetHeaderType(HeaderType.Home);
|
||||
useSetFooterMode(true);
|
||||
useSetFooterCurrentPage(FooterItemActiveKey.Home);
|
||||
|
||||
const { callLogin } = useUserInfo();
|
||||
|
||||
const today = moment().format('YYYYMMDD').toString();
|
||||
let bannerToday = getLocalStorage(StorageKeys.BottomBannerClose);
|
||||
|
||||
const [bottomBannerOn, setBottomBannerOn] = useState<boolean>(false);
|
||||
const [authRegisterOn, setAuthRegisterOn] = useState<boolean>(false);
|
||||
|
||||
/*
|
||||
const userParmas = {
|
||||
id: 'thenaun12',
|
||||
password: 'answjddl1!'
|
||||
};
|
||||
*/
|
||||
|
||||
const userParmas = {
|
||||
id: 'nictest00',
|
||||
password: 'nictest00'
|
||||
};
|
||||
|
||||
const handleLogin = useCallback(async () =>{
|
||||
await callLogin(userParmas);
|
||||
}, []);
|
||||
const checkBottomBannerOpen = () => {
|
||||
if(!!bannerToday){
|
||||
bannerToday = bannerToday.toString();
|
||||
}
|
||||
let sw = (today !== bannerToday);
|
||||
setBottomBannerOn(sw);
|
||||
};
|
||||
const checkAuthRegisterOpen = () => {
|
||||
setAuthRegisterOn(true);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
handleLogin();
|
||||
checkBottomBannerOpen();
|
||||
checkAuthRegisterOpen();
|
||||
}, []);
|
||||
|
||||
const setBottomBannerEffect = (mode: boolean) => {
|
||||
setBottomBannerOn(mode);
|
||||
if(mode === false){
|
||||
if(authRegisterOn){
|
||||
setAuthRegisterOn(false);
|
||||
setTimeout(() => {
|
||||
setAuthRegisterOn(true);
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*useLoginMutation
|
||||
const [userInfo] = useStore(
|
||||
useShallow((state) => [state.userSlice.userInfo]),
|
||||
);
|
||||
|
||||
*/
|
||||
// useRefreshUserInfo();
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
{/*<!-- 탭 컨텐츠 영역 -->*/}
|
||||
<div className="tab-content blue">
|
||||
<div className="tab-pane dashboard active" id="tab1">
|
||||
<FavoriteWrapper></FavoriteWrapper>
|
||||
<DayStatusBox></DayStatusBox>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<HomeBottomBanner
|
||||
setBottomBannerOn={ setBottomBannerEffect }
|
||||
bottomBannerOn={ bottomBannerOn }
|
||||
></HomeBottomBanner>
|
||||
{ (!bottomBannerOn) &&
|
||||
<AuthRegister
|
||||
setAuthRegisterOn={ setAuthRegisterOn }
|
||||
authRegisterOn={ authRegisterOn }
|
||||
></AuthRegister>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
3
src/pages/index.ts
Normal file
3
src/pages/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Home } from './Home';
|
||||
export { default as Contact } from './Contact';
|
||||
export { default as Test } from './Test';
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { PaymentTab } from '@/entities/payment/ui/payment-tab';
|
||||
import { DataNotificationWrap } from '@/entities/payment/ui/data-notification-wrap';
|
||||
import { PaymentTabKeys } from '@/entities/payment/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const DataNotificationPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<PaymentTabKeys>(PaymentTabKeys.DataNotification);
|
||||
|
||||
useSetHeaderTitle('결제 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<PaymentTab activeTab={ activeTab }></PaymentTab>
|
||||
<DataNotificationWrap></DataNotificationWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
39
src/pages/payment/info/info-page.tsx
Normal file
39
src/pages/payment/info/info-page.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { PaymentTab } from '@/entities/payment/ui/payment-tab';
|
||||
import { InfoWrap } from '@/entities/payment/ui/info-wrap';
|
||||
import { PaymentTabKeys } from '@/entities/payment/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const InfoPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<PaymentTabKeys>(PaymentTabKeys.Info);
|
||||
|
||||
useSetHeaderTitle('결제 관리');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<PaymentTab activeTab={ activeTab }></PaymentTab>
|
||||
<InfoWrap></InfoWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
16
src/pages/payment/payment-pages.tsx
Normal file
16
src/pages/payment/payment-pages.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { InfoPage } from './info/info-page';
|
||||
import { DataNotificationPage } from './data-notification/data-notification-page';
|
||||
|
||||
export const PaymentPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.payment.info} element={<InfoPage />} />
|
||||
<Route path={ROUTE_NAMES.payment.dataNotification} element={<DataNotificationPage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
108
src/pages/setting/setting-page.tsx
Normal file
108
src/pages/setting/setting-page.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const SettingPage = () => {
|
||||
useSetHeaderTitle('설정');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="pop">
|
||||
<div className="sub-wrap">
|
||||
<div className="settings-header">
|
||||
<div className="settings-title">알림 수신 설정</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="settings-divider"></div>
|
||||
|
||||
<div className="settings-section">
|
||||
<div className="settings-section-title">공지사항 알림</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title">서비스 운영 및 장애</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title">무이자 혜택 및 이벤트</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title">서비스 이용 및 정책 변경</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title">NICE소식 및 기타 정보</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-section nopadding">
|
||||
<div className="settings-section-title">정산 알림</div>
|
||||
<div className="settings-row">
|
||||
<div className="settings-row-title">정산처리 현황</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-row nopadding">
|
||||
<div className="settings-row-title">정산한도 알림</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-divider"></div>
|
||||
|
||||
<div className="settings-section nopadding">
|
||||
<div className="settings-row link">
|
||||
<div className="settings-row-title bd-style">마케팅 정보 수신 동의</div>
|
||||
<label className="settings-switch">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-row link">
|
||||
<div className="settings-row-title bd-style">로그인 방식 설정</div>
|
||||
<div className="click">ID/PW</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-row link nopadding">
|
||||
<div className="settings-row-title bd-style">서비스 언어 설정</div>
|
||||
<div className="click">기기 설정 언어</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-divider"></div>
|
||||
|
||||
<div className="settings-row danger">
|
||||
<div className="settings-row-title bd-style">로그아웃</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
39
src/pages/settlement/calendar/calendar-page.tsx
Normal file
39
src/pages/settlement/calendar/calendar-page.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { SettlementTab } from '@/entities/settlement/ui/settlement-tab';
|
||||
import { CalendarWrap } from '@/entities/settlement/ui/calandar-wrap';
|
||||
import { SettlementTabKeys } from '@/entities/settlement/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const CalendarPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<SettlementTabKeys>(SettlementTabKeys.Calendar);
|
||||
|
||||
useSetHeaderTitle('정산조회');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<SettlementTab activeTab={ activeTab }></SettlementTab>
|
||||
<CalendarWrap></CalendarWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
85
src/pages/settlement/list/detail-page.tsx
Normal file
85
src/pages/settlement/list/detail-page.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { DetailAmountInfo } from '@/entities/settlement/ui/detail-amount-info';
|
||||
import { DetailSettlementInfo } from '@/entities/settlement/ui/detail-settlement-info';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
SettlementDetailParams,
|
||||
DetailResponse,
|
||||
DetailAmountInfoProps,
|
||||
DetailSettlementInfoProps,
|
||||
} from '@/entities/settlement/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const DetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { tid } = useParams();
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [settlementInfo, setSettlementInfo] = useState<DetailSettlementInfoProps>();
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showSettlement, setShowSettlement] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('정산내역 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.settlement.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
const onClickToShowInfo = (info: string) => {
|
||||
if(info === 'amount'){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === 'settlement'){
|
||||
setShowSettlement(!showSettlement);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
<div className="txn-num-group">
|
||||
<div className="txn-amount">
|
||||
<div className="value">63,736,320<span className="unit">원</span></div>
|
||||
{/*
|
||||
<button className="chip-btn" type="button">금액상세 <img src="../images/select_arrow.svg" alt="펼치기"/></button>
|
||||
*/}
|
||||
</div>
|
||||
<div className="txn-date">2025.06.08</div>
|
||||
</div>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailAmountInfo
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailSettlementInfo
|
||||
settlementInfo={ settlementInfo }
|
||||
show={ showSettlement }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSettlementInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button className="btn-50 btn-blue flex-1">거래 취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
42
src/pages/settlement/list/list-page.tsx
Normal file
42
src/pages/settlement/list/list-page.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { SettlementTab } from '@/entities/settlement/ui/settlement-tab';
|
||||
import { ListWrap } from '@/entities/settlement/ui/list-wrap';
|
||||
import { SettlementTabKeys } from '@/entities/settlement/model/types';
|
||||
import { FooterItemActiveKey } from '@/entities/common/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetFooterCurrentPage,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const ListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<SettlementTabKeys>(SettlementTabKeys.List);
|
||||
|
||||
useSetHeaderTitle('정산조회');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetFooterCurrentPage(FooterItemActiveKey.Settlement);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<SettlementTab activeTab={ activeTab }></SettlementTab>
|
||||
<ListWrap></ListWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
16
src/pages/settlement/settlement-pages.tsx
Normal file
16
src/pages/settlement/settlement-pages.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { CalendarPage } from './calendar/calendar-page';
|
||||
import { ListPage } from './list/list-page';
|
||||
|
||||
export const SettlementPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.settlement.calendar} element={<CalendarPage />} />
|
||||
<Route path={ROUTE_NAMES.settlement.list} element={<ListPage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
18
src/pages/support/faq/detail-page.tsx
Normal file
18
src/pages/support/faq/detail-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const FaqDetailPage = () => {
|
||||
useSetHeaderTitle('자주 묻는 질문');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
18
src/pages/support/faq/list-page.tsx
Normal file
18
src/pages/support/faq/list-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const FaqListPage = () => {
|
||||
useSetHeaderTitle('자주 묻는 질문');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
48
src/pages/support/notice/detail-page.tsx
Normal file
48
src/pages/support/notice/detail-page.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const NoticeDetailPage = () => {
|
||||
useSetHeaderTitle('공지사항');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="option-list pb-120">
|
||||
<div className="notice-detail">
|
||||
<div className="notice-detail__title">[관리비 출금] 5월 관리비(4월 사용료) 출금일 변경(정정)</div>
|
||||
<div className="notice-detail__meta">2025.08.19 | 카테고리</div>
|
||||
<div className="notice-detail__divider"></div>
|
||||
<div className="notice-detail__body">안녕하세요. 페이앳 관리자입니다.
|
||||
|
||||
‘25년 5월 페이앳 관리비(4월 사용료)출금일자 변경을 다음과 같이 공지드립니다.
|
||||
|
||||
관리비 청구 데이터 확정 지연으로 부득이하게 금번 5월 페이앳 관리비 출금일자가 아래와 같이 변경되어 출금될 예정입니다.
|
||||
|
||||
서비스 운영에 참고하시기 바라며, 서비스 이용에 불편드려 죄송합니다.
|
||||
|
||||
----------- 다음 -----------
|
||||
|
||||
기존 : 매월 15일(영업일 기준)/*5월 출금일 : 19일(월)
|
||||
|
||||
변경 : 5월 19일(월)
|
||||
|
||||
세금계산서 발행일 : 5월 19일(*19일 출금분에 한함)
|
||||
|
||||
-----------------------------
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
18
src/pages/support/notice/list-page.tsx
Normal file
18
src/pages/support/notice/list-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const NoticeListPage = () => {
|
||||
useSetHeaderTitle('공지사항');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
18
src/pages/support/qna/detail-page.tsx
Normal file
18
src/pages/support/qna/detail-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const QnaDetailPage = () => {
|
||||
useSetHeaderTitle('1:1 문의');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
18
src/pages/support/qna/list-page.tsx
Normal file
18
src/pages/support/qna/list-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const QnaListPage = () => {
|
||||
useSetHeaderTitle('1:1 문의');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
30
src/pages/support/support-pages.tsx
Normal file
30
src/pages/support/support-pages.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { NoticeListPage } from './notice/list-page';
|
||||
import { NoticeDetailPage } from './notice/detail-page';
|
||||
import { FaqListPage } from './faq/list-page';
|
||||
import { FaqDetailPage } from './faq/detail-page';
|
||||
import { QnaListPage } from './qna/list-page';
|
||||
import { QnaDetailPage } from './qna/detail-page';
|
||||
|
||||
export const SupportPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.support.notice.base}>
|
||||
<Route path={ROUTE_NAMES.support.notice.list} element={<NoticeListPage />} />
|
||||
<Route path={ROUTE_NAMES.support.notice.detail} element={<NoticeDetailPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.support.faq.base}>
|
||||
<Route path={ROUTE_NAMES.support.faq.list} element={<FaqListPage />} />
|
||||
<Route path={ROUTE_NAMES.support.faq.detail} element={<FaqDetailPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.support.qna.base}>
|
||||
<Route path={ROUTE_NAMES.support.qna.list} element={<QnaListPage />} />
|
||||
<Route path={ROUTE_NAMES.support.qna.detail} element={<QnaDetailPage />} />
|
||||
</Route>
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
133
src/pages/tax/invoice/detail-page.tsx
Normal file
133
src/pages/tax/invoice/detail-page.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useLocation } from 'react-router';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useInvoiceDetailMutation } from '@/entities/tax/api/use-invoice-detail-mutation';
|
||||
import { DetailAmountInfoSection } from '@/entities/tax/ui/detail-amount-info-section';
|
||||
import { DetailPublishInfoSection } from '@/entities/tax/ui/detail-publish-info-section';
|
||||
import { DetailReceiverInfoSection } from '@/entities/tax/ui/detail-receiver-info-section';
|
||||
import { DetailSupplierInfoSection } from '@/entities/tax/ui/detail-supplier-info-section';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
DetailResponse,
|
||||
InvoiceDetailParams,
|
||||
DetailAmountInfoProps,
|
||||
DetailInfoSectionKeys,
|
||||
DetailPublishInfoProps,
|
||||
DetailReceiverInfoProps,
|
||||
DetailSupplierInfoProps
|
||||
} from '@/entities/tax/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const InvoiceDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [tid, setTid] = useState<string>(location?.state.tid);
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [publishInfo, setPublishInfo] = useState<DetailPublishInfoProps>();
|
||||
const [receiverInfo, setReceiverInfo] = useState<DetailReceiverInfoProps>();
|
||||
const [supplierInfo, setSupplierInfo] = useState<DetailSupplierInfoProps>();
|
||||
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showPublish, setShowPublish] = useState<boolean>(false);
|
||||
const [showReceiver, setShowReceiver] = useState<boolean>(false);
|
||||
const [showSupplier, setShowSupplier] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('세금계산서 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.tax.invoice.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
|
||||
const { mutateAsync: invoiceDetail } = useInvoiceDetailMutation();
|
||||
|
||||
const onClickToShowInfo = (info: DetailInfoSectionKeys) => {
|
||||
if(info === DetailInfoSectionKeys.Amount){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === DetailInfoSectionKeys.Publish){
|
||||
setShowPublish(!showReceiver);
|
||||
}
|
||||
else if(info === DetailInfoSectionKeys.Receiver){
|
||||
setShowReceiver(!showReceiver);
|
||||
}
|
||||
else if(info === DetailInfoSectionKeys.Supplier){
|
||||
setShowSupplier(!showSupplier);
|
||||
}
|
||||
};
|
||||
|
||||
const callDetail = () => {
|
||||
let invoiceDetailParams: InvoiceDetailParams = {
|
||||
svcCd: 'st',
|
||||
tid: tid
|
||||
};
|
||||
invoiceDetail(invoiceDetailParams).then((rs: DetailResponse) => {
|
||||
setAmountInfo(rs.amountInfo);
|
||||
setPublishInfo(rs.publishInfo);
|
||||
setReceiverInfo(rs.receiverInfo);
|
||||
setSupplierInfo(rs.supplierInfo);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
// callDetail();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="full-height">
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
|
||||
<DetailAmountInfoSection
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfoSection>
|
||||
|
||||
<div className="txn-divider minus"></div>
|
||||
|
||||
<DetailPublishInfoSection
|
||||
publishInfo={ publishInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPublishInfoSection>
|
||||
|
||||
<div className="txn-divider minus"></div>
|
||||
|
||||
<DetailReceiverInfoSection
|
||||
receiverInfo={ receiverInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailReceiverInfoSection>
|
||||
|
||||
<div className="txn-divider"></div>
|
||||
|
||||
<DetailSupplierInfoSection
|
||||
supplierInfo={ supplierInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSupplierInfoSection>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
};
|
||||
38
src/pages/tax/invoice/list-page.tsx
Normal file
38
src/pages/tax/invoice/list-page.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { TaxTab } from '@/entities/tax/ui/tax-tab';
|
||||
import { InvoiceListWrap } from '@/entities/tax/ui/invoice-list-wrap';
|
||||
import { TaxTabKeys } from '@/entities/tax/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const InvoiceListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<TaxTabKeys>(TaxTabKeys.InvoiceList);
|
||||
useSetHeaderTitle('부가세 신고 자료');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<TaxTab activeTab={ activeTab }></TaxTab>
|
||||
<InvoiceListWrap></InvoiceListWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
20
src/pages/tax/tax-pages.tsx
Normal file
20
src/pages/tax/tax-pages.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { InvoiceListPage } from './invoice/list-page';
|
||||
import { InvoiceDetailPage } from './invoice/detail-page';
|
||||
import { VatReferencePage } from './vat-reference/vat-reference-page';
|
||||
|
||||
export const TaxPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.tax.invoice.base}>
|
||||
<Route path={ROUTE_NAMES.tax.invoice.list} element={<InvoiceListPage />} />
|
||||
<Route path={ROUTE_NAMES.tax.invoice.detail} element={<InvoiceDetailPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.tax.vatReference} element={<VatReferencePage />} />
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
39
src/pages/tax/vat-reference/vat-reference-page.tsx
Normal file
39
src/pages/tax/vat-reference/vat-reference-page.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { TaxTab } from '@/entities/tax/ui/tax-tab';
|
||||
import { VatReferenceWrap } from '@/entities/tax/ui/vat-reference-wrap';
|
||||
import { TaxTabKeys } from '@/entities/tax/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
|
||||
export const VatReferencePage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [activeTab, setActiveTab] = useState<TaxTabKeys>(TaxTabKeys.VatReference);
|
||||
useSetHeaderTitle('부가세 신고 자료');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(true);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<TaxTab activeTab={ activeTab }></TaxTab>
|
||||
<VatReferenceWrap></VatReferenceWrap>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
103
src/pages/transaction/all-transaction/cancel-page.tsx
Normal file
103
src/pages/transaction/all-transaction/cancel-page.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { AllTransactionAllCancel } from '@/entities/transaction/ui/all-transaction-all-cancel'
|
||||
import { AllTransactionPartCancel } from '@/entities/transaction/ui/all-transaction-part-cancel'
|
||||
import { useAllTransactioCancleMutation } from '@/entities/transaction/api/use-all-transaction-cancel-mutation';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
AllTransactionCancelParams,
|
||||
AllTransactionCancelResponse,
|
||||
CancelTabKeys
|
||||
} from '@/entities/transaction/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const AllTransactionCancelPage = () => {
|
||||
const location = useLocation();
|
||||
|
||||
useSetHeaderTitle('거래 취소');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetFooterMode(false);
|
||||
|
||||
// all or part
|
||||
const [tabAction, setTabAction] = useState<CancelTabKeys>(CancelTabKeys.All);
|
||||
|
||||
const { mutateAsync: transactionCancel } = useAllTransactioCancleMutation();
|
||||
|
||||
const callTransactionCancel = () => {
|
||||
let transactionCancelParams: AllTransactionCancelParams = {
|
||||
tid: location?.state.tid,
|
||||
cancelAmount: 0,
|
||||
cancelPassword: "string",
|
||||
bankCode: "string",
|
||||
accountNo: "string",
|
||||
accountHolder: "string",
|
||||
supplyAmount: 0,
|
||||
goodsVatAmount: 0,
|
||||
taxFreeAmount: 0,
|
||||
serviceAmount: 0
|
||||
};
|
||||
transactionCancel(transactionCancelParams).then((rs: AllTransactionCancelResponse) => {
|
||||
console.log(rs);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
|
||||
}, []);
|
||||
|
||||
const onClickToChangeTab = (tab: CancelTabKeys) => {
|
||||
setTabAction(tab);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="option-list-nopadding">
|
||||
<div className="subTab">
|
||||
<button
|
||||
className={ `subtab-btn ${(tabAction === CancelTabKeys.All)? 'active': ''}` }
|
||||
onClick={ () => onClickToChangeTab(CancelTabKeys.All) }
|
||||
>전체 취소</button>
|
||||
<button
|
||||
className={ `subtab-btn ${(tabAction === CancelTabKeys.Part)? 'active': ''}` }
|
||||
onClick={ () => onClickToChangeTab(CancelTabKeys.Part) }
|
||||
>부분 취소</button>
|
||||
</div>
|
||||
<div className="cancel-list">
|
||||
<div className="amount-info">
|
||||
<ul className="amount-list">
|
||||
<li className="amount-item">
|
||||
<span className="label">· 총 잔액</span>
|
||||
<span className="value">500,000,000</span>
|
||||
</li>
|
||||
<li className="amount-item">
|
||||
<span className="label">· 총 취소금액</span>
|
||||
<span className="value">500,000,000</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{ (tabAction === CancelTabKeys.All) &&
|
||||
<AllTransactionAllCancel></AllTransactionAllCancel>
|
||||
}
|
||||
{ (tabAction === CancelTabKeys.Part) &&
|
||||
<AllTransactionPartCancel></AllTransactionPartCancel>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => callTransactionCancel() }
|
||||
>신청</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
187
src/pages/transaction/all-transaction/detail-page.tsx
Normal file
187
src/pages/transaction/all-transaction/detail-page.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useAllTransactionDetailMutation } from '@/entities/transaction/api/use-all-transaction-detail-mutation';
|
||||
import { DetailAmountInfo } from '@/entities/transaction/ui/detail-amount-info';
|
||||
import { DetailImportantInfo } from '@/entities/transaction/ui/detail-important-info';
|
||||
import { DetailPaymentInfo } from '@/entities/transaction/ui/detail-payment-info';
|
||||
import { DetailTransactionInfo } from '@/entities/transaction/ui/detail-transaction-info';
|
||||
import { DetailSettlementInfo } from '@/entities/transaction/ui/detail-settlement-info';
|
||||
import { DetailPartCancelInfo } from '@/entities/transaction/ui/detail-part-cancel-info';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
PageType,
|
||||
AllTransactionDetailParams,
|
||||
DetailResponse,
|
||||
DetailAmountInfoProps,
|
||||
DetailImportantInfoProps,
|
||||
DetailPaymentInfoProps,
|
||||
DetailTransactionInfoProps,
|
||||
DetailSettlementInfoProps,
|
||||
DetailPartCancelInfoProps,
|
||||
DetailInfoKeys
|
||||
} from '@/entities/transaction/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const AllTransactionDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { tid } = useParams();
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [importantInfo, setImportantInfo] = useState<DetailImportantInfoProps>();
|
||||
const [paymentInfo, setPaymentInfo] = useState<DetailPaymentInfoProps>();
|
||||
const [transactionInfo, setTransactionInfo] = useState<DetailTransactionInfoProps>();
|
||||
const [settlementInfo, setSettlementInfo] = useState<DetailSettlementInfoProps>();
|
||||
const [partCancelInfo, setPartCancelInfo] = useState<DetailPartCancelInfoProps>();
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showPayment, setShowPayment] = useState<boolean>(false);
|
||||
const [showTransaction, setShowTransaction] = useState<boolean>(false);
|
||||
const [showSettlement, setShowSettlement] = useState<boolean>(false);
|
||||
const [showPartCancel, setShowPartCancel] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('거래내역 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.transaction.allTransaction.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
const { mutateAsync: allTransactionDetail } = useAllTransactionDetailMutation();
|
||||
|
||||
const callDetail = () => {
|
||||
let allTransactionDetailParams: AllTransactionDetailParams = {
|
||||
svcCd: 'st',
|
||||
tid: tid
|
||||
};
|
||||
allTransactionDetail(allTransactionDetailParams).then((rs: DetailResponse) => {
|
||||
setAmountInfo(rs.amountInfo);
|
||||
setImportantInfo(rs.importantInfo);
|
||||
setPaymentInfo(rs.paymentInfo);
|
||||
setTransactionInfo(rs.transactionInfo);
|
||||
setSettlementInfo(rs.settlementInfo);
|
||||
setPartCancelInfo(rs.partCancelInfo);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
callDetail();
|
||||
}, []);
|
||||
|
||||
const onClickToNavigate = (path: string) => {
|
||||
let timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
navigate(PATHS.transaction.allTransaction.cancel, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}, 10)
|
||||
};
|
||||
|
||||
const onClickToCancel = () => {
|
||||
let msg = '거래를 취소하시겠습니까?';
|
||||
|
||||
overlay.open(({
|
||||
isOpen,
|
||||
close,
|
||||
unmount
|
||||
}) => {
|
||||
return (
|
||||
<Dialog
|
||||
afterLeave={ unmount }
|
||||
open={ isOpen }
|
||||
onClose={ close }
|
||||
onConfirmClick={ () => onClickToNavigate(PATHS.transaction.allTransaction.cancel) }
|
||||
message={ msg }
|
||||
buttonLabel={['취소', '확인']}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onClickToShowInfo = (info: DetailInfoKeys) => {
|
||||
if(info === DetailInfoKeys.Amount){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Payment){
|
||||
setShowPayment(!showPayment);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Transaction){
|
||||
setShowTransaction(!showTransaction);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Settlement){
|
||||
setShowSettlement(!showSettlement);
|
||||
}
|
||||
else if(info === DetailInfoKeys.PartCancel){
|
||||
setShowPartCancel(!showPartCancel);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
<DetailAmountInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailImportantInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
importantInfo={ importantInfo }
|
||||
></DetailImportantInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailPaymentInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
paymentInfo={ paymentInfo }
|
||||
show={ showPayment }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPaymentInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailTransactionInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
transactionInfo={ transactionInfo }
|
||||
show={ showTransaction }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailTransactionInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailSettlementInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
settlementInfo={ settlementInfo }
|
||||
show={ showSettlement }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSettlementInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailPartCancelInfo
|
||||
pageType={ PageType.AllTransaction }
|
||||
partCancelInfo={ partCancelInfo }
|
||||
show={ showPartCancel }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPartCancelInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToCancel() }
|
||||
>거래 취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
232
src/pages/transaction/all-transaction/list-page.tsx
Normal file
232
src/pages/transaction/all-transaction/list-page.tsx
Normal file
@@ -0,0 +1,232 @@
|
||||
import moment from 'moment';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AllTransactionList } from '@/entities/transaction/ui/all-transaction-list';
|
||||
import { ListItem, PageType, SortByKeys } from '@/entities/transaction/model/types';
|
||||
import { useAllTransactionListMutation } from '@/entities/transaction/api/use-all-transaction-list-mutation';
|
||||
import { useAllTransactionListSummaryMutation } from '@/entities/transaction/api/use-all-transaction-list-summary-mutation';
|
||||
import { useDownloadExcelMutation } from '@/entities/transaction/api/use-download-excel-mutation';
|
||||
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
|
||||
import { Filter } from '@/entities/transaction/ui/filter';
|
||||
import { SortOptionsBox } from '@/entities/transaction/ui/sort-options-box';
|
||||
import { FooterItemActiveKey } from '@/entities/common/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetFooterCurrentPage
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const AllTransactionListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [serviceCodeOptions, setServiceCodeOptions] = useState<Array<Record<string, any>>>();
|
||||
const [selectedServiceCode, setSelectedServiceCode] = useState<string>('st');
|
||||
const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New);
|
||||
const [listItems, setListItems] = useState({});
|
||||
const [totalTransactionCount, setTotalTransactionCount] = useState<number>(0);
|
||||
const [totalTransactionAmount, setTotalTransactionAmount] = useState<number>(0);
|
||||
const [filterOn, setFilterOn] = useState<boolean>(false);
|
||||
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
|
||||
const [fromDate, setFromDate] = useState(moment().subtract(1, 'month').format('YYYYMMDD'));
|
||||
const [toDate, setToDate] = useState(moment().format('YYYYMMDD'));
|
||||
|
||||
useSetHeaderTitle('거래내역 조회');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
useSetFooterMode(true);
|
||||
useSetFooterCurrentPage(FooterItemActiveKey.Transaction);
|
||||
|
||||
const { mutateAsync: allTransactionList } = useAllTransactionListMutation();
|
||||
const { mutateAsync: allTransactionListSummary } = useAllTransactionListSummaryMutation();
|
||||
const { mutateAsync: downloadExcel } = useDownloadExcelMutation();
|
||||
|
||||
const callList = (option?: {
|
||||
sortBy?: string,
|
||||
val?: string
|
||||
}) => {
|
||||
let listSummaryParams = {
|
||||
moid: 'string',
|
||||
tid: 'string',
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
stateCode: '0',
|
||||
serviceCode: (option?.val)? option.val: selectedServiceCode,
|
||||
minAmount: 0,
|
||||
maxAmount: 0,
|
||||
dateCl: 'TRANS',
|
||||
goodsName: 'string',
|
||||
cardCode: 'st',
|
||||
bankCode: 'str',
|
||||
searchCl: 'CARD_NO',
|
||||
searchValue: 'string',
|
||||
};
|
||||
pageParam.sortBy = (option?.sortBy)? option.sortBy: sortBy;
|
||||
setPageParam(pageParam);
|
||||
|
||||
let listParams = {
|
||||
...listSummaryParams,
|
||||
...{page: pageParam}
|
||||
};
|
||||
allTransactionList(listParams).then((rs) => {
|
||||
setListItems(assembleData(rs.content));
|
||||
});
|
||||
allTransactionListSummary(listSummaryParams).then((rs) => {
|
||||
setTotalTransactionAmount(rs.totalTransactionAmount);
|
||||
setTotalTransactionCount(rs.totalTransactionCount);
|
||||
});
|
||||
};
|
||||
|
||||
const assembleData = (content: Array<ListItem>) => {
|
||||
let data: any = {};
|
||||
if(content && content.length > 0){
|
||||
for(let i=0;i<content?.length;i++){
|
||||
let stateDate = content[i]?.stateDate;
|
||||
let groupDate = stateDate?.substring(0, 8);
|
||||
if(!!groupDate && !data.hasOwnProperty(groupDate)){
|
||||
data[groupDate] = [];
|
||||
}
|
||||
if(!!groupDate && data.hasOwnProperty(groupDate)){
|
||||
data[groupDate].push(content[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const onClickToOpenFilter = () => {
|
||||
setFilterOn(!filterOn);
|
||||
};
|
||||
const onClickToDownloadExcel = () => {
|
||||
// tid??? 확인 필요
|
||||
downloadExcel({
|
||||
// tid: tid
|
||||
}).then((rs) => {
|
||||
|
||||
});
|
||||
};
|
||||
const onCliCkToSort = (sort: SortByKeys) => {
|
||||
setSortBy(sort);
|
||||
callList({sortBy: sort});
|
||||
};
|
||||
const callServiceCodeOptions = () => {
|
||||
let options = [
|
||||
{text: '계좌간편결제', value: 'simple'},
|
||||
{text: '신용카드', value: 'card'},
|
||||
{text: '계좌이체', value: 'transfer'},
|
||||
{text: '휴대폰', value: 'phone'},
|
||||
{text: '문화상품권', value: 'gift'},
|
||||
{text: '티머니페이', value: '티머니페이'},
|
||||
];
|
||||
setServiceCodeOptions(options);
|
||||
setSelectedServiceCode('simple');
|
||||
};
|
||||
|
||||
const onChangeSelectedServiceCode = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
let val = e.target.value;
|
||||
// onchagne 에서 useState가 즉시 반영 안되므로 값을 직접 바로 넘긴다.
|
||||
setSelectedServiceCode(val);
|
||||
callList({val: val});
|
||||
};
|
||||
|
||||
const getServiceCodeOptions = () => {
|
||||
let rs = [];
|
||||
if(!!serviceCodeOptions && serviceCodeOptions.length > 0)
|
||||
for(let i=0;i<serviceCodeOptions.length;i++){
|
||||
rs.push(
|
||||
<option value={serviceCodeOptions[i]?.value}>{ serviceCodeOptions[i]?.text }</option>
|
||||
)
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callServiceCodeOptions();
|
||||
callList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="summary-section">
|
||||
<div className="summary-label">조회금액</div>
|
||||
<div className="summary-amount">
|
||||
<span className="amount-text">
|
||||
<NumericFormat
|
||||
value={ totalTransactionAmount }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
suffix={ '원' }
|
||||
></NumericFormat>
|
||||
</span>
|
||||
<div className="summary-actions">
|
||||
<button className="filter-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
onClick={ () => onClickToOpenFilter() }
|
||||
/>
|
||||
</button>
|
||||
<button className="download-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
onClick={ () => onClickToDownloadExcel() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="summary-details">
|
||||
<div className="detail-item">
|
||||
<span className="detail-label">조회일자</span>
|
||||
<span className="detail-value">{ moment(fromDate).format('YYYY.MM.DD') } ~ { moment(toDate).format('YYYY.MM.DD') }</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="detail-label">거래건수</span>
|
||||
<span className="detail-value">
|
||||
<NumericFormat
|
||||
value={ totalTransactionCount }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
prefix={ '총 ' }
|
||||
suffix={ ' 건' }
|
||||
></NumericFormat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="filter-section">
|
||||
<SortOptionsBox
|
||||
sortBy={ sortBy }
|
||||
onCliCkToSort={ onCliCkToSort }
|
||||
></SortOptionsBox>
|
||||
<select
|
||||
value={ selectedServiceCode }
|
||||
onChange={ (e) => onChangeSelectedServiceCode(e) }>
|
||||
{ getServiceCodeOptions() }
|
||||
</select>
|
||||
</div>
|
||||
<AllTransactionList
|
||||
listItems={ listItems }
|
||||
pageType={ PageType.AllTransaction }
|
||||
></AllTransactionList>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Filter
|
||||
filterOn={ filterOn }
|
||||
setFilterOn={ setFilterOn }
|
||||
></Filter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
188
src/pages/transaction/billing/detail-page.tsx
Normal file
188
src/pages/transaction/billing/detail-page.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useBillingDetailMutation } from '@/entities/transaction/api/use-billing-detail-mutation';
|
||||
import { DetailAmountInfo } from '@/entities/transaction/ui/detail-amount-info';
|
||||
import { DetailImportantInfo } from '@/entities/transaction/ui/detail-important-info';
|
||||
import { DetailPaymentInfo } from '@/entities/transaction/ui/detail-payment-info';
|
||||
import { DetailTransactionInfo } from '@/entities/transaction/ui/detail-transaction-info';
|
||||
import { DetailSettlementInfo } from '@/entities/transaction/ui/detail-settlement-info';
|
||||
import { DetailPartCancelInfo } from '@/entities/transaction/ui/detail-part-cancel-info';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
PageType,
|
||||
BillingDetailParams,
|
||||
DetailResponse,
|
||||
DetailAmountInfoProps,
|
||||
DetailImportantInfoProps,
|
||||
DetailPaymentInfoProps,
|
||||
DetailTransactionInfoProps,
|
||||
DetailSettlementInfoProps,
|
||||
DetailPartCancelInfoProps,
|
||||
DetailInfoKeys
|
||||
} from '@/entities/transaction/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const BillingDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { tid } = useParams();
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [importantInfo, setImportantInfo] = useState<DetailImportantInfoProps>();
|
||||
const [paymentInfo, setPaymentInfo] = useState<DetailPaymentInfoProps>();
|
||||
const [transactionInfo, setTransactionInfo] = useState<DetailTransactionInfoProps>();
|
||||
const [settlementInfo, setSettlementInfo] = useState<DetailSettlementInfoProps>();
|
||||
const [partCancelInfo, setPartCancelInfo] = useState<DetailPartCancelInfoProps>();
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showPayment, setShowPayment] = useState<boolean>(false);
|
||||
const [showTransaction, setShowTransaction] = useState<boolean>(false);
|
||||
const [showSettlement, setShowSettlement] = useState<boolean>(false);
|
||||
const [showPartCancel, setShowPartCancel] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('빌링 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.transaction.billing.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
const { mutateAsync: billingDetail } = useBillingDetailMutation();
|
||||
|
||||
const callDetail = () => {
|
||||
let billingDetailParams: BillingDetailParams = {
|
||||
svcCd: 'st',
|
||||
tid: tid
|
||||
};
|
||||
billingDetail(billingDetailParams).then((rs: DetailResponse) => {
|
||||
setAmountInfo(rs.amountInfo);
|
||||
setImportantInfo(rs.importantInfo);
|
||||
setPaymentInfo(rs.paymentInfo);
|
||||
setTransactionInfo(rs.transactionInfo);
|
||||
setSettlementInfo(rs.settlementInfo);
|
||||
setPartCancelInfo(rs.partCancelInfo);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
callDetail();
|
||||
}, []);
|
||||
|
||||
const onClickToNavigate = (path: string) => {
|
||||
let timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
navigate(PATHS.transaction.allTransaction.cancel, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}, 10)
|
||||
};
|
||||
|
||||
|
||||
const onClickToCancel = () => {
|
||||
let msg = '거래를 취소하시겠습니까?';
|
||||
|
||||
overlay.open(({
|
||||
isOpen,
|
||||
close,
|
||||
unmount
|
||||
}) => {
|
||||
return (
|
||||
<Dialog
|
||||
afterLeave={ unmount }
|
||||
open={ isOpen }
|
||||
onClose={ close }
|
||||
onConfirmClick={ () => onClickToNavigate(PATHS.transaction.allTransaction.cancel) }
|
||||
message={ msg }
|
||||
buttonLabel={['취소', '확인']}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onClickToShowInfo = (info: DetailInfoKeys) => {
|
||||
if(info === DetailInfoKeys.Amount){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Payment){
|
||||
setShowPayment(!showPayment);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Transaction){
|
||||
setShowTransaction(!showTransaction);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Settlement){
|
||||
setShowSettlement(!showSettlement);
|
||||
}
|
||||
else if(info === DetailInfoKeys.PartCancel){
|
||||
setShowPartCancel(!showPartCancel);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
<DetailAmountInfo
|
||||
pageType={ PageType.Billing }
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailImportantInfo
|
||||
pageType={ PageType.Billing }
|
||||
importantInfo={ importantInfo }
|
||||
></DetailImportantInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailPaymentInfo
|
||||
pageType={ PageType.Billing }
|
||||
paymentInfo={ paymentInfo }
|
||||
show={ showPayment }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPaymentInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailTransactionInfo
|
||||
pageType={ PageType.Billing }
|
||||
transactionInfo={ transactionInfo }
|
||||
show={ showTransaction }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailTransactionInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailSettlementInfo
|
||||
pageType={ PageType.Billing }
|
||||
settlementInfo={ settlementInfo }
|
||||
show={ showSettlement }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSettlementInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailPartCancelInfo
|
||||
pageType={ PageType.Billing }
|
||||
partCancelInfo={ partCancelInfo }
|
||||
show={ showPartCancel }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPartCancelInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToCancel() }
|
||||
>거래 취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
187
src/pages/transaction/billing/list-page.tsx
Normal file
187
src/pages/transaction/billing/list-page.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { BillingList } from '@/entities/transaction/ui/billing-list';
|
||||
import { ListItem, PageType, SortByKeys } from '@/entities/transaction/model/types';
|
||||
import { useBillingListMutation } from '@/entities/transaction/api/use-billing-list-mutation';
|
||||
import { useDownloadExcelMutation } from '@/entities/transaction/api/use-download-excel-mutation';
|
||||
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
|
||||
import { Filter } from '@/entities/transaction/ui/filter';
|
||||
import { SortOptionsBox } from '@/entities/transaction/ui/sort-options-box';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
const serviceCodes = [
|
||||
{name: '전체', key: 'all'},
|
||||
{name: '진행중', key: 'process'},
|
||||
{name: '성공', key: 'success'},
|
||||
{name: '요청취소', key: 'cancel'}
|
||||
];
|
||||
|
||||
export const BillingListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [selectedServiceCode, setSelectedServiceCode] = useState<string>('all');
|
||||
const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New);
|
||||
const [listItems, setListItems] = useState({});
|
||||
const [filterOn, setFilterOn] = useState<boolean>(false);
|
||||
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
|
||||
const [fromDate, setFromDate] = useState(moment().subtract(1, 'month').format('YYYYMMDD'));
|
||||
const [toDate, setToDate] = useState(moment().format('YYYYMMDD'));
|
||||
|
||||
useSetHeaderTitle('빌링');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
useSetFooterMode(true);
|
||||
|
||||
const { mutateAsync: billingList } = useBillingListMutation();
|
||||
const { mutateAsync: downloadExcel } = useDownloadExcelMutation();
|
||||
|
||||
const callList = (option?: {
|
||||
sortBy?: string,
|
||||
val?: string
|
||||
}) => {
|
||||
let listSummaryParams = {
|
||||
moid: 'string',
|
||||
tid: 'string',
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
stateCode: '0',
|
||||
serviceCode: (option?.val)? option.val: selectedServiceCode,
|
||||
minAmount: 0,
|
||||
maxAmount: 0,
|
||||
dateCl: 'TRANS',
|
||||
goodsName: 'string',
|
||||
cardCode: 'st',
|
||||
bankCode: 'str',
|
||||
searchCl: 'CARD_NO',
|
||||
searchValue: 'string',
|
||||
};
|
||||
pageParam.sortBy = (option?.sortBy)? option.sortBy: sortBy;
|
||||
setPageParam(pageParam);
|
||||
|
||||
let listParam = {
|
||||
...listSummaryParams,
|
||||
...{page: pageParam}
|
||||
};
|
||||
billingList(listParam).then((rs) => {
|
||||
setListItems(assembleData(rs.content));
|
||||
});
|
||||
}
|
||||
|
||||
const assembleData = (content: Array<ListItem>) => {
|
||||
let data: any = {};
|
||||
if(content && content.length > 0){
|
||||
for(let i=0;i<content?.length;i++){
|
||||
let stateDate = content[i]?.stateDate;
|
||||
let groupDate = stateDate?.substring(0, 8);
|
||||
if(!!groupDate && !data.hasOwnProperty(groupDate)){
|
||||
data[groupDate] = [];
|
||||
}
|
||||
if(!!groupDate && data.hasOwnProperty(groupDate)){
|
||||
data[groupDate].push(content[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const onClickToOpenFilter = () => {
|
||||
setFilterOn(!filterOn);
|
||||
};
|
||||
const onClickToDownloadExcel = () => {
|
||||
// tid??? 확인 필요
|
||||
downloadExcel({
|
||||
// tid: tid
|
||||
}).then((rs) => {
|
||||
|
||||
});
|
||||
};
|
||||
const onCliCkToSort = (sort: SortByKeys) => {
|
||||
setSortBy(sort);
|
||||
callList({sortBy: sort});
|
||||
};
|
||||
|
||||
const onClickToServiceCode = (val: string) => {
|
||||
setSelectedServiceCode(val);
|
||||
callList({val: val});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="summary-section">
|
||||
<div className="credit-controls">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="credit-period"
|
||||
value={ moment(fromDate).format('YYYY.MM.DD') + '-' + moment(toDate).format('YYYY.MM.DD') }
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button className="filter-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
onClick={ () => onClickToOpenFilter() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button className="download-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
onClick={ () => onClickToDownloadExcel() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="filter-section">
|
||||
<SortOptionsBox
|
||||
sortBy={ sortBy }
|
||||
onCliCkToSort={ onCliCkToSort }
|
||||
></SortOptionsBox>
|
||||
<div className="excrow">
|
||||
<div className="full-menu-keywords no-padding">
|
||||
{
|
||||
serviceCodes.map((value, index) => (
|
||||
<span
|
||||
key={ `key-service-code=${ index }` }
|
||||
className={ `keyword-tag ${(selectedServiceCode === value.key)? 'active': ''}` }
|
||||
onClick={ () => onClickToServiceCode(value.key) }
|
||||
>{ value.name }</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BillingList
|
||||
listItems={ listItems }
|
||||
pageType={ PageType.Billing }
|
||||
></BillingList>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Filter
|
||||
filterOn={ filterOn }
|
||||
setFilterOn={ setFilterOn }
|
||||
></Filter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
123
src/pages/transaction/billing/payment-request-page.tsx
Normal file
123
src/pages/transaction/billing/payment-request-page.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const BillingPaymentRequestPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
useSetHeaderTitle('빌링 결제 신청');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.transaction.billing.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="option-list">
|
||||
<div className="billing-title">결제 정보 입력</div>
|
||||
|
||||
<div className="billing-form">
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">빌키 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="BIKYvattest01m"
|
||||
readOnly={ true }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">상품명 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="테스트상품123"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">상품금액 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="1,000,000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">주문번호 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="P146733723"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">구매자명 <span>*</span></div>
|
||||
<div className="billing-field">
|
||||
<input
|
||||
type="text"
|
||||
value="김테스트"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">결제 요청일자</div>
|
||||
<div className="billing-field">
|
||||
<div className="input-wrapper date wid-100">
|
||||
<input
|
||||
type="text"
|
||||
value="2025/03/21"
|
||||
placeholder=""
|
||||
/>
|
||||
<button
|
||||
className="date-btn"
|
||||
type="button"
|
||||
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_date.svg' }
|
||||
alt="clear"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="billing-row">
|
||||
<div className="billing-label">할부 개월</div>
|
||||
<div className="billing-field">
|
||||
<select>
|
||||
<option value="">선택</option>
|
||||
<option value="0">일시불</option>
|
||||
<option value="2">2개월</option>
|
||||
<option value="3">3개월</option>
|
||||
<option value="6">6개월</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button className="btn-50 btn-blue flex-1">결제 신청</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
188
src/pages/transaction/cash-receit/detail-page.tsx
Normal file
188
src/pages/transaction/cash-receit/detail-page.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useCashReceitDetailMutation } from '@/entities/transaction/api/use-cash-receit-detail-mutation';
|
||||
import { DetailAmountInfo } from '@/entities/transaction/ui/detail-amount-info';
|
||||
import { DetailImportantInfo } from '@/entities/transaction/ui/detail-important-info';
|
||||
import { DetailPaymentInfo } from '@/entities/transaction/ui/detail-payment-info';
|
||||
import { DetailTransactionInfo } from '@/entities/transaction/ui/detail-transaction-info';
|
||||
import { DetailSettlementInfo } from '@/entities/transaction/ui/detail-settlement-info';
|
||||
import { DetailPartCancelInfo } from '@/entities/transaction/ui/detail-part-cancel-info';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
PageType,
|
||||
CashReceitDetailParams,
|
||||
DetailResponse,
|
||||
DetailAmountInfoProps,
|
||||
DetailImportantInfoProps,
|
||||
DetailPaymentInfoProps,
|
||||
DetailTransactionInfoProps,
|
||||
DetailSettlementInfoProps,
|
||||
DetailPartCancelInfoProps,
|
||||
DetailInfoKeys
|
||||
} from '@/entities/transaction/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const CashReceitDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { tid } = useParams();
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [importantInfo, setImportantInfo] = useState<DetailImportantInfoProps>();
|
||||
const [paymentInfo, setPaymentInfo] = useState<DetailPaymentInfoProps>();
|
||||
const [transactionInfo, setTransactionInfo] = useState<DetailTransactionInfoProps>();
|
||||
const [settlementInfo, setSettlementInfo] = useState<DetailSettlementInfoProps>();
|
||||
const [partCancelInfo, setPartCancelInfo] = useState<DetailPartCancelInfoProps>();
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showPayment, setShowPayment] = useState<boolean>(false);
|
||||
const [showTransaction, setShowTransaction] = useState<boolean>(false);
|
||||
const [showSettlement, setShowSettlement] = useState<boolean>(false);
|
||||
const [showPartCancel, setShowPartCancel] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('현금영수증 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.transaction.cashReceit.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
const { mutateAsync: escroDetail } = useCashReceitDetailMutation();
|
||||
|
||||
const callDetail = () => {
|
||||
let cashReceitDetailParams: CashReceitDetailParams = {
|
||||
svcCd: 'st',
|
||||
tid: tid
|
||||
};
|
||||
escroDetail(cashReceitDetailParams).then((rs: DetailResponse) => {
|
||||
setAmountInfo(rs.amountInfo);
|
||||
setImportantInfo(rs.importantInfo);
|
||||
setPaymentInfo(rs.paymentInfo);
|
||||
setTransactionInfo(rs.transactionInfo);
|
||||
setSettlementInfo(rs.settlementInfo);
|
||||
setPartCancelInfo(rs.partCancelInfo);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
callDetail();
|
||||
}, []);
|
||||
|
||||
const onClickToNavigate = (path: string) => {
|
||||
let timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
navigate(PATHS.transaction.allTransaction.cancel, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}, 10)
|
||||
};
|
||||
|
||||
|
||||
const onClickToCancel = () => {
|
||||
let msg = '거래를 취소하시겠습니까?';
|
||||
|
||||
overlay.open(({
|
||||
isOpen,
|
||||
close,
|
||||
unmount
|
||||
}) => {
|
||||
return (
|
||||
<Dialog
|
||||
afterLeave={ unmount }
|
||||
open={ isOpen }
|
||||
onClose={ close }
|
||||
onConfirmClick={ () => onClickToNavigate(PATHS.transaction.allTransaction.cancel) }
|
||||
message={ msg }
|
||||
buttonLabel={['취소', '확인']}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onClickToShowInfo = (info: DetailInfoKeys) => {
|
||||
if(info === DetailInfoKeys.Amount){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Payment){
|
||||
setShowPayment(!showPayment);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Transaction){
|
||||
setShowTransaction(!showTransaction);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Settlement){
|
||||
setShowSettlement(!showSettlement);
|
||||
}
|
||||
else if(info === DetailInfoKeys.PartCancel){
|
||||
setShowPartCancel(!showPartCancel);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
<DetailAmountInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailImportantInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
importantInfo={ importantInfo }
|
||||
></DetailImportantInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailPaymentInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
paymentInfo={ paymentInfo }
|
||||
show={ showPayment }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPaymentInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailTransactionInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
transactionInfo={ transactionInfo }
|
||||
show={ showTransaction }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailTransactionInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailSettlementInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
settlementInfo={ settlementInfo }
|
||||
show={ showSettlement }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSettlementInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailPartCancelInfo
|
||||
pageType={ PageType.CashReceit }
|
||||
partCancelInfo={ partCancelInfo }
|
||||
show={ showPartCancel }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPartCancelInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToCancel() }
|
||||
>거래 취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { CashReceitHandWrittenIssuanceStep1 } from '@/entities/transaction/ui/cash-receit-hand-written-issuance-step1';
|
||||
import { CashReceitHandWrittenIssuanceStep2 } from '@/entities/transaction/ui/cash-receit-hand-written-issuance-step2';
|
||||
import { ProcessStep } from '@/entities/transaction/model/types';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const CashReceitHandWrittenIssuancePage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
// 1 or 2
|
||||
const [processStep, setProcessStep] = useState<ProcessStep>(ProcessStep.One);
|
||||
|
||||
useSetHeaderTitle('수기 발행');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetFooterMode(false);
|
||||
|
||||
const onClickToChangeTab = () => {
|
||||
if(processStep === ProcessStep.One){
|
||||
setProcessStep(ProcessStep.Two);
|
||||
}
|
||||
else if(processStep === ProcessStep.Two){
|
||||
// 완료시?
|
||||
alert('완료');
|
||||
navigate(PATHS.transaction.cashReceit.list);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="option-list">
|
||||
<div className="issue-progress">
|
||||
<div className="bar">
|
||||
<div
|
||||
className="fill"
|
||||
style={{ width: '50%' }}
|
||||
></div>
|
||||
</div>
|
||||
{ (processStep === ProcessStep.One) &&
|
||||
<CashReceitHandWrittenIssuanceStep1></CashReceitHandWrittenIssuanceStep1>
|
||||
}
|
||||
{ (processStep === ProcessStep.Two) &&
|
||||
<CashReceitHandWrittenIssuanceStep2
|
||||
setProcessStep={ setProcessStep }
|
||||
></CashReceitHandWrittenIssuanceStep2>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToChangeTab() }
|
||||
>다음</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
237
src/pages/transaction/cash-receit/list-page.tsx
Normal file
237
src/pages/transaction/cash-receit/list-page.tsx
Normal file
@@ -0,0 +1,237 @@
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { CashReceitList } from '@/entities/transaction/ui/cash-receit-list';
|
||||
import { ListItem, PageType, SortByKeys } from '@/entities/transaction/model/types';
|
||||
import { useCashReceitListMutation } from '@/entities/transaction/api/use-cash-receit-list-mutation';
|
||||
import { useCashReceitListSummaryMutation } from '@/entities/transaction/api/use-cash-receit-list-summary-mutation';
|
||||
import { useDownloadExcelMutation } from '@/entities/transaction/api/use-download-excel-mutation';
|
||||
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
|
||||
import { Filter } from '@/entities/transaction/ui/filter';
|
||||
import { SortOptionsBox } from '@/entities/transaction/ui/sort-options-box';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
const serviceCodes = [
|
||||
{name: '전체', key: 'all'},
|
||||
{name: '승인', key: 'approval'},
|
||||
{name: '취소', key: 'cancel'}
|
||||
];
|
||||
|
||||
export const CashReceitListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [selectedServiceCode, setSelectedServiceCode] = useState<string>('all');
|
||||
const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New);
|
||||
const [listItems, setListItems] = useState({});
|
||||
const [totalTransactionCount, setTotalTransactionCount] = useState<number>(0);
|
||||
const [totalTransactionAmount, setTotalTransactionAmount] = useState<number>(0);
|
||||
const [filterOn, setFilterOn] = useState<boolean>(false);
|
||||
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
|
||||
const [fromDate, setFromDate] = useState(moment().subtract(1, 'month').format('YYYYMMDD'));
|
||||
const [toDate, setToDate] = useState(moment().format('YYYYMMDD'));
|
||||
|
||||
useSetHeaderTitle('현금영수증');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
useSetFooterMode(true);
|
||||
|
||||
const { mutateAsync: cashReceitList } = useCashReceitListMutation();
|
||||
const { mutateAsync: cashReceitListSummary } = useCashReceitListSummaryMutation();
|
||||
const { mutateAsync: downloadExcel } = useDownloadExcelMutation();
|
||||
|
||||
const callList = (option?: {
|
||||
sortBy?: string,
|
||||
val?: string
|
||||
}) => {
|
||||
let listSummaryParams = {
|
||||
moid: 'string',
|
||||
tid: 'string',
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
stateCode: '0',
|
||||
serviceCode: (option?.val)? option.val: selectedServiceCode,
|
||||
minAmount: 0,
|
||||
maxAmount: 0,
|
||||
dateCl: 'TRANS',
|
||||
goodsName: 'string',
|
||||
cardCode: 'st',
|
||||
bankCode: 'str',
|
||||
searchCl: 'CARD_NO',
|
||||
searchValue: 'string',
|
||||
};
|
||||
pageParam.sortBy = (option?.sortBy)? option.sortBy: sortBy;
|
||||
setPageParam(pageParam);
|
||||
|
||||
let listParams = {
|
||||
...listSummaryParams,
|
||||
...{page: pageParam}
|
||||
};
|
||||
cashReceitList(listParams).then((rs) => {
|
||||
setListItems(assembleData(rs.content));
|
||||
});
|
||||
cashReceitListSummary(listSummaryParams).then((rs) => {
|
||||
setTotalTransactionAmount(rs.totalTransactionAmount);
|
||||
setTotalTransactionCount(rs.totalTransactionCount);
|
||||
});
|
||||
};
|
||||
|
||||
const assembleData = (content: Array<ListItem>) => {
|
||||
let data: any = {};
|
||||
if(content && content.length > 0){
|
||||
for(let i=0;i<content?.length;i++){
|
||||
let stateDate = content[i]?.stateDate;
|
||||
let groupDate = stateDate?.substring(0, 8);
|
||||
if(!!groupDate && !data.hasOwnProperty(groupDate)){
|
||||
data[groupDate] = [];
|
||||
}
|
||||
if(!!groupDate && data.hasOwnProperty(groupDate)){
|
||||
data[groupDate].push(content[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const onClickToOpenFilter = () => {
|
||||
setFilterOn(!filterOn);
|
||||
};
|
||||
const onClickToDownloadExcel = () => {
|
||||
// tid??? 확인 필요
|
||||
downloadExcel({
|
||||
// tid: tid
|
||||
}).then((rs) => {
|
||||
|
||||
});
|
||||
};
|
||||
const onCliCkToSort = (sort: SortByKeys) => {
|
||||
setSortBy(sort);
|
||||
callList({sortBy: sort});
|
||||
};
|
||||
|
||||
const onClickToServiceCode = (val: string) => {
|
||||
setSelectedServiceCode(val);
|
||||
callList({val: val});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="summary-section">
|
||||
<div className="credit-controls">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="credit-period"
|
||||
value={ moment(fromDate).format('YYYY.MM.DD') + '-' + moment(toDate).format('YYYY.MM.DD') }
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button className="filter-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
onClick={ () => onClickToOpenFilter() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button className="download-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
onClick={ () => onClickToDownloadExcel() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="credit-summary">
|
||||
<div className="row">
|
||||
<span className="label">승인</span>
|
||||
<strong className="amount22">
|
||||
<NumericFormat
|
||||
value={ 83745200 }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
suffix={ '원' }
|
||||
></NumericFormat>
|
||||
</strong>
|
||||
<span className="count">
|
||||
<NumericFormat
|
||||
value={ 2745 }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
prefix={ '(' }
|
||||
suffix={ '건)' }
|
||||
></NumericFormat>
|
||||
</span>
|
||||
</div>
|
||||
<div className="row">
|
||||
<span className="label">취소</span>
|
||||
<strong className="amount19">
|
||||
<NumericFormat
|
||||
value={ 534407 }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
suffix={ '원' }
|
||||
></NumericFormat>
|
||||
</strong>
|
||||
<span className="count">
|
||||
<NumericFormat
|
||||
value={ 32 }
|
||||
thousandSeparator
|
||||
displayType="text"
|
||||
prefix={ '(' }
|
||||
suffix={ '건)' }
|
||||
></NumericFormat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="filter-section">
|
||||
<SortOptionsBox
|
||||
sortBy={ sortBy }
|
||||
onCliCkToSort={ onCliCkToSort }
|
||||
></SortOptionsBox>
|
||||
<div>
|
||||
<div className="full-menu-keywords no-padding">
|
||||
{
|
||||
serviceCodes.map((value, index) => (
|
||||
<span
|
||||
key={ `key-service-code=${ index }` }
|
||||
className={ `keyword-tag ${(selectedServiceCode === value.key)? 'active': ''}` }
|
||||
onClick={ () => onClickToServiceCode(value.key) }
|
||||
>{ value.name }</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CashReceitList
|
||||
listItems={ listItems }
|
||||
pageType={ PageType.CashReceit }
|
||||
></CashReceitList>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Filter
|
||||
filterOn={ filterOn }
|
||||
setFilterOn={ setFilterOn }
|
||||
></Filter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
188
src/pages/transaction/escro/detail-page.tsx
Normal file
188
src/pages/transaction/escro/detail-page.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useEscroDetailMutation } from '@/entities/transaction/api/use-escro-detail-mutation';
|
||||
import { DetailAmountInfo } from '@/entities/transaction/ui/detail-amount-info';
|
||||
import { DetailImportantInfo } from '@/entities/transaction/ui/detail-important-info';
|
||||
import { DetailPaymentInfo } from '@/entities/transaction/ui/detail-payment-info';
|
||||
import { DetailTransactionInfo } from '@/entities/transaction/ui/detail-transaction-info';
|
||||
import { DetailSettlementInfo } from '@/entities/transaction/ui/detail-settlement-info';
|
||||
import { DetailPartCancelInfo } from '@/entities/transaction/ui/detail-part-cancel-info';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
PageType,
|
||||
EscroDetailParams,
|
||||
DetailResponse,
|
||||
DetailAmountInfoProps,
|
||||
DetailImportantInfoProps,
|
||||
DetailPaymentInfoProps,
|
||||
DetailTransactionInfoProps,
|
||||
DetailSettlementInfoProps,
|
||||
DetailPartCancelInfoProps,
|
||||
DetailInfoKeys
|
||||
} from '@/entities/transaction/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
export const EscroDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { tid } = useParams();
|
||||
|
||||
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
|
||||
const [importantInfo, setImportantInfo] = useState<DetailImportantInfoProps>();
|
||||
const [paymentInfo, setPaymentInfo] = useState<DetailPaymentInfoProps>();
|
||||
const [transactionInfo, setTransactionInfo] = useState<DetailTransactionInfoProps>();
|
||||
const [settlementInfo, setSettlementInfo] = useState<DetailSettlementInfoProps>();
|
||||
const [partCancelInfo, setPartCancelInfo] = useState<DetailPartCancelInfoProps>();
|
||||
const [showAmount, setShowAmount] = useState<boolean>(false);
|
||||
const [showPayment, setShowPayment] = useState<boolean>(false);
|
||||
const [showTransaction, setShowTransaction] = useState<boolean>(false);
|
||||
const [showSettlement, setShowSettlement] = useState<boolean>(false);
|
||||
const [showPartCancel, setShowPartCancel] = useState<boolean>(false);
|
||||
|
||||
useSetHeaderTitle('에스크로 상세');
|
||||
useSetHeaderType(HeaderType.RightClose);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.transaction.escro.list);
|
||||
});
|
||||
useSetFooterMode(false);
|
||||
|
||||
const { mutateAsync: escroDetail } = useEscroDetailMutation();
|
||||
|
||||
const callDetail = () => {
|
||||
let escroDetailParams: EscroDetailParams = {
|
||||
svcCd: 'st',
|
||||
tid: tid
|
||||
};
|
||||
escroDetail(escroDetailParams).then((rs: DetailResponse) => {
|
||||
setAmountInfo(rs.amountInfo);
|
||||
setImportantInfo(rs.importantInfo);
|
||||
setPaymentInfo(rs.paymentInfo);
|
||||
setTransactionInfo(rs.transactionInfo);
|
||||
setSettlementInfo(rs.settlementInfo);
|
||||
setPartCancelInfo(rs.partCancelInfo);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
callDetail();
|
||||
}, []);
|
||||
|
||||
const onClickToNavigate = (path: string) => {
|
||||
let timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
navigate(PATHS.transaction.allTransaction.cancel, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}, 10)
|
||||
};
|
||||
|
||||
|
||||
const onClickToCancel = () => {
|
||||
let msg = '거래를 취소하시겠습니까?';
|
||||
|
||||
overlay.open(({
|
||||
isOpen,
|
||||
close,
|
||||
unmount
|
||||
}) => {
|
||||
return (
|
||||
<Dialog
|
||||
afterLeave={ unmount }
|
||||
open={ isOpen }
|
||||
onClose={ close }
|
||||
onConfirmClick={ () => onClickToNavigate(PATHS.transaction.allTransaction.cancel) }
|
||||
message={ msg }
|
||||
buttonLabel={['취소', '확인']}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onClickToShowInfo = (info: DetailInfoKeys) => {
|
||||
if(info === DetailInfoKeys.Amount){
|
||||
setShowAmount(!showAmount);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Payment){
|
||||
setShowPayment(!showPayment);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Transaction){
|
||||
setShowTransaction(!showTransaction);
|
||||
}
|
||||
else if(info === DetailInfoKeys.Settlement){
|
||||
setShowSettlement(!showSettlement);
|
||||
}
|
||||
else if(info === DetailInfoKeys.PartCancel){
|
||||
setShowPartCancel(!showPartCancel);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active">
|
||||
<div className="option-list">
|
||||
<div className="txn-detail">
|
||||
<DetailAmountInfo
|
||||
pageType={ PageType.Escro }
|
||||
amountInfo={ amountInfo }
|
||||
show={ showAmount }
|
||||
tid={ tid }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailAmountInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailImportantInfo
|
||||
pageType={ PageType.Escro }
|
||||
importantInfo={ importantInfo }
|
||||
></DetailImportantInfo>
|
||||
<div className="txn-divider minus"></div>
|
||||
<DetailPaymentInfo
|
||||
pageType={ PageType.Escro }
|
||||
paymentInfo={ paymentInfo }
|
||||
show={ showPayment }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPaymentInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailTransactionInfo
|
||||
pageType={ PageType.Escro }
|
||||
transactionInfo={ transactionInfo }
|
||||
show={ showTransaction }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailTransactionInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailSettlementInfo
|
||||
pageType={ PageType.Escro }
|
||||
settlementInfo={ settlementInfo }
|
||||
show={ showSettlement }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailSettlementInfo>
|
||||
<div className="txn-divider"></div>
|
||||
<DetailPartCancelInfo
|
||||
pageType={ PageType.Escro }
|
||||
partCancelInfo={ partCancelInfo }
|
||||
show={ showPartCancel }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></DetailPartCancelInfo>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToCancel() }
|
||||
>거래 취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
186
src/pages/transaction/escro/list-page.tsx
Normal file
186
src/pages/transaction/escro/list-page.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { EscroList } from '@/entities/transaction/ui/escro-list';
|
||||
import { ListItem, PageType, SortByKeys } from '@/entities/transaction/model/types';
|
||||
import { useEscroListMutation } from '@/entities/transaction/api/use-escro-list-mutation';
|
||||
import { useDownloadExcelMutation } from '@/entities/transaction/api/use-download-excel-mutation';
|
||||
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
|
||||
import { Filter } from '@/entities/transaction/ui/filter';
|
||||
import { SortOptionsBox } from '@/entities/transaction/ui/sort-options-box';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import {
|
||||
useSetOnBack,
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
|
||||
const serviceCodes = [
|
||||
{name: '전체', key: 'all'},
|
||||
{name: '결제완료', key: 'paid'},
|
||||
{name: '배송등록', key: 'register'},
|
||||
];
|
||||
|
||||
export const EscroListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [selectedServiceCode, setSelectedServiceCode] = useState<string>('all');
|
||||
const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New);
|
||||
const [listItems, setListItems] = useState({});
|
||||
const [filterOn, setFilterOn] = useState<boolean>(false);
|
||||
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
|
||||
const [fromDate, setFromDate] = useState(moment().subtract(1, 'month').format('YYYYMMDD'));
|
||||
const [toDate, setToDate] = useState(moment().format('YYYYMMDD'));
|
||||
|
||||
useSetHeaderTitle('에스크로');
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetOnBack(() => {
|
||||
navigate(PATHS.home);
|
||||
});
|
||||
useSetFooterMode(true);
|
||||
|
||||
const { mutateAsync: escroList } = useEscroListMutation();
|
||||
const { mutateAsync: downloadExcel } = useDownloadExcelMutation();
|
||||
|
||||
const callList = (option?: {
|
||||
sortBy?: string,
|
||||
val?: string
|
||||
}) => {
|
||||
let listSummaryParams = {
|
||||
moid: 'string',
|
||||
tid: 'string',
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
stateCode: '0',
|
||||
serviceCode: (option?.val)? option.val: selectedServiceCode,
|
||||
minAmount: 0,
|
||||
maxAmount: 0,
|
||||
dateCl: 'TRANS',
|
||||
goodsName: 'string',
|
||||
cardCode: 'st',
|
||||
bankCode: 'str',
|
||||
searchCl: 'CARD_NO',
|
||||
searchValue: 'string',
|
||||
};
|
||||
pageParam.sortBy = (option?.sortBy)? option.sortBy: sortBy;
|
||||
setPageParam(pageParam);
|
||||
|
||||
let listParam = {
|
||||
...listSummaryParams,
|
||||
...{page: pageParam}
|
||||
};
|
||||
escroList(listParam).then((rs) => {
|
||||
setListItems(assembleData(rs.content));
|
||||
});
|
||||
};
|
||||
|
||||
const assembleData = (content: Array<ListItem>) => {
|
||||
let data: any = {};
|
||||
if(content && content.length > 0){
|
||||
for(let i=0;i<content?.length;i++){
|
||||
let stateDate = content[i]?.stateDate;
|
||||
let groupDate = stateDate?.substring(0, 8);
|
||||
if(!!groupDate && !data.hasOwnProperty(groupDate)){
|
||||
data[groupDate] = [];
|
||||
}
|
||||
if(!!groupDate && data.hasOwnProperty(groupDate)){
|
||||
data[groupDate].push(content[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const onClickToOpenFilter = () => {
|
||||
setFilterOn(!filterOn);
|
||||
};
|
||||
const onClickToDownloadExcel = () => {
|
||||
// tid??? 확인 필요
|
||||
downloadExcel({
|
||||
// tid: tid
|
||||
}).then((rs) => {
|
||||
|
||||
});
|
||||
};
|
||||
const onCliCkToSort = (sort: SortByKeys) => {
|
||||
setSortBy(sort);
|
||||
callList({sortBy: sort});
|
||||
};
|
||||
|
||||
const onClickToServiceCode = (val: string) => {
|
||||
setSelectedServiceCode(val);
|
||||
callList({val: val});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane sub active" id="tab1">
|
||||
<div className="summary-section">
|
||||
<div className="credit-controls">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="credit-period"
|
||||
value={ moment(fromDate).format('YYYY.MM.DD') + '-' + moment(toDate).format('YYYY.MM.DD') }
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button className="filter-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_setting.svg' }
|
||||
alt="검색옵션"
|
||||
onClick={ () => onClickToOpenFilter() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button className="download-btn">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_download.svg' }
|
||||
alt="다운로드"
|
||||
onClick={ () => onClickToDownloadExcel() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="filter-section">
|
||||
<SortOptionsBox
|
||||
sortBy={ sortBy }
|
||||
onCliCkToSort={ onCliCkToSort }
|
||||
></SortOptionsBox>
|
||||
<div className="excrow">
|
||||
<div className="full-menu-keywords no-padding">
|
||||
{
|
||||
serviceCodes.map((value, index) => (
|
||||
<span
|
||||
key={ `key-service-code=${ index }` }
|
||||
className={ `keyword-tag ${(selectedServiceCode === value.key)? 'active': ''}` }
|
||||
onClick={ () => onClickToServiceCode(value.key) }
|
||||
>{ value.name }</span>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<EscroList
|
||||
listItems={ listItems }
|
||||
pageType={ PageType.Escro }
|
||||
></EscroList>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Filter
|
||||
filterOn={ filterOn }
|
||||
setFilterOn={ setFilterOn }
|
||||
></Filter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
43
src/pages/transaction/transaction-pages.tsx
Normal file
43
src/pages/transaction/transaction-pages.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Route } from 'react-router-dom';
|
||||
import { SentryRoutes } from '@/shared/configs/sentry';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { AllTransactionListPage } from './all-transaction/list-page';
|
||||
import { AllTransactionDetailPage } from './all-transaction/detail-page';
|
||||
import { AllTransactionCancelPage } from './all-transaction/cancel-page';
|
||||
import { CashReceitListPage } from './cash-receit/list-page';
|
||||
import { CashReceitDetailPage } from './cash-receit/detail-page';
|
||||
import { CashReceitHandWrittenIssuancePage } from './cash-receit/hand-written-issuance-page';
|
||||
import { EscroListPage } from './escro/list-page';
|
||||
import { EscroDetailPage } from './escro/detail-page';
|
||||
import { BillingListPage } from './billing/list-page';
|
||||
import { BillingDetailPage } from './billing/detail-page';
|
||||
import { BillingPaymentRequestPage } from './billing/payment-request-page';
|
||||
|
||||
|
||||
export const TransactionPages = () => {
|
||||
return (
|
||||
<>
|
||||
<SentryRoutes>
|
||||
<Route path={ROUTE_NAMES.transaction.allTransaction.base}>
|
||||
<Route path={ROUTE_NAMES.transaction.allTransaction.list} element={<AllTransactionListPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.allTransaction.detail} element={<AllTransactionDetailPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.allTransaction.cancel} element={<AllTransactionCancelPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.transaction.cashReceit.base}>
|
||||
<Route path={ROUTE_NAMES.transaction.cashReceit.list} element={<CashReceitListPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.cashReceit.detail} element={<CashReceitDetailPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.cashReceit.handWrittenIssuance} element={<CashReceitHandWrittenIssuancePage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.transaction.escro.base}>
|
||||
<Route path={ROUTE_NAMES.transaction.escro.list} element={<EscroListPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.escro.detail} element={<EscroDetailPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.transaction.billing.base}>
|
||||
<Route path={ROUTE_NAMES.transaction.billing.list} element={<BillingListPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.billing.detail} element={<BillingDetailPage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.billing.paymentRequest} element={<BillingPaymentRequestPage />} />
|
||||
</Route>
|
||||
</SentryRoutes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user