79492184d0
- WBS-CRM-02: 상담 이력 (consultations 테이블 V008, ClientDetail.razor) - WBS-CRM-03: 문의→고객 전환 (V009 client_id FK, InquiryDetail 고객등록 버튼) - WBS-CRM-04: 신고 일정 캘린더 (tax_filings 테이블 V010, TaxFilingList.razor) - WBS-CRM-05: 문의 상태 5단계 확장 (V011, InquiryStatus enum, InquiryList 탭) - WBS-MKT-04: 시즌 시뮬레이터 어드민 페이지 (SeasonSimulator.razor) - WBS-UX-04: 개인정보처리방침 /taxbaik/privacy, 이용약관 /taxbaik/terms - Dashboard.razor 마감 임박 신고 위젯 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
212 lines
10 KiB
Plaintext
212 lines
10 KiB
Plaintext
@page "/admin/season-simulator"
|
|
@attribute [Authorize]
|
|
@using TaxBaik.Application.Seasonal
|
|
@using TaxBaik.Application.Services
|
|
|
|
<PageTitle>시즌 시뮬레이터</PageTitle>
|
|
|
|
<section class="admin-page-hero">
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="admin-eyebrow">Season Preview</MudText>
|
|
<MudText Typo="Typo.h4" Class="admin-page-title">시즌 시뮬레이터</MudText>
|
|
<MudText Typo="Typo.body2" Class="admin-page-subtitle">날짜를 선택해 홈페이지 시즌 화면이 어떻게 보이는지 미리 확인합니다.</MudText>
|
|
</div>
|
|
</section>
|
|
|
|
<MudGrid>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-3">시뮬레이션 날짜</MudText>
|
|
<MudDatePicker @bind-Date="simulationDate" Label="날짜 선택" DateFormat="yyyy-MM-dd" PickerVariant="PickerVariant.Static" />
|
|
<MudDivider Class="my-3" />
|
|
<MudText Typo="Typo.subtitle2" Class="mb-2">연간 세무 캘린더</MudText>
|
|
@foreach (var season in TaxSeasonCalendar.Seasons)
|
|
{
|
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" FullWidth="true"
|
|
Class="mb-1" Color="Color.Primary"
|
|
OnClick="@(() => JumpToSeason(season))">
|
|
@season.StartMonth/@season.StartDay — @season.Name
|
|
</MudButton>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" md="8">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-1">
|
|
@(simulationDate?.ToString("yyyy년 MM월 dd일") ?? "날짜를 선택하세요") 홈페이지 미리보기
|
|
</MudText>
|
|
@if (activeSeason != null)
|
|
{
|
|
<MudChip T="string" Color="Color.Warning" Size="Size.Small" Class="mb-3">
|
|
@activeSeason.Name 시즌 활성
|
|
</MudChip>
|
|
<MudDivider Class="mb-4" />
|
|
<!-- Hero 섹션 미리보기 -->
|
|
<div style="background: linear-gradient(135deg, #1a365d 0%, #2a4365 100%); border-radius: 12px; padding: 2rem; color: white; margin-bottom: 1.5rem;">
|
|
@if (activeSeason.DaysUntilDeadline <= 7 && activeSeason.DaysUntilDeadline >= 0)
|
|
{
|
|
<div style="background: #f59e0b; color: #1a202c; display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 0.8rem; font-weight: 700; margin-bottom: 1rem;">
|
|
D-@activeSeason.DaysUntilDeadline 마감 임박
|
|
</div>
|
|
}
|
|
<div style="font-size: 1.8rem; font-weight: 800; white-space: pre-line; margin-bottom: 0.5rem; line-height: 1.3;">
|
|
@activeSeason.HeroHeadline
|
|
</div>
|
|
<div style="font-size: 0.95rem; color: rgba(255,255,255,0.8); margin-bottom: 1.5rem;">
|
|
@activeSeason.HeroSubtext
|
|
</div>
|
|
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
|
|
<div style="background: #e53e3e; color: white; padding: 10px 20px; border-radius: 8px; font-weight: 700; font-size: 0.95rem;">
|
|
@activeSeason.CtaText
|
|
</div>
|
|
<div style="background: transparent; border: 2px solid rgba(255,255,255,0.5); color: white; padding: 10px 20px; border-radius: 8px; font-size: 0.95rem;">
|
|
서비스 안내
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<MudGrid Spacing="2">
|
|
<MudItem xs="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">활성 시즌 키</MudText>
|
|
<MudText><code>@activeSeason.Key</code></MudText>
|
|
</MudItem>
|
|
<MudItem xs="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">마감까지</MudText>
|
|
<MudText>
|
|
@if (activeSeason.DaysUntilDeadline >= 0)
|
|
{
|
|
<MudChip T="string" Size="Size.Small"
|
|
Color="@(activeSeason.DaysUntilDeadline <= 7 ? Color.Error : Color.Warning)">
|
|
D-@activeSeason.DaysUntilDeadline
|
|
</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<span>마감 후 @(-activeSeason.DaysUntilDeadline)일</span>
|
|
}
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">포커스 서비스</MudText>
|
|
<MudText>@activeSeason.FocusService</MudText>
|
|
</MudItem>
|
|
<MudItem xs="6">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">블로그 카테고리</MudText>
|
|
<MudText>@activeSeason.RelatedCategorySlug</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">긴박감 배지 문구</MudText>
|
|
<MudText><code>@activeSeason.UrgencyBadge</code></MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
}
|
|
else
|
|
{
|
|
<MudAlert Severity="Severity.Info">
|
|
선택한 날짜(@(simulationDate?.ToString("MM월 dd일") ?? "-"))는 시즌 비활성 기간입니다.
|
|
홈페이지는 기본 Hero를 표시합니다.
|
|
</MudAlert>
|
|
<div style="background: linear-gradient(135deg, #1a365d 0%, #2a4365 100%); border-radius: 12px; padding: 2rem; color: white; margin-top: 1.5rem;">
|
|
<div style="font-size: 1.8rem; font-weight: 800; margin-bottom: 0.5rem;">
|
|
사업자 세금, 부동산,<br/>가족자산까지
|
|
</div>
|
|
<div style="font-size: 0.95rem; color: rgba(255,255,255,0.8); margin-bottom: 1.5rem;">
|
|
세무사·부동산중개사·보험설계사 자격 보유 | 온라인 맞춤 상담
|
|
</div>
|
|
<div style="background: #e53e3e; color: white; display: inline-block; padding: 10px 20px; border-radius: 8px; font-weight: 700;">
|
|
무료 상담 신청
|
|
</div>
|
|
</div>
|
|
}
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4 mt-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-3">연간 시즌 타임라인</MudText>
|
|
<MudSimpleTable Dense="true">
|
|
<thead>
|
|
<tr>
|
|
<th>기간</th>
|
|
<th>시즌</th>
|
|
<th>블로그 카테고리</th>
|
|
<th>상태</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var s in TaxSeasonCalendar.Seasons)
|
|
{
|
|
var isActive = activeSeason?.Key == s.Key;
|
|
<tr style="@(isActive ? "background: rgba(66,153,225,0.1);" : "")">
|
|
<td style="white-space: nowrap;">
|
|
@s.StartMonth/@s.StartDay ~ @s.EndMonth/@s.EndDay
|
|
</td>
|
|
<td>@s.Name</td>
|
|
<td><code style="font-size:0.8rem;">@s.RelatedCategorySlug</code></td>
|
|
<td>
|
|
@if (isActive)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Success">활성</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<span style="color: #a0aec0; font-size: 0.85rem;">비활성</span>
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
@code {
|
|
private DateTime? simulationDate = DateTime.Today;
|
|
private CurrentSeasonDto? activeSeason;
|
|
|
|
protected override void OnInitialized() => ComputeSeason();
|
|
|
|
private void ComputeSeason()
|
|
{
|
|
if (simulationDate == null) { activeSeason = null; return; }
|
|
var date = simulationDate.Value;
|
|
var season = TaxSeasonCalendar.Seasons.FirstOrDefault(s =>
|
|
{
|
|
var start = new DateTime(date.Year, s.StartMonth, s.StartDay);
|
|
var endYear = (s.EndMonth < s.StartMonth) ? date.Year + 1 : date.Year;
|
|
var end = new DateTime(endYear, s.EndMonth, s.EndDay);
|
|
return date >= start && date <= end;
|
|
});
|
|
|
|
if (season == null) { activeSeason = null; return; }
|
|
|
|
var endYearCalc = (season.EndMonth < season.StartMonth) ? date.Year + 1 : date.Year;
|
|
var deadline = new DateTime(endYearCalc, season.EndMonth, season.EndDay);
|
|
var ddays = (deadline.Date - date.Date).Days;
|
|
|
|
var badge = ddays <= 7 && ddays >= 0
|
|
? season.UrgencyBadge.Replace("{n}", ddays.ToString())
|
|
: season.UrgencyBadge;
|
|
|
|
activeSeason = new CurrentSeasonDto
|
|
{
|
|
Key = season.Key,
|
|
Name = season.Name,
|
|
HeroHeadline = season.HeroHeadline,
|
|
HeroSubtext = season.HeroSubtext,
|
|
UrgencyBadge = badge,
|
|
FocusService = season.FocusService,
|
|
RelatedCategorySlug = season.RelatedCategorySlug,
|
|
CtaText = season.CtaText,
|
|
DaysUntilDeadline = ddays,
|
|
Deadline = deadline
|
|
};
|
|
}
|
|
|
|
private void JumpToSeason(TaxSeason season)
|
|
{
|
|
simulationDate = new DateTime(DateTime.Today.Year, season.StartMonth, season.StartDay);
|
|
ComputeSeason();
|
|
}
|
|
}
|