Groups the repo root into src (buildable source), docs (already existed),
and everything else (db/, scripts/, tests/, deploy/ - deployment/ops/test
assets that aren't compiled, already organized as their own folders). CI
now only needs src/ to build: dotnet restore/build/test/publish all point
at src/TaxBaik.sln, src/TaxBaik.Web/, src/TaxBaik.Proxy/.
- git mv every project (Domain, Infrastructure, Application,
Application.Tests, Web, Web.Client, Proxy) and TaxBaik.sln into src/ as a
unit, so relative ProjectReference/.sln paths stay valid unchanged.
- .gitea/workflows/deploy.yml: 6 dotnet restore/clean/build/test/publish
invocations now point at src/. db/migrations and scripts/ stay at root
(deploy_gb.sh and browser-e2e.yml only touch published output and the
deployed URL, not source paths - verified, no changes needed there).
- scripts/validate_admin_render.sh: admin render-mode file paths now
src/TaxBaik.Web.Client/...
- scripts/validate_kst_timestamps.sh: dropped deploy.sh from its target
list - that script was removed in the prior cleanup commit (dead, no
CI workflow referenced it) but this validator still expected it to exist.
- CLAUDE.md, docs/ENGINEERING_HARNESS.md, docs/ADMIN_PATTERN_CRITIQUE_WBS.md:
updated project-structure diagram, dotnet run/build commands, and grep
targets to the new src/ paths (also fixed a pre-existing stale path in
ADMIN_PATTERN_CRITIQUE_WBS.md that still said TaxBaik.Web/Components/Admin
from before that ever moved to TaxBaik.Web.Client).
- Added a Repo Root harness rule + Architecture Guardrail entries: new files
belong under src/docs/tests/scripts/db/deploy, not loose at root; temp
work stays outside the repo (or under a gitignored .scratch/) and is
never committed.
Verified locally: dotnet build/test src/TaxBaik.sln (26/26 tests), and all
three scripts/validate_*.sh pass against the new layout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AdminLoginForm's submit button had disabled hardcoded as static markup, not
bound to component state. The early inline <script> (before WASM boots)
flipped it via raw DOM mutation, but when the WASM runtime later resumed the
prerendered component, Blazor's own first render re-asserted the static
disabled from the markup - silently undoing the JS fix. The second
bindLoginForm() call from OnAfterRenderAsync then bailed out immediately on
the one-shot "already bound" guard, so nothing ever re-enabled it.
Fix: bind disabled to a real isReady field flipped in OnAfterRenderAsync so
Blazor owns that attribute going forward, and make the JS-side enable
idempotent (runs on every call, not gated behind the bind-once guard) as a
second line of defense.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Admin: replace the global @rendermode on <Routes>/<Router> with per-page
render mode. Login.razor now prerenders (form visible before WASM loads);
every other [Authorize] page stays prerender: false to avoid the
AuthorizeRouteView blank-render regression from earlier attempts. Adds a
"준비 중" -> "로그인" splash tied to WASM boot completion, and lets the
authenticated-shell loading overlay stay up until AdminShell actually renders.
- Contact.cshtml: fix the "Agree" checkbox missing value="true" - a checked
box sent the browser-default "on", which bool model binding can't parse,
so ModelState.IsValid silently went false and OnPostAsync returned a blank
form with no visible error on every submission. Validation summary widened
from ModelOnly to All so this class of failure isn't silent again.
- TelegramInquiryNotificationService: read Telegram:InquiryChatId (falling
back to ChatId) instead of only ChatId, matching the channel routing
CLAUDE.md documents and deploy.yml already provisions as separate secrets.
- Reconcile CLAUDE.md's self-contradicting Phase 8 prerender notes (Phase 9),
rewrite validate_admin_render.sh for the per-page design, and add a
SmartAdmin 5.5 design reference section to DOUZONE_UX_GUIDE.md for future
admin screens (existing screens unchanged, tracked as WBS P4-03).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Page may be already rendered when showLoading() is called (fast nav, cached state)
- Check .admin-page-hero / .admin-login-page immediately and hide if present
- Prevents stuck loading overlay on rapid navigation between pages
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- App.razor: loading overlay starts with `show` class (visible on cold load)
- admin-session.js: add showLoading()/hideLoading(); MutationObserver detects
.admin-page-hero / .admin-login-page instead of mud-element count threshold;
observer restarts on every navigation cycle via LocationChanged
- MainLayout.razor: subscribe to NavigationManager.LocationChanged →
call JS showLoading() on every route change; implements IDisposable
- InquiryList.razor: remove unused IInquiryRepository injection; load data
once (GetPagedAsync(1,200)) and pass IReadOnlyList to all six tab panels
- InquiryTable.razor: accept Inquiries parameter; filter synchronously in
OnParametersSet() — eliminates 6 redundant API calls per page visit
- admin.css: overlay fade-in animation (0.15s); page content fade-in on
route mount via .admin-page-hero / .admin-login-page animation (0.25s)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
**Issue**: Loading indicator remained visible, intercepting all user interactions (pointer-events: auto blocks clicks)
**Root cause**: Multiple detection methods insufficient, race condition between JavaScript execution and Blazor initialization
**Solution**: Add guaranteed 3-second timeout + multiple detection methods
- Method 1: 3000ms timeout (guaranteed)
- Method 2: Detect when 10+ MudBlazor components appear
- Method 3: Hide when readystatechange to 'interactive' or 'complete'
**Failsafe**: Even if Blazor never fires events, loading WILL hide after 3 seconds max
**Result**:
- Loading shows: immediate on page load
- Loading hides: within 1-3 seconds (whichever is first)
- User can interact: guaranteed by 3-second timeout at latest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Issue**: Loading indicator (spinner) continues to display even after page fully loads
**Root cause**:
- Blazor.start() was already called by blazor.web.js (auto-starts)
- Calling it again in JavaScript won't trigger promise resolution
- Promise callback never executed, overlay never hidden
**Solution**: Use multiple detection methods to ensure loading hides:
1. Blazor 'ready' event listener (when circuit is ready)
2. DOMContentLoaded + 500ms timeout (fallback)
3. MutationObserver watching for 20+ MudBlazor components
**Result**:
- Loading spinner shows: page load starts
- Spinner hides: when ANY of the above conditions met (whichever is first)
- No more stuck loading indicator
This ensures loading always hides regardless of how Blazor initializes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Issue**: White screen appears 1-2 seconds during page load/transitions while Blazor circuit connects
**Solution**: Add loading spinner overlay that displays while Blazor initializes
**Changes**:
1. App.razor: Add loading overlay HTML element
2. admin.css: Add loading spinner styles + animations
3. admin-session.js: Show overlay on load, hide when Blazor circuit ready
**UX Flow**:
- Page load starts → Blazor loading spinner appears
- Blazor circuit connects (~1-2s) → Spinner disappears
- Page fully interactive → User sees content
**Styling**:
- Centered spinner with 'Loading...' text
- Semi-transparent background (blur effect)
- Smooth fade-out when complete
- High z-index (9999) to cover all content
This provides clear visual feedback that the app is working, not frozen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 연간 세무 캘린더(7개 시즌) 기반 자동 Hero 섹션 전환
- 시즌 감지 시 D-Day 카운트다운, 긴박감 배지, 시즌 CTA 표시
- 서비스 카드 순서 시즌 관련 항목 우선 정렬
- 어드민 공지사항 CRUD (등록·수정·삭제, 기간·유형 설정)
- 홈페이지 상단 공지 배너 자동 노출 (일반/배너/긴급)
- CLAUDE.md에 세무 캘린더 및 마케팅 방향 하네스 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>