- Add EasyMDE 2.18.0 CDN to App.razor
- Add Marked.js for markdown preview rendering
- Replace MudTextField with EasyMDE editor in BlogCreate.razor
- Replace MudTextField with EasyMDE editor in BlogEdit.razor
- Add JavaScript interop for editor initialization and content sync
- Support markdown syntax highlighting and formatting toolbar
Features:
✅ Bold, italic, strikethrough
✅ Headings (H1-H6)
✅ Code blocks and inline code
✅ Lists (ordered/unordered)
✅ Links and images
✅ Tables
✅ Quotes
✅ Horizontal rules
✅ Real-time preview (side-by-side mode)
✅ Full-screen editing
✅ Markdown guide
The editor syncs content with Blazor form on save.
Markdown syntax is preserved in database and rendered as HTML on blog pages.
Architecture:
- Create companies table with company_code as unique identifier
- Add company_id foreign key to admin_users for multi-tenant support
- Implement backward compatibility with DEFAULT company for existing users
Core Components:
- Company entity with full CRUD operations
- ICompanyRepository interface following Repository pattern
- CompanyRepository with Dapper implementation
- CompanyService with business logic and validation
- CompanyController with REST API endpoints
Admin UI:
- CompanyForm reusable component (Create/Edit pattern)
- CompanyList.razor with pagination and company overview
- CompanyCreate.razor for registering new companies
- CompanyEdit.razor for managing existing companies with delete
- All pages follow admin-page-hero pattern for consistency
SOLID Principles:
- Single Responsibility: Each component has one reason to change
- Open/Closed: Extensible without modifying existing code
- Interface Segregation: Clean repository and service contracts
- Dependency Inversion: All layers depend on abstractions
Database Migration (V014):
- Creates companies table with active/inactive status
- Assigns existing admin users to DEFAULT company
- Provides foundation for role-based access control
Future Enhancement:
- Admin users can belong to specific companies
- Data filtering based on company_id (multi-tenant isolation)
- Company-based permission model
Core Components:
- Create reusable InquiryForm.razor component following SOLID principles
- Implement InquiryCreate.razor for registering new inquiries (offline, phone)
- Implement InquiryEdit.razor for modifying existing inquiries with delete
- Add DeleteAsync method to InquiryRepository and InquiryService
- Update InquiryList with 'Create' button and Edit link in table
Architecture:
- InquiryForm: Encapsulates form logic, can be reused for create/edit
- Service Layer: All operations go through InquiryService for cache invalidation
- Repository Pattern: Database operations isolated in InquiryRepository
- UI Consistency: Both pages follow admin-page-hero pattern
Features:
- Admin can create inquiries from phone/offline consultations
- Admin can modify inquiry details (name, phone, email, message, status, memo)
- Admin can delete inquiries with confirmation dialog
- All operations update dashboard cache
- Status validation and error handling throughout
Testing:
- Updated FakeInquiryRepository in tests to implement DeleteAsync
- Add 'Remember ID' checkbox for improved UX
- Store username in localStorage when checked
- Restore saved username on login page load
- Remove saved username when checkbox unchecked
- Follow security best practice: save username only, not password
- Create BlogEdit.razor for editing existing posts
- Add admin-page-hero section for consistent navigation
- Implement delete functionality with confirmation dialog
- Add GetByIdAsync method to BlogService to support entity retrieval by ID
- Follow SOLID principles: single responsibility for each component
InquiryDetail and ClientDetail pages were missing the admin-page-hero
section, causing the loading overlay to remain stuck on navigation.
The loading indicator (admin-session.js) detects page.admin-page-hero
to know when to hide the overlay.
Now all detail pages show smooth loading indicators on navigation.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Move MudTabs inside MudPaper always visible structure
- Only render MudTabs content (with data) after isLoading becomes false
- Add null/empty check in InquiryTable.OnParametersSet()
- Add error handling in InquiryList data loading
Previously, MudTabs would render before data loaded, causing child
InquiryTable components to mount with empty Inquiries list. After
data loaded, child components weren't re-rendered because Blazor
didn't detect parameter changes in that scenario.
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>
Wrap the page header section in MudContainer to ensure proper MudBlazor component hierarchy and rendering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Issue**: Settings 페이지가 흰 화면으로만 표시됨
**Root cause**: MudGrid 내 MudPaper 요소들의 들여쓰기 누락으로 인한 HTML 구조 손상
- Line 22: MudPaper이 MudItem 없이 렌더링
- Line 50: 동일한 구조 오류
**Fix**: 모든 요소를 올바르게 들여쓰기
- MudPaper > MudForm > MudTextField 계층 정렬
- 모든 자식 요소 2칸 들여쓰기
**Result**: Settings 페이지가 정상 렌더링되고 폼 필드 표시됨
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Enhancement:**
- Wrap login form in HTML <form> element with @onsubmit
- HTML form automatically treats Enter key as submit action
- No need for custom @onkeypress handler
**Behavior:**
- Users can now press Enter in password field to login
- Or click the login button (existing behavior maintained)
- Both methods trigger HandleLogin() async handler
This provides better UX for keyboard-first users.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Issues Fixed:**
1. ✅ Logout not working
- Created Logout.razor page (was missing)
- Properly calls AuthStateProvider.LogoutAsync()
- Redirects to login page with forceLoad: true
- Button click now triggers async logout flow
2. ✅ Accordion state not persisting
- Changed MudNavGroup from fixed Expanded=true/false
- to @bind-Expanded data binding
- Now properly toggles between expanded/collapsed
- State persists across clicks
- Added expandedCustomerGroup, expandedWebsiteGroup properties
3. ✅ Drawer responsiveness
- Already working with @bind-open="@drawerOpen"
- ToggleDrawer() properly toggles state
- Responsive behavior controlled via Breakpoint.Md
**Implementation:**
- Logout.razor: New page for async logout
- Calls AuthStateProvider.LogoutAsync()
- Clears TokenStore + localStorage
- Redirects to /admin/login
- MainLayout.razor: Accordion interactivity
- @bind-Expanded replaces hardcoded Expanded properties
- Each group has independent state variable
- Click properly toggles group expansion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>