CrewWidgetListPanel.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. 'use client';
  2. import { useState } from 'react';
  3. import { useRouter } from 'next/navigation';
  4. import { fetchApi } from '@/lib/utils/client';
  5. import { useStudioContext } from '@/app/studio/context';
  6. import { useCrewWidgetConfigContext } from '../context';
  7. import { CREW_PERIODS, CREW_WIDGET_THEMES } from '../constants';
  8. import { formatDateTime } from '../types';
  9. import { Button } from '@/components/ui/button';
  10. import { Checkbox } from '@/components/ui/checkbox';
  11. export default function CrewWidgetListPanel()
  12. {
  13. const router = useRouter();
  14. const { channelID } = useStudioContext();
  15. const { items, loading, fetchList } = useCrewWidgetConfigContext();
  16. const [selected, setSelected] = useState<Set<number>>(new Set());
  17. const toggleSelect = (id: number) => {
  18. setSelected(prev => {
  19. const next = new Set(prev);
  20. if (next.has(id)) next.delete(id); else next.add(id);
  21. return next;
  22. });
  23. };
  24. const toggleAll = () => {
  25. if (selected.size === items.length) {
  26. setSelected(new Set());
  27. } else {
  28. setSelected(new Set(items.map(i => i.id)));
  29. }
  30. };
  31. const handleBatchDelete = async () => {
  32. if (selected.size === 0) {
  33. return;
  34. }
  35. if (!confirm(`${selected.size}개 설정을 삭제하시겠습니까?`)) {
  36. return;
  37. }
  38. for (const id of selected) {
  39. try {
  40. await fetchApi(`/api/studio/crew/widget/config/${id}/${channelID}`, { method: 'DELETE' });
  41. } catch {}
  42. }
  43. setSelected(new Set());
  44. fetchList();
  45. };
  46. const getPeriodLabel = (p: number) => CREW_PERIODS.find(x => x.value === p)?.label ?? '-';
  47. const getThemeLabel = (t: number) => CREW_WIDGET_THEMES.find(x => x.value === t)?.label ?? '-';
  48. if (loading) return <p className="studio-page__empty">준비 중...</p>;
  49. return (
  50. <div className="studio-page">
  51. <div className="studio-page__header">
  52. <h1 className="studio-page__title">크루 위젯 설정</h1>
  53. <div className="studio-page__header-actions">
  54. {selected.size > 0 && (
  55. <Button variant="destructive" size="sm" onClick={handleBatchDelete}>
  56. {selected.size}개 삭제
  57. </Button>
  58. )}
  59. <Button size="sm" onClick={() => router.push('/studio/donation/crew/widget/add')}>
  60. + 추가
  61. </Button>
  62. </div>
  63. </div>
  64. <div className="studio-page__table-wrap">
  65. <table className="studio-page__table">
  66. <thead>
  67. <tr>
  68. <th>
  69. <Checkbox checked={items.length > 0 && selected.size === items.length} onCheckedChange={toggleAll} />
  70. </th>
  71. <th>제목</th>
  72. <th>기간</th>
  73. <th>테마</th>
  74. <th>최대 표시</th>
  75. <th>활성</th>
  76. <th>작업</th>
  77. </tr>
  78. </thead>
  79. <tbody>
  80. {items.length === 0 ? (
  81. <tr><td colSpan={7} className="studio-page__empty">등록된 위젯 설정이 없습니다.</td></tr>
  82. ) : items.map(item => (
  83. <tr key={item.id}>
  84. <td>
  85. <Checkbox checked={selected.has(item.id)} onCheckedChange={() => toggleSelect(item.id)} />
  86. </td>
  87. <td>{item.title}</td>
  88. <td>
  89. {getPeriodLabel(item.period)}
  90. {item.period === 5 && item.startAt && item.endAt && (
  91. <div style={{ fontSize: '0.75rem', color: 'var(--muted-foreground)' }}>
  92. {formatDateTime(item.startAt)} ~ {formatDateTime(item.endAt)}
  93. </div>
  94. )}
  95. </td>
  96. <td>{getThemeLabel(item.theme)}</td>
  97. <td>{item.maxDisplayCount}명</td>
  98. <td>
  99. <span className={`studio-page__badge studio-page__badge--${item.isActive ? 'active' : 'inactive'}`}>
  100. {item.isActive ? '활성' : '비활성'}
  101. </span>
  102. </td>
  103. <td>
  104. <Button variant="outline" size="sm" onClick={() => router.push(`/studio/donation/crew/widget/edit/${item.id}`)}>
  105. 수정
  106. </Button>
  107. </td>
  108. </tr>
  109. ))}
  110. </tbody>
  111. </table>
  112. </div>
  113. </div>
  114. );
  115. }