GoalPreviewPanel.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 'use client';
  2. import { useEffect, useState, type CSSProperties } from 'react';
  3. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  4. import { faPlay } from '@fortawesome/free-solid-svg-icons';
  5. import {
  6. DONATION_AMOUNT_MIN, DONATION_AMOUNT_MAX, DONATION_AMOUNT_STEP, DONATION_AMOUNT_DEFAULT
  7. } from '@/constants/donation';
  8. import type { FormState } from '../types';
  9. type Props = {
  10. form: FormState;
  11. };
  12. export default function GoalPreviewPanel({ form }: Props) {
  13. const [previewAmount, setPreviewAmount] = useState(DONATION_AMOUNT_DEFAULT);
  14. const [simulatedAmount, setSimulatedAmount] = useState(0);
  15. // form.startAmount가 변경되면 시뮬레이션 누적값 초기화
  16. useEffect(() => {
  17. setSimulatedAmount(0);
  18. }, [form.startAmount]);
  19. const currentAmount = form.startAmount + simulatedAmount;
  20. const percent = form.targetAmount > 0 ? Math.min((currentAmount / form.targetAmount) * 100, 100) : 0;
  21. const titleStyle = {
  22. '--title-font': form.titleFontFamily || 'inherit',
  23. '--title-size': `${form.titleFontSizePx}px`,
  24. '--title-color': form.titleFontColor
  25. } as CSSProperties;
  26. const barWrapStyle = {
  27. '--bar-height': `${form.barHeightPx}px`,
  28. '--bar-bg': form.barBackgroundColor
  29. } as CSSProperties;
  30. const barFillStyle = {
  31. '--bar-percent': `${percent}%`,
  32. '--bar-color': form.barColor
  33. } as CSSProperties;
  34. const amountStyle = {
  35. '--amount-font': form.amountFontFamily || 'inherit',
  36. '--amount-size': `${form.amountFontSizePx}px`,
  37. '--amount-color': form.amountFontColor
  38. } as CSSProperties;
  39. const handlePreview = () => {
  40. setSimulatedAmount(prev => prev + (previewAmount || DONATION_AMOUNT_DEFAULT));
  41. };
  42. const handleReset = () => {
  43. setSimulatedAmount(0);
  44. };
  45. return (
  46. <aside className="goal-config__preview-panel">
  47. <div className="goal-config__widget">
  48. <div className="goal-config__widget-label">미리보기</div>
  49. <div className="goal-config__widget-body">
  50. <div className="goal-preview">
  51. <div className="goal-preview__title" style={titleStyle}>
  52. {form.title || '후원 목표'}
  53. </div>
  54. <div className="goal-preview__bar-wrap" style={barWrapStyle}>
  55. <div className="goal-preview__bar-fill" style={barFillStyle} />
  56. <div className="goal-preview__amount" style={amountStyle}>
  57. {currentAmount.toLocaleString()}원 / {form.targetAmount.toLocaleString()}원
  58. {form.isShowPercent && (
  59. <span className="goal-preview__percent">
  60. {' '}({Math.round(percent)}%)
  61. </span>
  62. )}
  63. </div>
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. <fieldset className="goal-config__fieldset">
  69. <legend className="goal-config__legend">미리보기</legend>
  70. <div className="goal-config__preview-form">
  71. <div className="goal-config__field">
  72. <label htmlFor="goal-preview-amount" className="goal-config__field-label">후원 금액 (원)</label>
  73. <input
  74. id="goal-preview-amount"
  75. type="number"
  76. className="goal-config__input"
  77. min={DONATION_AMOUNT_MIN}
  78. max={DONATION_AMOUNT_MAX}
  79. step={DONATION_AMOUNT_STEP}
  80. value={previewAmount}
  81. onChange={e => setPreviewAmount(parseInt(e.target.value) || DONATION_AMOUNT_DEFAULT)}
  82. />
  83. </div>
  84. <div className="goal-config__preview-actions">
  85. <button
  86. type="button"
  87. className="goal-config__btn goal-config__btn--primary goal-config__preview-btn"
  88. onClick={handlePreview}
  89. >
  90. <FontAwesomeIcon icon={faPlay} />
  91. 미리보기
  92. </button>
  93. <button
  94. type="button"
  95. className="goal-config__btn"
  96. onClick={handleReset}
  97. disabled={simulatedAmount === 0}
  98. >
  99. 초기화
  100. </button>
  101. </div>
  102. {simulatedAmount > 0 && (
  103. <p className="goal-config__field-hint">누적: +{simulatedAmount.toLocaleString()}원</p>
  104. )}
  105. </div>
  106. </fieldset>
  107. </aside>
  108. );
  109. }