feat: revamp UI/UX of homepage & portal, clean warnings, and update ROADMAP_WBS
TaxBaik CI/CD / build-and-deploy (push) Successful in 54s
TaxBaik CI/CD / build-and-deploy (push) Successful in 54s
This commit is contained in:
@@ -522,3 +522,46 @@ Todo:
|
|||||||
- WBS-UX-03/04 구현 완료
|
- WBS-UX-03/04 구현 완료
|
||||||
- WBS-CRM-01/02/03/04/05 구현 완료 (배포 후 검증 필요)
|
- WBS-CRM-01/02/03/04/05 구현 완료 (배포 후 검증 필요)
|
||||||
- WBS-CRM-06/07/08 (텔레그램·포털·소셜 로그인) Phase 3 미착수
|
- WBS-CRM-06/07/08 (텔레그램·포털·소셜 로그인) Phase 3 미착수
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ── 홈페이지 · 어드민 · 포털 프리미엄 UX/UI 개편 (2026-06-30) ──────────────────
|
||||||
|
|
||||||
|
## WBS-UX-05 홈페이지 프리미엄 UI 및 마이크로 인터랙션
|
||||||
|
|
||||||
|
목표: 홈페이지 디자인을 극도로 모던하고 신뢰성 있는 프리미엄 스타일로 전면 개편한다.
|
||||||
|
|
||||||
|
성공 기준:
|
||||||
|
- Hero 섹션에 유려한 배경 그라데이션 및 부드러운 CSS 애니메이션 효과 적용
|
||||||
|
- 서비스 카드에 섀도우 및 보더 트랜지션, 골드/그린 그라데이션 호버 이펙트 추가
|
||||||
|
- 신뢰도 스트립 카드에 입체감 및 돋보이는 레이아웃 설계
|
||||||
|
- Noto Sans KR 외에 Outfit/Inter 등의 보조 영문 폰트 결합으로 타이포그래피 고급화
|
||||||
|
|
||||||
|
Todo:
|
||||||
|
- [x] `site.css` 내 Hero 섹션 그라데이션 및 CSS 애니메이션 보강
|
||||||
|
- [x] 서비스 카드 및 신뢰도 스트립 컴포넌트 프리미엄 스타일로 개편
|
||||||
|
- [x] 홈페이지 폰트 스택 확장 및 메인 레이아웃 적용
|
||||||
|
|
||||||
|
## WBS-PORTAL-01 고객 포털 UI/UX 고도화 및 글래스모피즘
|
||||||
|
|
||||||
|
목표: 고객 마이 포털 화면을 미려하고 현대적인 글래스모피즘 디자인으로 개편하여 이용 가치를 극대화한다.
|
||||||
|
|
||||||
|
성공 기준:
|
||||||
|
- 포털 메인 대시보드 카드를 Glassmorphism 스타일(blur, semi-transparent border)로 변경
|
||||||
|
- 세무 신고 현황 테이블 및 상담 이력 타임라인 컴포넌트의 모던 디자인화
|
||||||
|
|
||||||
|
Todo:
|
||||||
|
- [x] `site.css` 내 포털 전용 모던 글래스모피즘 클래스군 추가
|
||||||
|
- [x] `Portal/Index.cshtml` 레이아웃 및 컴포넌트 UI 고도화
|
||||||
|
|
||||||
|
## WBS-MAINT-02 코드 품질 및 경고 결함 차단
|
||||||
|
|
||||||
|
목표: 빌드 컴파일 타임 경고(Warnings)를 0으로 유지하여 미래 코드 결함을 방지한다.
|
||||||
|
|
||||||
|
성공 기준:
|
||||||
|
- `dotnet build` 수행 시 경고 0개 달성
|
||||||
|
|
||||||
|
Todo:
|
||||||
|
- [x] `CustomAuthenticationStateProvider.cs` Nullable 경고 수정
|
||||||
|
- [x] `Dashboard.razor` 미사용 변수 제거 및 UI 연계 바인딩 처리
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,15 @@
|
|||||||
</MudButton>
|
</MudButton>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(errorMessage))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Class="mb-4">@errorMessage</MudAlert>
|
||||||
|
}
|
||||||
|
@if (isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-4" />
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Metrics Grid - Pure HTML div instead of MudGrid to ensure proper layout -->
|
<!-- Metrics Grid - Pure HTML div instead of MudGrid to ensure proper layout -->
|
||||||
<div class="admin-metric-grid">
|
<div class="admin-metric-grid">
|
||||||
<div class="admin-metric-card accent-blue cursor-pointer" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/inquiries"))' style="cursor: pointer;">
|
<div class="admin-metric-card accent-blue cursor-pointer" @onclick='(() => Nav.NavigateTo("/taxbaik/admin/inquiries"))' style="cursor: pointer;">
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<!-- 왼쪽: 세무 신고 현황 (Tax Filings) -->
|
<!-- 왼쪽: 세무 신고 현황 (Tax Filings) -->
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<div class="card border-0 shadow-sm rounded-3 mb-4">
|
<div class="card glass-card mb-4">
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h3 class="h5 fw-bold text-dark mb-0">
|
<h3 class="h5 fw-bold text-dark mb-0">
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
|
|
||||||
<!-- 오른쪽: 상담 이력 요약 (Consulting Activities) -->
|
<!-- 오른쪽: 상담 이력 요약 (Consulting Activities) -->
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<div class="card border-0 shadow-sm rounded-3">
|
<div class="card glass-card">
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<h3 class="h5 fw-bold text-dark mb-4">
|
<h3 class="h5 fw-bold text-dark mb-4">
|
||||||
<i class="bi bi-chat-text text-primary me-2"></i> 최근 상담 및 지원 이력
|
<i class="bi bi-chat-text text-primary me-2"></i> 최근 상담 및 지원 이력
|
||||||
@@ -139,14 +139,10 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="timeline">
|
<div class="timeline ps-2">
|
||||||
@foreach (var activity in Model.Consultations)
|
@foreach (var activity in Model.Consultations)
|
||||||
{
|
{
|
||||||
<div class="border-start border-2 border-primary-subtle ps-3 pb-4 position-relative">
|
<div class="timeline-item-modern">
|
||||||
<!-- 타임라인 아이콘 -->
|
|
||||||
<div class="position-absolute start-0 translate-middle-x bg-primary rounded-circle"
|
|
||||||
style="width: 10px; height: 10px; margin-left: -1px; top: 6px;"></div>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||||
<span class="badge bg-primary-subtle text-primary small">@activity.ActivityType</span>
|
<span class="badge bg-primary-subtle text-primary small">@activity.ActivityType</span>
|
||||||
<small class="text-muted">@activity.ActivityDate.ToString("yyyy-MM-dd")</small>
|
<small class="text-muted">@activity.ActivityDate.ToString("yyyy-MM-dd")</small>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
|
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Noto+Sans+KR:wght@400;500;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
|
||||||
<link rel="canonical" href="@(ViewData["CanonicalUrl"] ?? "http://178.104.200.7/taxbaik/")" />
|
<link rel="canonical" href="@(ViewData["CanonicalUrl"] ?? "http://178.104.200.7/taxbaik/")" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var principal = _authService.ValidateToken(accessToken);
|
var principal = _authService.ValidateToken(accessToken ?? string.Empty);
|
||||||
if (principal == null)
|
if (principal == null)
|
||||||
{
|
{
|
||||||
await LogoutAsync();
|
await LogoutAsync();
|
||||||
@@ -115,13 +115,13 @@ public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
|||||||
private bool ShouldRefreshToken()
|
private bool ShouldRefreshToken()
|
||||||
{
|
{
|
||||||
// 토큰이 5분 이내로 만료되면 갱신 (300초 = 5분)
|
// 토큰이 5분 이내로 만료되면 갱신 (300초 = 5분)
|
||||||
if (_tokenStore.TokenExpiryTicks <= 0)
|
if (!_tokenStore.TokenExpiryTicks.HasValue || _tokenStore.TokenExpiryTicks.Value <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const int refreshThresholdSeconds = 300;
|
const int refreshThresholdSeconds = 300;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var expiryTime = new DateTime((long)_tokenStore.TokenExpiryTicks, DateTimeKind.Utc);
|
var expiryTime = new DateTime(_tokenStore.TokenExpiryTicks.Value, DateTimeKind.Utc);
|
||||||
var timeUntilExpiry = expiryTime - DateTime.UtcNow;
|
var timeUntilExpiry = expiryTime - DateTime.UtcNow;
|
||||||
return timeUntilExpiry.TotalSeconds <= refreshThresholdSeconds && timeUntilExpiry.TotalSeconds > 0;
|
return timeUntilExpiry.TotalSeconds <= refreshThresholdSeconds && timeUntilExpiry.TotalSeconds > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -746,3 +746,162 @@ img {
|
|||||||
.faq-answer ul li {
|
.faq-answer ul li {
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== 프리미엄 고도화 & 마이크로 인터랙션 (2026-06-30) ===== */
|
||||||
|
|
||||||
|
/* 영어/숫자용 폰트 클래스 */
|
||||||
|
.font-numeric, .font-heading-en {
|
||||||
|
font-family: 'Outfit', 'Inter', 'Noto Sans KR', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 히어로 섹션 프리미엄 개편 (메쉬 그라데이션 및 CSS 애니메이션) */
|
||||||
|
.hero-section {
|
||||||
|
background: radial-gradient(circle at 10% 20%, rgba(46, 92, 78, 1) 0%, rgba(31, 58, 48, 1) 44%, rgba(13, 30, 26, 1) 100%) !important;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -30%;
|
||||||
|
right: -10%;
|
||||||
|
width: 600px;
|
||||||
|
height: 600px;
|
||||||
|
background: radial-gradient(circle, rgba(200, 157, 110, 0.25) 0%, rgba(200, 157, 110, 0) 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: floatAnimation 8s ease-in-out infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20%;
|
||||||
|
left: -10%;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
background: radial-gradient(circle, rgba(232, 228, 216, 0.15) 0%, rgba(232, 228, 216, 0) 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: floatAnimation2 12s ease-in-out infinite alternate;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes floatAnimation {
|
||||||
|
0% { transform: translateY(0px) scale(1); }
|
||||||
|
50% { transform: translateY(-30px) scale(1.05); }
|
||||||
|
100% { transform: translateY(0px) scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes floatAnimation2 {
|
||||||
|
0% { transform: translateX(0px) rotate(0deg); }
|
||||||
|
50% { transform: translateX(20px) translateY(15px) rotate(10deg); }
|
||||||
|
100% { transform: translateX(0px) rotate(0deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 신뢰도 스트립 카드 리뉴얼 */
|
||||||
|
.trust-strip {
|
||||||
|
background-color: var(--color-bg-alt);
|
||||||
|
padding: 3rem 0;
|
||||||
|
margin-top: -1.5rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
box-shadow: 0 10px 30px rgba(61, 40, 23, 0.05);
|
||||||
|
border: 1px solid rgba(200, 157, 110, 0.15);
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-item:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 15px 35px rgba(61, 40, 23, 0.1);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-icon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: inline-block;
|
||||||
|
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.1));
|
||||||
|
transition: transform var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-item:hover .trust-icon {
|
||||||
|
transform: scale(1.15) rotate(5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 서비스 카드 고도화 */
|
||||||
|
.service-card {
|
||||||
|
border: 1px solid rgba(217, 211, 196, 0.6) !important;
|
||||||
|
box-shadow: 0 10px 25px rgba(61, 40, 23, 0.03) !important;
|
||||||
|
transition: all var(--transition-normal) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover {
|
||||||
|
transform: translateY(-8px) !important;
|
||||||
|
box-shadow: 0 20px 40px rgba(61, 40, 23, 0.1) !important;
|
||||||
|
border-color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card--featured {
|
||||||
|
background: linear-gradient(180deg, #FFFFFF 0%, #FAF8F5 100%) !important;
|
||||||
|
border-left: 4px solid var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 글래스모피즘 포털 클래스 (Glassmorphism Portal Classes) */
|
||||||
|
.glass-card {
|
||||||
|
background: rgba(255, 255, 255, 0.7) !important;
|
||||||
|
backdrop-filter: blur(12px) saturate(180%) !important;
|
||||||
|
-webkit-backdrop-filter: blur(12px) saturate(180%) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4) !important;
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(61, 40, 23, 0.05) !important;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.8) !important;
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(61, 40, 23, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portal-welcome-strip {
|
||||||
|
background: linear-gradient(135deg, var(--color-secondary-dark) 0%, #152A22 100%);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
color: white;
|
||||||
|
padding: 2.5rem;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border-bottom: 3px solid var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 타임라인 컴포넌트 뷰티화 */
|
||||||
|
.timeline-item-modern {
|
||||||
|
border-left: 2px solid rgba(200, 157, 110, 0.4);
|
||||||
|
position: relative;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item-modern::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--color-primary);
|
||||||
|
left: -7px;
|
||||||
|
top: 6px;
|
||||||
|
box-shadow: 0 0 0 4px rgba(200, 157, 110, 0.25);
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item-modern:hover::after {
|
||||||
|
background: var(--color-secondary);
|
||||||
|
box-shadow: 0 0 0 6px rgba(46, 92, 78, 0.3);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user