Files
ef_front/app/usr/2_mychannel/page.tsx

266 lines
12 KiB
TypeScript
Raw Normal View History

2025-09-07 22:57:43 +00:00
"use client";
import CalenderSelector from "@/app/components/CalenderSelector";
import Modal from "@/app/components/Modal";
import { useEffect, useRef, useState } from "react";
2025-09-09 00:15:08 +00:00
import { useSession } from "next-auth/react";
2025-09-07 22:57:43 +00:00
export default function Page() {
2025-09-09 00:15:08 +00:00
const { data: session } = useSession();
2025-09-07 22:57:43 +00:00
const [isreqmodal, setIsreqmodal] = useState(false);
2025-09-09 00:15:08 +00:00
const [isLoading, setIsLoading] = useState(false);
2025-09-07 22:57:43 +00:00
const [chanellist, setChanellist] = useState<string[]>([]);
const [contentlist, setContentlist] = useState<{
id: string;
email: string;
pubDate: string;
is_approved: boolean;
is_certified: boolean;
video_count: number;
views: number;
revenue: number;
pendingRevenue: number;
}[]>([]);
const [channelList, setChannelList] = useState<{
id: string;
handle: string;
createtime: string;
is_approved: boolean;
icon: string;
}[]>([]);
const [registerCode, setRegisterCode] = useState<string>("");
const handleInputRef = useRef<HTMLInputElement>(null);
const formatNumberWithCommas = (number: number): string => {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const register_channel = async () => {
2025-09-09 00:15:08 +00:00
if (isLoading) return;
if (!(handleInputRef.current && handleInputRef.current.value)) {
alert("채널 핸들을 입력해주세요");
return;
}
const inputValue = handleInputRef.current.value;
setIsLoading(true);
try {
const response = await fetch(`/api/channel/register?handle=${encodeURIComponent(inputValue)}`);
const data = await response.json();
if (!response.ok) {
alert(data?.error ?? data?.message ?? '등록 요청 실패');
return;
2025-09-07 22:57:43 +00:00
}
2025-09-09 00:15:08 +00:00
alert('등록 요청을 전송했습니다.');
fetchListChannel();
setIsreqmodal(false);
} catch (error) {
console.error('등록 요청 중 오류:', error);
alert('등록 요청 실패');
} finally {
setIsLoading(false);
}
2025-09-07 22:57:43 +00:00
}
2025-09-09 00:15:08 +00:00
const fetchRegisterCode = async () => {
try {
const resp = await fetch('/api/channel/mycode', { cache: 'no-store' });
const data = await resp.json();
setRegisterCode(data.registerCode);
} catch (e) {
console.error('register_code 요청 에러:', e);
}
}
2025-09-07 22:57:43 +00:00
const fetchListChannel = async () => {
try {
2025-09-09 00:15:08 +00:00
const resp = await fetch('/api/channel/list', { cache: 'no-store' });
2025-09-07 22:57:43 +00:00
const data = await resp.json();
setChannelList(data.items);
console.log('list_channel:', data);
} catch (e) {
console.error('list_channel 요청 에러:', e);
}
}
2025-09-09 00:15:08 +00:00
const delete_channel = async (handle: string) => {
if (isLoading) return;
if (!handle) return;
const ok = confirm(`정말 "${handle}" 채널 연결을 해제하시겠습니까?`);
if (!ok) return;
setIsLoading(true);
try {
const resp = await fetch(`/api/channel/delete?handle=${encodeURIComponent(handle)}`);
const data = await resp.json();
if (!resp.ok) {
alert(data?.error ?? '삭제 실패');
return;
}
alert(data?.message ?? '채널 연결을 해제했습니다.');
await fetchListChannel();
} catch (e) {
console.error('delete_channel 요청 에러:', e);
alert('삭제 요청 실패');
} finally {
setIsLoading(false);
}
}
2025-09-07 22:57:43 +00:00
useEffect(() => {
// 세션 이메일 기준 채널 목록 조회 로그
fetchListChannel();
2025-09-09 00:15:08 +00:00
fetchRegisterCode();
2025-09-07 22:57:43 +00:00
}, []);
return (
<div className="flex flex-col w-[100vw] p-2 lg:w-[calc(100vw-350px)] max-w-[1200px] mx-auto">
<div className="flex flex-row h-[64px] justify-between items-center">
<div className="flex flex-row h-[36px]">
</div>
<div className="flex flex-row h-[36px]">
<div
className="w-[104px] h-[36px] rounded-lg flex items-center justify-center bg-[#F94B37] border-[#D73B29] text-white cursor-pointer hover:bg-[#D73B29]"
2025-09-09 00:15:08 +00:00
onClick={() => {setIsreqmodal(true); }}
2025-09-07 22:57:43 +00:00
>
</div>
</div>
</div>
<div className="
border-1 border-[#e6e9ef] rounded-lg bg-white overflow-y-auto
">
<table className="w-full h-full border-separate border-spacing-y-1 table-fixed px-[10px]">
<colgroup>
<col className="min-w-[250px] max-w-[300px]"/>
<col className="w-[120px]"/>
<col className="w-[80px]"/>
<col className="w-[100px]"/>
<col className="w-[100px]"/>
<col className="w-[110px]"/>
2025-09-09 00:15:08 +00:00
<col className="w-[90px]"/>
2025-09-07 22:57:43 +00:00
</colgroup>
<thead>
<tr className="sticky top-0 bg-white h-[49px] ">
<th className="border-b-1 right-border border-[#e6e9ef] "> </th>
<th className="border-b-1 right-border border-[#e6e9ef] "> </th>
<th className="border-b-1 right-border border-[#e6e9ef] "></th>
<th className="border-b-1 right-border border-[#e6e9ef] "></th>
<th className="border-b-1 right-border border-[#e6e9ef] "></th>
2025-09-09 00:15:08 +00:00
<th className="border-b-1 right-border border-[#e6e9ef] "></th>
<th className="border-b-1 border-[#e6e9ef] "></th>
2025-09-07 22:57:43 +00:00
</tr>
</thead>
<tbody>
{
2025-09-09 00:15:08 +00:00
channelList && channelList.map((channel)=>{
2025-09-07 22:57:43 +00:00
return (
<tr key={channel.id} className="h-[54px] border-1 border-[#e6e9ef] rounded-lg font-semibold">
<td className="right-border rounded-l-lg border-l-1 border-t-1 border-b-1 border-[#e6e9ef] pl-2 h-[54px] " >
<div className="flex flex-row items-center gap-2">
<div className="w-[48px] h-[48px] rounded-full border-1 border-[#e6e9ef]">
<img src={channel.icon} alt="channel icon" className="w-[48px] h-[48px] rounded-full" />
</div>
<div>
{channel.handle}
</div>
</div>
</td>
<td className="right-border border-b-1 border-t-1 border-[#e6e9ef] text-center whitespace-nowrap overflow-hidden">{channel.createtime.split("T")[0]}</td>
<td className="right-border border-b-1 border-t-1 border-[#e6e9ef] text-center whitespace-nowrap overflow-hidden">{channel.is_approved? "승인" : "미승인"}</td>
<td className="right-border border-b-1 border-t-1 border-[#e6e9ef] text-center"> - </td>
<td className="right-border border-b-1 border-t-1 border-[#e6e9ef] text-center"> - </td>
2025-09-09 00:15:08 +00:00
<td className="right-border border-b-1 border-t-1 border-[#e6e9ef] text-center"> - </td>
<td className="border-b-1 border-t-1 border-[#e6e9ef] border-r-1 rounded-r-lg text-center">
<button
className={`px-3 py-1 rounded-md ${isLoading ? 'bg-gray-300 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'} text-white`}
onClick={() => { if (!isLoading) delete_channel(channel.handle); }}
>
</button>
</td>
2025-09-07 22:57:43 +00:00
</tr>
)
})}
</tbody>
</table>
</div>
<Modal isOpen={isreqmodal} onClose={() => setIsreqmodal(false)}>
<div className=" w-full h-full flex flex-col justify-start gap-5">
<div className="flex flex-row justify-between items-center">
<div className="text-3xl font-semibold"></div>
<div className="text-3xl cursor-pointer" onClick={() => setIsreqmodal(false)}>
<svg width="18" height="17" viewBox="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M0.951569 0.451569C1.4202 -0.0170597 2.18 -0.0170597 2.64863 0.451569L9.0001 6.80304L15.3516 0.451569C15.8202 -0.0170597 16.58 -0.0170597 17.0486 0.451569C17.5173 0.920199 17.5173 1.68 17.0486 2.14863L10.6972 8.5001L17.0486 14.8516C17.5173 15.3202 17.5173 16.08 17.0486 16.5486C16.58 17.0173 15.8202 17.0173 15.3516 16.5486L9.0001 10.1972L2.64863 16.5486C2.18 17.0173 1.4202 17.0173 0.951569 16.5486C0.48294 16.08 0.48294 15.3202 0.951569 14.8516L7.30304 8.5001L0.951569 2.14863C0.48294 1.68 0.48294 0.920199 0.951569 0.451569Z" fill="#848484" />
</svg>
</div>
</div>
2025-09-09 00:15:08 +00:00
<div className="flex flex-col sh-[287px] gap-3">
2025-09-07 22:57:43 +00:00
<div className="flex flex-col justify-between">
<div className="text-lg font-semibold text-black"></div>
<div className="flex flex-row justify-between items-center">
2025-09-09 00:15:08 +00:00
<div className=" h-[56px] flex-1 border-[#d5d5d5] border-1 bg-white text-black font-normal rounded-lg flex items-center justify-center text-md p-1 px-2">
<input type="text" className="w-full h-full border-none outline-none" ref={handleInputRef} />
2025-09-07 22:57:43 +00:00
</div>
</div>
</div>
2025-09-09 00:15:08 +00:00
<div className="flex flex-col justify-between ">
2025-09-07 22:57:43 +00:00
<div className="text-lg font-semibold text-black"> </div>
<div className="flex flex-row items-center gap-2">
2025-09-09 00:15:08 +00:00
2025-09-07 22:57:43 +00:00
<div className="w-full h-[56px] border-[#d5d5d5] border-1 bg-white text-black font-normal rounded-lg flex items-center justify-start text-xl p-5">
2025-09-09 00:15:08 +00:00
{registerCode ? registerCode : "코드 로드 에러"}
2025-09-07 22:57:43 +00:00
</div>
{registerCode && (
2025-09-09 00:15:08 +00:00
<div
2025-09-07 22:57:43 +00:00
className="w-[45px] h-[56px] border-[#d5d5d5] border-1 bg-white rounded-lg flex items-center justify-center cursor-pointer"
onClick={() => {
navigator.clipboard.writeText(registerCode);
}}
>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2025-09-09 00:15:08 +00:00
<path d="M20 9H11C9.89543 9 9 9.89543 9 11V20C9 21.1046 9.89543 22 11 22H20C21.1046 22 22 21.1046 22 20V11C22 9.89543 21.1046 9 20 9Z" stroke="#666666" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M5 15H4C3.46957 15 2.96086 14.7893 2.58579 14.4142C2.21071 14.0391 2 13.5304 2 13V4C2 3.46957 2.21071 2.96086 2.58579 2.58579C2.96086 2.21071 3.46957 2 4 2H13C13.5304 2 14.0391 2.21071 14.4142 2.58579C14.7893 2.96086 15 3.46957 15 4V5" stroke="#666666" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
2025-09-07 22:57:43 +00:00
</svg>
</div>
)}
2025-09-09 00:15:08 +00:00
</div>
2025-09-07 22:57:43 +00:00
2025-09-09 00:15:08 +00:00
</div>
2025-09-07 22:57:43 +00:00
2025-09-09 00:15:08 +00:00
<div className =" flex flex-row justify-between items-center gap-3">
<div className="text-sm text-gray-500">
* .
2025-09-07 22:57:43 +00:00
</div>
2025-09-09 00:15:08 +00:00
<div className={`mt-3 h-[56px] flex-1 border-[#D73B29] bg-[#F94B37] text-white font-bold rounded-lg flex items-center justify-center text-xl transition-colors ${isLoading ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer hover:bg-[#D73B29]'}`} onClick={() => { if (!isLoading) register_channel(); }}>
2025-09-07 22:57:43 +00:00
</div>
</div>
</div>
</div>
</Modal>
2025-09-09 00:15:08 +00:00
{isLoading && (
<div className="fixed inset-0 z-[1000] bg-black/40 backdrop-blur-sm flex items-center justify-center">
<div className="w-12 h-12 rounded-full border-4 border-white/30 border-t-white animate-spin" />
</div>
)}
2025-09-07 22:57:43 +00:00
</div>
);
}