page.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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 { useGoalConfigContext } from '../context';
  8. import { Separator } from '@/components/ui/separator';
  9. import GoalPreviewPanel from '../_components/GoalPreviewPanel';
  10. import GoalFormPanel from '../_components/GoalFormPanel';
  11. import { createEmptyForm, parseInput } from '../types';
  12. import type { FormState } from '../types';
  13. export default function GoalAddPage()
  14. {
  15. const router = useRouter();
  16. const { channelID } = useStudioContext();
  17. const { setSaving, fetchList } = useGoalConfigContext();
  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. if (form.targetAmount < 1) {
  34. alert('목표금액은 1원 이상이어야 합니다.');
  35. return;
  36. }
  37. setLocalSaving(true);
  38. setSaving(true);
  39. try {
  40. await fetchApi('/api/studio/donation/goal/config', {
  41. method: 'POST',
  42. body: {
  43. channelID,
  44. ...form,
  45. startAt: parseInput(form.startAt ?? '') || undefined,
  46. endAt: parseInput(form.endAt ?? '') || undefined,
  47. titleFontFamily: form.titleFontFamily || null,
  48. amountFontFamily: form.amountFontFamily || null
  49. }
  50. });
  51. alert('등록되었습니다.');
  52. fetchList();
  53. router.push('/studio/donation/goal/list');
  54. } catch (err) {
  55. alert(err instanceof Error ? err.message : '저장에 실패했습니다.');
  56. } finally {
  57. setLocalSaving(false);
  58. setSaving(false);
  59. }
  60. };
  61. // ── 취소 ─────────────────────────────────────────
  62. const handleCancel = () => {
  63. router.push('/studio/donation/goal/list');
  64. };
  65. return (
  66. <>
  67. <div className="studio-page__title-row">
  68. <h1 className="studio-page__title">후원 목표 추가</h1>
  69. <Link href="/studio/donation/goal/list" className="goal-config__btn goal-config__btn--sm">< 목록으로</Link>
  70. </div>
  71. <div className="pt-5 pb-5">
  72. <Separator orientation="horizontal" />
  73. </div>
  74. <div className="goal-config__layout">
  75. <GoalPreviewPanel form={form} />
  76. <Separator orientation="vertical" />
  77. <GoalFormPanel
  78. form={form}
  79. editingItem={null}
  80. saving={localSaving}
  81. onFormChange={handleFormChange}
  82. onSave={handleSave}
  83. onCancel={handleCancel}
  84. />
  85. </div>
  86. </>
  87. );
  88. }