page.tsx 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. 'use client';
  2. import { useState } from 'react';
  3. import { useRouter } from 'next/navigation';
  4. import Link from 'next/link';
  5. import { fetchApi } from '@/lib/utils/client';
  6. import { useStudioContext } from '@/app/studio/context';
  7. import { useRankConfigContext } from '../context';
  8. import { Separator } from '@/components/ui/separator';
  9. import RankPreviewPanel from '../_components/RankPreviewPanel';
  10. import RankFormPanel from '../_components/RankFormPanel';
  11. import { createEmptyForm, parseInput } from '../types';
  12. import type { FormState } from '../types';
  13. export default function RankAddPage()
  14. {
  15. const router = useRouter();
  16. const { channelID } = useStudioContext();
  17. const { setSaving, fetchList } = useRankConfigContext();
  18. const [form, setForm] = useState<FormState>(createEmptyForm());
  19. const [localSaving, setLocalSaving] = useState(false);
  20. // ── 폼 필드 변경 ────────────────────────────────
  21. const handleFormChange = <K extends keyof FormState>(field: K, value: FormState[K]) => {
  22. setForm(prev => ({ ...prev, [field]: value }));
  23. };
  24. // ── 저장 ─────────────────────────────────────────
  25. const handleSave = async () => {
  26. if (!channelID) {
  27. return;
  28. }
  29. if (!form.title.trim()) {
  30. alert('제목을 입력해 주세요.');
  31. return;
  32. }
  33. setLocalSaving(true);
  34. setSaving(true);
  35. try {
  36. await fetchApi('/api/studio/donation/rank/config', {
  37. method: 'POST',
  38. body: {
  39. channelID,
  40. ...form,
  41. startAt: form.startAt ? (parseInput(form.startAt) || undefined) : undefined,
  42. endAt: form.endAt ? (parseInput(form.endAt) || undefined) : undefined
  43. }
  44. });
  45. alert('등록되었습니다.');
  46. fetchList();
  47. router.push('/studio/donation/rank/list');
  48. } catch (err) {
  49. alert(err instanceof Error ? err.message : '저장에 실패했습니다.');
  50. } finally {
  51. setLocalSaving(false);
  52. setSaving(false);
  53. }
  54. };
  55. // ── 취소 ─────────────────────────────────────────
  56. const handleCancel = () => {
  57. router.push('/studio/donation/rank/list');
  58. };
  59. return (
  60. <>
  61. <div className="studio-page__title-row">
  62. <h1 className="studio-page__title">후원 순위 추가</h1>
  63. <Link href="/studio/donation/rank/list" className="rank-config__btn rank-config__btn--sm">< 목록으로</Link>
  64. </div>
  65. <div className="pt-5 pb-5">
  66. <Separator orientation="horizontal" />
  67. </div>
  68. <div className="rank-config__layout">
  69. <RankPreviewPanel form={form} />
  70. <Separator orientation="vertical" />
  71. <RankFormPanel
  72. form={form}
  73. editingItem={null}
  74. saving={localSaving}
  75. onFormChange={handleFormChange}
  76. onSave={handleSave}
  77. onCancel={handleCancel}
  78. />
  79. </div>
  80. </>
  81. );
  82. }