refactor: move buildable .NET source into src/, update CI/doc paths
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m7s
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m7s
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>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
@page
|
||||
@{
|
||||
ViewData["Title"] = "소개 | 백원숙 세무회계";
|
||||
}
|
||||
|
||||
<!-- Breadcrumb Navigation -->
|
||||
<nav aria-label="breadcrumb" class="py-3" style="background: #F9F7F3; border-bottom: 1px solid #D9D3C4;">
|
||||
<div class="container">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item"><a href="/taxbaik/" class="text-decoration-none">홈</a></li>
|
||||
<li class="breadcrumb-item active">소개</li>
|
||||
</ol>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-5">
|
||||
<!-- 돌아가기 버튼 -->
|
||||
<div class="mb-4">
|
||||
<a href="/taxbaik/" class="btn btn-sm btn-outline-secondary">← 홈으로 돌아가기</a>
|
||||
</div>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<h1 class="fw-bold mb-4" style="font-size: 2.5rem;">안녕하세요, 백원숙 세무사입니다.</h1>
|
||||
<div class="row g-5">
|
||||
<div class="col-lg-6">
|
||||
<p class="lead">사업자 세무, 부동산 거래, 가족 자산 관리 등 종합적인 세무 컨설팅을 제공합니다.</p>
|
||||
<p>10년 이상의 풍부한 경험과 3개의 국가자격증을 바탕으로, 각 클라이언트의 상황에 맞는 맞춤형 솔루션을 제시합니다.</p>
|
||||
<p class="text-muted">저도 작게 시작하는 사업가였습니다. 처음 사업을 시작할 때의 막막함을 잘 알고 있습니다. 그 경험이 오늘날 고객분들과 소통하는 원동력입니다.</p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">보유 자격증</h5>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">🎓 <strong>세무사</strong></p>
|
||||
<small class="text-muted">2015년 자격취득 · 10년 경력</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">🏠 <strong>부동산중개사</strong></p>
|
||||
<small class="text-muted">부동산 거래 구조 이해 · 실무 전문성</small>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-1">📊 <strong>보험설계사</strong></p>
|
||||
<small class="text-muted">자산관리·상속 대비 전문성</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Expertise Section -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<h2 class="fw-bold mb-4">세 가지 자격의 시너지</h2>
|
||||
<p class="text-muted mb-4">단순히 세금을 계산하는 것이 아니라, 사업 구조와 자산 흐름을 종합적으로 이해합니다.</p>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="p-4 border rounded-3" style="border-color: #D9D3C4;">
|
||||
<div style="font-size: 2rem; margin-bottom: 1rem;">⚖️</div>
|
||||
<h5 class="fw-bold mb-2">공인 세무사</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
세무신고·장부관리·조세 자문 등 세무 업무 전반을 공식 대리합니다. 신고 기한 내 불이익 없는 신고를 기본으로 합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="p-4 border rounded-3" style="border-color: #D9D3C4;">
|
||||
<div style="font-size: 2rem; margin-bottom: 1rem;">🏠</div>
|
||||
<h5 class="fw-bold mb-2">공인 부동산중개사</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
부동산 거래 구조를 이해해 양도·증여·임대 세무상담에 현실감을 더합니다. 계약 전 사전검토로 선택지를 최대화합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="p-4 border rounded-3" style="border-color: #D9D3C4;">
|
||||
<div style="font-size: 2rem; margin-bottom: 1rem;">🛡️</div>
|
||||
<h5 class="fw-bold mb-2">보험설계사 자격</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
상속·증여·대표자 리스크 관점에서 가족 현금흐름과 보험 구조를 함께 설명합니다. 절세와 리스크 관리를 동시에 다룹니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Philosophy Section -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<h2 class="fw-bold mb-4">상담 철학</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2.5rem;">🎯</div>
|
||||
<h5>명확한 설명</h5>
|
||||
<p class="small text-muted">어려운 세법을 쉽게 설명하여 이해를 높입니다. 전문용어로 일방적 설명하지 않습니다.</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2.5rem;">💰</div>
|
||||
<h5>최대 절세</h5>
|
||||
<p class="small text-muted">법적 범위 내에서 세금을 최소화합니다. 초기 세무 전략이 연간 수백만 원의 차이를 만듭니다.</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2.5rem;">🤝</div>
|
||||
<h5>신뢰 파트너</h5>
|
||||
<p class="small text-muted">장기적 파트너로서 성장을 함께 합니다. 일회성 상담이 아닌 지속적 관계를 지향합니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Online Consultation Section -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<h2 class="fw-bold mb-4">전국 비대면 온라인 상담</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h5 class="fw-bold mb-3">왜 온라인인가?</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2"><strong>✓ 시간 절약</strong><br/><small class="text-muted">서울로 올 필요 없이 카카오·이메일로 진행</small></li>
|
||||
<li class="mb-2"><strong>✓ 자료 공유 편의</strong><br/><small class="text-muted">온라인으로 자료 검토 후 맞춤 상담</small></li>
|
||||
<li class="mb-2"><strong>✓ 기록 남음</strong><br/><small class="text-muted">채팅·메일로 모든 내용을 기록 관리</small></li>
|
||||
<li class="mb-2"><strong>✓ 비용 절감</strong><br/><small class="text-muted">방문 비용 없이 효율적 상담 제공</small></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5 class="fw-bold mb-3">상담 방식</h5>
|
||||
<div class="p-3 bg-light rounded-3 mb-3">
|
||||
<p class="fw-bold mb-2">📞 전화 상담</p>
|
||||
<small class="text-muted">즉시 상황 파악 필요 시 · 010-4122-8268</small>
|
||||
</div>
|
||||
<div class="p-3 bg-light rounded-3 mb-3">
|
||||
<p class="fw-bold mb-2">💬 카카오채널</p>
|
||||
<small class="text-muted">당일 응답 · 편한 시간에 문의</small>
|
||||
</div>
|
||||
<div class="p-3 bg-light rounded-3 mb-3">
|
||||
<p class="fw-bold mb-2">✉️ 이메일</p>
|
||||
<small class="text-muted">자료 첨부 상담 · taxbaik5668@gmail.com</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="text-center mb-5 pb-5 border-bottom">
|
||||
<h3 class="fw-bold mb-3">세금 고민, 이제 끝내세요</h3>
|
||||
<p class="text-muted mb-5">무료 상담으로 현재 상황을 진단하고 맞춤형 절세 전략을 받아보세요.</p>
|
||||
<div class="d-flex gap-3 justify-content-center flex-wrap">
|
||||
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 신청하기</a>
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-warning btn-lg">카카오채널로 문의</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 관련 페이지 네비게이션 -->
|
||||
<section class="text-center py-5">
|
||||
<h4 class="fw-bold mb-4">다른 페이지 보기</h4>
|
||||
<div class="row g-3 justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
🏠 홈으로<br/>
|
||||
<small class="text-muted">서비스 및 최신 정보</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/services" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
📊 전문 서비스<br/>
|
||||
<small class="text-muted">사업자·부동산·자산 관리</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/blog" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
📝 세무 정보 블로그<br/>
|
||||
<small class="text-muted">절세팁 및 신고 가이드</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
@page "/announcement"
|
||||
@{
|
||||
Response.Redirect("/taxbaik/#top");
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.Blog.BlogIndexModel
|
||||
@{
|
||||
ViewData["Title"] = "블로그 | 백원숙 세무회계";
|
||||
}
|
||||
|
||||
<div class="container py-5">
|
||||
<h1 class="fw-bold mb-5">세무 블로그</h1>
|
||||
|
||||
<!-- Category Tabs -->
|
||||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||||
<a href="/taxbaik/blog" class="btn btn-sm @(Model.SelectedCategoryId == null ? "btn-primary" : "btn-outline-primary")">전체</a>
|
||||
@foreach (var cat in Model.Categories)
|
||||
{
|
||||
<a href="/taxbaik/blog?categoryId=@cat.Id" class="btn btn-sm @(Model.SelectedCategoryId == cat.Id ? "btn-primary" : "btn-outline-primary")">@cat.Name</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Posts Grid -->
|
||||
<div class="row g-4">
|
||||
@foreach (var post in Model.Posts)
|
||||
{
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<small class="badge bg-primary">@post.CategoryName</small>
|
||||
<h5 class="card-title mt-2">@post.Title</h5>
|
||||
<p class="card-text small">@post.CreatedAt.ToString("yyyy-MM-dd")</p>
|
||||
<a href="/taxbaik/blog/@post.Slug" class="btn btn-sm btn-primary">글 내용 보기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation" class="mt-5">
|
||||
<ul class="pagination justify-content-center">
|
||||
@if (Model.CurrentPage > 1)
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/taxbaik/blog?page=@(Model.CurrentPage - 1)">이전</a>
|
||||
</li>
|
||||
}
|
||||
@for (int i = 1; i <= Model.TotalPages; i++)
|
||||
{
|
||||
<li class="page-item @(i == Model.CurrentPage ? "active" : "")">
|
||||
<a class="page-link" href="/taxbaik/blog?page=@i">@i</a>
|
||||
</li>
|
||||
}
|
||||
@if (Model.CurrentPage < Model.TotalPages)
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="/taxbaik/blog?page=@(Model.CurrentPage + 1)">다음</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Blog;
|
||||
|
||||
public class BlogIndexModel : PageModel
|
||||
{
|
||||
private readonly BlogService _blogService;
|
||||
private readonly CategoryService _categoryService;
|
||||
|
||||
public List<BlogPost> Posts { get; set; } = [];
|
||||
public List<Category> Categories { get; set; } = [];
|
||||
public int CurrentPage { get; set; } = 1;
|
||||
public int TotalPages { get; set; }
|
||||
public int? SelectedCategoryId { get; set; }
|
||||
private const int PageSize = 12;
|
||||
|
||||
public BlogIndexModel(BlogService blogService, CategoryService categoryService)
|
||||
{
|
||||
_blogService = blogService;
|
||||
_categoryService = categoryService;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync(int page = 1, int? categoryId = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentPage = page;
|
||||
SelectedCategoryId = categoryId;
|
||||
|
||||
Categories = (await _categoryService.GetAllAsync()).ToList();
|
||||
|
||||
var (posts, total) = await _blogService.GetPublishedPagedAsync(page, PageSize, categoryId);
|
||||
Posts = posts.ToList();
|
||||
TotalPages = (total + PageSize - 1) / PageSize;
|
||||
}
|
||||
catch
|
||||
{
|
||||
CurrentPage = page;
|
||||
SelectedCategoryId = categoryId;
|
||||
Categories = [];
|
||||
Posts = [];
|
||||
TotalPages = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
@page "/blog/{slug}"
|
||||
@model TaxBaik.Web.Pages.Blog.BlogPostModel
|
||||
@{
|
||||
ViewData["Title"] = Model.Post?.SeoTitle ?? Model.Post?.Title;
|
||||
ViewData["Description"] = Model.Post?.SeoDescription ?? "";
|
||||
ViewData["OgImage"] = Model.Post?.ThumbnailUrl ?? "";
|
||||
var canonicalUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}/blog/{Model.Post?.Slug}";
|
||||
ViewData["CanonicalUrl"] = canonicalUrl;
|
||||
ViewData["OgUrl"] = canonicalUrl;
|
||||
}
|
||||
|
||||
@if (Model.Post != null)
|
||||
{
|
||||
<article class="container section" style="max-width: 720px;">
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/taxbaik">홈</a></li>
|
||||
<li class="breadcrumb-item"><a href="/taxbaik/blog">블로그</a></li>
|
||||
<li class="breadcrumb-item active">@Model.Post.CategoryName</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="mb-4">
|
||||
<a href="/taxbaik/blog" class="btn btn-outline-primary btn-sm">← 블로그 목록으로 돌아가기</a>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Post.ThumbnailUrl))
|
||||
{
|
||||
<img src="@Model.Post.ThumbnailUrl" alt="@Model.Post.Title"
|
||||
loading="lazy" class="img-fluid rounded mb-4" style="max-height: 400px; object-fit: cover; width: 100%;" />
|
||||
}
|
||||
|
||||
<h1 class="fw-bold mb-3">@Model.Post.Title</h1>
|
||||
<div class="text-muted small mb-4 d-flex flex-wrap gap-3">
|
||||
<span>📅 @Model.Post.CreatedAt.ToString("yyyy년 MM월 dd일")</span>
|
||||
<span>👁️ @Model.Post.ViewCount 조회</span>
|
||||
<span class="badge bg-primary">@Model.Post.CategoryName</span>
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<div class="article-body lh-lg markdown-body">
|
||||
@Html.Raw(Model.HtmlContent)
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<!-- CTA -->
|
||||
<section class="bg-primary-light p-4 rounded mb-5">
|
||||
<h5 class="fw-bold mb-2 text-primary">상담이 필요하신가요?</h5>
|
||||
<p class="mb-3 text-muted">이 글과 관련된 상담이 필요하면 언제든 연락주세요.</p>
|
||||
<a href="/taxbaik/contact" class="btn btn-primary">📞 상담 신청하기</a>
|
||||
</section>
|
||||
|
||||
<!-- Share -->
|
||||
<section class="text-center mb-5">
|
||||
<p class="small text-muted mb-3">이 글을 공유하세요:</p>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="copyUrl()" title="링크 복사">📋 링크복사</button>
|
||||
</section>
|
||||
</article>
|
||||
}
|
||||
else
|
||||
{
|
||||
<section class="container section text-center">
|
||||
<p class="fs-5">포스트를 찾을 수 없습니다.</p>
|
||||
<a href="/taxbaik/blog" class="btn btn-primary">블로그 돌아가기</a>
|
||||
</section>
|
||||
}
|
||||
|
||||
<script>
|
||||
function copyUrl() {
|
||||
navigator.clipboard.writeText(window.location.href).then(() => {
|
||||
const btn = event.target;
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = '✅ 복사됨';
|
||||
setTimeout(() => btn.textContent = originalText, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using System.Net;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Blog;
|
||||
|
||||
public class BlogPostModel : PageModel
|
||||
{
|
||||
private readonly BlogService _blogService;
|
||||
|
||||
public BlogPost? Post { get; set; }
|
||||
public string? HtmlContent { get; set; }
|
||||
|
||||
public BlogPostModel(BlogService blogService)
|
||||
{
|
||||
_blogService = blogService;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync(string slug)
|
||||
{
|
||||
Post = await _blogService.GetBySlugAsync(slug);
|
||||
if (Post != null)
|
||||
{
|
||||
HtmlContent = WebUtility.HtmlEncode(Post.Content ?? "").Replace("\r\n", "<br />").Replace("\n", "<br />");
|
||||
_ = _blogService.IncrementViewCountAsync(Post.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.ContactModel
|
||||
@{
|
||||
ViewData["Title"] = "상담 신청 | 백원숙 세무회계";
|
||||
}
|
||||
|
||||
<div class="container py-5" style="max-width: 600px;">
|
||||
<div class="d-flex align-items-center justify-content-between gap-3 mb-4">
|
||||
<h1 class="fw-bold mb-0">상담 신청</h1>
|
||||
<a href="/taxbaik" class="btn btn-outline-secondary btn-sm"
|
||||
onclick="if (history.length > 1) { history.back(); return false; }">
|
||||
뒤로가기
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div id="contact-success" class="alert alert-success alert-dismissible fade show" role="alert" role="status" style="font-size: 1.05rem;">
|
||||
<strong>✅ 성공!</strong> @TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<script>
|
||||
// 성공 메시지를 3초 후 자동 숨김 (사용자 클릭 가능)
|
||||
setTimeout(() => {
|
||||
const alert = document.getElementById('contact-success');
|
||||
if (alert) {
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
}
|
||||
}, 5000);
|
||||
// 폼 자동 초기화
|
||||
setTimeout(() => {
|
||||
document.querySelector('form').reset();
|
||||
document.getElementById('agree').checked = false;
|
||||
}, 1000);
|
||||
</script>
|
||||
}
|
||||
|
||||
<form method="post" id="contactForm">
|
||||
@Html.AntiForgeryToken()
|
||||
<div asp-validation-summary="All" class="text-danger mb-3"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">이름 <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="name" name="Name" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">전화번호 <span class="text-danger">*</span></label>
|
||||
<input type="tel" class="form-control" id="phone" name="Phone" placeholder="010-0000-0000" required />
|
||||
<small class="form-text text-muted">형식: 010-0000-0000</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">이메일</label>
|
||||
<input type="email" class="form-control" id="email" name="Email" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="service" class="form-label">상담분야</label>
|
||||
<select class="form-select" id="service" name="ServiceType">
|
||||
<option value="기장">사업자 기장</option>
|
||||
<option value="양도세">부동산 양도세</option>
|
||||
<option value="종소세">종합소득세</option>
|
||||
<option value="증여상속">증여상속세</option>
|
||||
<option value="기타">기타</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">문의내용 <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="message" name="Message" rows="5" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="agree" name="Agree" value="true" required />
|
||||
<label class="form-check-label" for="agree">
|
||||
개인정보 수집·이용에 동의합니다
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100">상담신청</button>
|
||||
</form>
|
||||
|
||||
<hr class="my-5" />
|
||||
|
||||
<h5 class="fw-bold mb-3">빠른 상담을 원하시나요?</h5>
|
||||
<p>카카오톡 채널을 통해 더 빠르게 상담받을 수 있습니다.</p>
|
||||
<div class="gap-2 d-flex flex-wrap">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-warning">카카오톡 채널 문의</a>
|
||||
<a href="tel:010-4122-8268" class="btn btn-outline-primary">전화 상담</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
public class ContactModel : PageModel
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
[BindProperty]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string Phone { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string? Email { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public string ServiceType { get; set; } = "기타";
|
||||
|
||||
[BindProperty]
|
||||
public string Message { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public bool Agree { get; set; }
|
||||
|
||||
public ContactModel(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (!ModelState.IsValid || !Agree)
|
||||
return Page();
|
||||
|
||||
try
|
||||
{
|
||||
await _inquiryService.SubmitAsync(
|
||||
Name,
|
||||
Phone,
|
||||
ServiceType,
|
||||
Message,
|
||||
Email,
|
||||
HttpContext.Connection.RemoteIpAddress?.ToString());
|
||||
TempData["Success"] = "상담 신청이 접수되었습니다. 빠른 시간 내에 연락드리겠습니다.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
ModelState.AddModelError("", ex.Message);
|
||||
return Page();
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError("", "시스템 오류가 발생했습니다. 잠시 후 다시 시도해주세요.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@page "/faq"
|
||||
@{
|
||||
Response.Redirect("/taxbaik/#faq");
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.IndexModel
|
||||
@{
|
||||
var season = Model.CurrentSeason;
|
||||
ViewData["Title"] = season != null
|
||||
? $"백원숙 세무회계 | {season.Name} — 지금 상담하세요"
|
||||
: "백원숙 세무회계 | 사업자·부동산·증여상속 세무 상담";
|
||||
ViewData["Description"] = "사업자 기장, 부동산 양도세·증여상속, 종합소득세 전문 상담. 온라인 맞춤 상담 제공.";
|
||||
}
|
||||
|
||||
@* ─── 공지사항 배너 (관리자 등록 공지) ─── *@
|
||||
@if (Model.ActiveAnnouncements.Count > 0)
|
||||
{
|
||||
foreach (var notice in Model.ActiveAnnouncements)
|
||||
{
|
||||
<div class="announcement-bar announcement-bar--@notice.DisplayType">
|
||||
<div class="container d-flex align-items-center gap-2 py-2">
|
||||
<span class="announcement-icon">
|
||||
@if (notice.DisplayType == "urgent") { <text>🚨</text> }
|
||||
else if (notice.DisplayType == "banner") { <text>📢</text> }
|
||||
else { <text>ℹ️</text> }
|
||||
</span>
|
||||
<span class="announcement-text fw-semibold">@notice.Title</span>
|
||||
@if (!string.IsNullOrEmpty(notice.Content))
|
||||
{
|
||||
<span class="d-none d-md-inline text-muted small ms-2">— @notice.Content</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@* ─── Hero Section ─── *@
|
||||
@if (season != null)
|
||||
{
|
||||
<section class="hero-section hero-section--seasonal text-white pt-5 pb-4">
|
||||
<div class="container">
|
||||
<div class="row align-items-center py-4">
|
||||
<div class="col-lg-7">
|
||||
<span class="badge bg-danger-badge mb-3 fs-6 px-3 py-2">
|
||||
@season.UrgencyBadge
|
||||
</span>
|
||||
<h1 class="mb-3" style="white-space: pre-line;">@season.HeroHeadline</h1>
|
||||
<p class="fs-5 mb-4" style="line-height: 1.8; opacity: 0.95;">
|
||||
@season.HeroSubtext
|
||||
</p>
|
||||
<div class="d-flex gap-3 flex-wrap">
|
||||
<a href="/taxbaik/contact" class="btn btn-warning btn-lg fw-bold">
|
||||
⏰ @season.CtaText
|
||||
</a>
|
||||
<a href="javascript:void(0);" class="btn btn-outline-primary btn-lg"
|
||||
onclick="openKakao()" style="border-color: white; color: white;">
|
||||
💬 카카오채널 문의
|
||||
</a>
|
||||
</div>
|
||||
@if (season.DaysUntilDeadline <= 7)
|
||||
{
|
||||
<p class="mt-3 small" style="opacity: 0.8;">
|
||||
마감까지 <strong>@(season.DaysUntilDeadline)일</strong> 남았습니다.
|
||||
지금 바로 상담 신청하세요.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
<div class="col-lg-5 d-none d-lg-block text-center">
|
||||
<div class="seasonal-deadline-badge">
|
||||
<div class="deadline-label">마감</div>
|
||||
<div class="deadline-date">@season.Deadline.ToString("M월 d일")</div>
|
||||
<div class="deadline-days">D-@season.DaysUntilDeadline</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
else
|
||||
{
|
||||
<section class="hero-section text-white pt-5 pb-4">
|
||||
<div class="container">
|
||||
<div class="row align-items-center py-4">
|
||||
<div class="col-lg-7">
|
||||
<span class="badge bg-primary-badge mb-3">경험 있는 세무사의 맞춤 전략</span>
|
||||
<h1 class="mb-3">
|
||||
세금과 자산<br/>
|
||||
<span style="color: #E8E4D8;">한 번에 해결하는</span>
|
||||
</h1>
|
||||
<p class="fs-5 mb-4" style="line-height: 1.8; opacity: 0.95;">
|
||||
사업자 세무, 부동산 거래, 가족자산 관리를 위한<br/>
|
||||
통합 솔루션을 제공합니다.
|
||||
</p>
|
||||
<div class="d-flex gap-3 flex-wrap">
|
||||
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">무료 상담 신청</a>
|
||||
<a href="javascript:void(0);" class="btn btn-outline-primary btn-lg"
|
||||
onclick="openKakao()" style="border-color: white; color: white;">
|
||||
💬 카카오채널 문의
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 d-none d-lg-block text-center">
|
||||
<div style="font-size: 120px; opacity: 0.15;">📋</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
<!-- About 링크 배너 -->
|
||||
<section class="py-3" style="background: rgba(46, 92, 78, 0.05); border-bottom: 1px solid rgba(46, 92, 78, 0.1);">
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center gap-3 flex-wrap">
|
||||
<div>
|
||||
<p class="mb-0 small text-muted">세무사의 경력, 자격, 상담 철학을 알아보세요</p>
|
||||
</div>
|
||||
<a href="/taxbaik/about" class="btn btn-sm btn-outline-primary">백원숙 세무사 소개 →</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 서비스 영역 -->
|
||||
<section class="py-5">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="section-title">전문 서비스</h2>
|
||||
<p class="fs-6 text-muted" style="max-width: 600px; margin: 0 auto;">
|
||||
각 분야의 복잡한 세무 이슈를 경험과 노하우로 해결합니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@{
|
||||
var focusService = season?.FocusService ?? "";
|
||||
// 시즌에 따라 서비스 카드 순서 결정
|
||||
var cardOrder = focusService switch
|
||||
{
|
||||
"real-estate-tax" => new[] { "real-estate-tax", "business-tax", "family-asset" },
|
||||
"family-asset" => new[] { "family-asset", "business-tax", "real-estate-tax" },
|
||||
_ => new[] { "business-tax", "real-estate-tax", "family-asset" }
|
||||
};
|
||||
}
|
||||
|
||||
<div class="row g-4">
|
||||
@foreach (var cardKey in cardOrder)
|
||||
{
|
||||
var isFeatured = cardKey == focusService;
|
||||
if (cardKey == "business-tax")
|
||||
{
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card service-card h-100 @(isFeatured ? "service-card--featured" : "")">
|
||||
@if (isFeatured) { <div class="service-card-badge">현재 시즌</div> }
|
||||
<div class="service-icon">📊</div>
|
||||
<div class="card-body pt-0">
|
||||
<h3 class="card-title">사업자 세무</h3>
|
||||
<p class="text-muted small">월 기장부터 종합소득세, 신규 사업자 세무까지 — 사업 초기부터 체계적인 세무 관리.</p>
|
||||
<a href="/taxbaik/services#business-tax" class="btn btn-sm btn-outline-primary mt-3">자세히 보기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (cardKey == "real-estate-tax")
|
||||
{
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card service-card h-100 @(isFeatured ? "service-card--featured" : "")">
|
||||
@if (isFeatured) { <div class="service-card-badge">현재 시즌</div> }
|
||||
<div class="service-icon">🏠</div>
|
||||
<div class="card-body pt-0">
|
||||
<h3 class="card-title">부동산 세금</h3>
|
||||
<p class="text-muted small">양도세·취득세·임대소득세 — 부동산 거래 시 세금 부담을 줄이는 전략.</p>
|
||||
<a href="/taxbaik/services#real-estate-tax" class="btn btn-sm btn-outline-primary mt-3">자세히 보기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card service-card h-100 @(isFeatured ? "service-card--featured" : "")">
|
||||
@if (isFeatured) { <div class="service-card-badge">현재 시즌</div> }
|
||||
<div class="service-icon">👨👩👧👦</div>
|
||||
<div class="card-body pt-0">
|
||||
<h3 class="card-title">가족자산 관리</h3>
|
||||
<p class="text-muted small">증여상속 사전 계획부터 대표자 리스크 관리까지 - 가족 자산을 지키는 전략.</p>
|
||||
<a href="/taxbaik/services#family-asset" class="btn btn-sm btn-outline-primary mt-3">자세히 보기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 블로그 & 시즌 포스트 (상단으로 올림) -->
|
||||
<section class="py-5">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
@if (season != null)
|
||||
{
|
||||
<div class="seasonal-blog-header mb-2">
|
||||
<span class="seasonal-blog-tag">📅 @season.Name 시즌</span>
|
||||
</div>
|
||||
<h2 class="section-title">이번 시즌 세무 정보</h2>
|
||||
<p class="text-muted">@season.Name 관련 절세 팁과 신고 가이드를 확인하세요</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h2 class="section-title">세무 정보 & 절세 팁</h2>
|
||||
<p class="text-muted">최신 세법 변화와 실무 팁을 공유합니다</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
@{
|
||||
var hasSeasonalPosts = Model.SeasonalPosts?.Count > 0;
|
||||
var hasRecentPosts = Model.RecentPosts?.Count > 0;
|
||||
}
|
||||
|
||||
@if (hasSeasonalPosts || hasRecentPosts)
|
||||
{
|
||||
<div class="row g-4">
|
||||
@* 시즌 관련 글 (배지 강조) *@
|
||||
@if (hasSeasonalPosts)
|
||||
{
|
||||
@foreach (var post in Model.SeasonalPosts!)
|
||||
{
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card blog-card h-100 blog-card--seasonal">
|
||||
<div class="blog-seasonal-ribbon">이번 시즌 추천</div>
|
||||
<div class="blog-placeholder">🗓️</div>
|
||||
<div class="card-body">
|
||||
<small class="badge bg-season-badge">@post.CategoryName</small>
|
||||
<h4 class="card-title mt-3">@post.Title</h4>
|
||||
<p class="text-muted small">@((post.PublishedAt ?? post.CreatedAt).ToString("yyyy년 MM월 dd일"))</p>
|
||||
<a href="/taxbaik/blog/@post.Slug" class="btn btn-sm btn-seasonal">읽기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@* 최신 글 (나머지 채우기) *@
|
||||
@if (hasRecentPosts)
|
||||
{
|
||||
@foreach (var post in Model.RecentPosts!)
|
||||
{
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card blog-card h-100">
|
||||
<div class="blog-placeholder">📝</div>
|
||||
<div class="card-body">
|
||||
<small class="badge bg-primary-badge">@post.CategoryName</small>
|
||||
<h4 class="card-title mt-3">@post.Title</h4>
|
||||
<p class="text-muted small">@((post.PublishedAt ?? post.CreatedAt).ToString("yyyy년 MM월 dd일"))</p>
|
||||
<a href="/taxbaik/blog/@post.Slug" class="btn btn-sm btn-primary">읽기</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5 d-flex justify-content-center gap-3 flex-wrap">
|
||||
@if (season != null && !string.IsNullOrEmpty(season.RelatedCategorySlug))
|
||||
{
|
||||
<a href="/taxbaik/blog?category=@season.RelatedCategorySlug" class="btn btn-outline-secondary btn-lg">
|
||||
📅 @season.Name 전체 글 보기
|
||||
</a>
|
||||
}
|
||||
<a href="/taxbaik/blog" class="btn btn-outline-primary btn-lg">전체 블로그 보기</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-center py-5">
|
||||
<div class="mb-3" style="font-size: 3rem;">📝</div>
|
||||
<h3 class="h5 mb-2">현재 표시할 블로그 글이 없습니다.</h3>
|
||||
<p class="text-muted mb-4">최신 세무 정보는 블로그에서 확인할 수 있습니다.</p>
|
||||
<a href="/taxbaik/blog" class="btn btn-outline-primary btn-lg">블로그 바로가기</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 상담 프로세스 -->
|
||||
<section class="py-5" style="background: #F9F7F3;">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="section-title">상담 과정</h2>
|
||||
</div>
|
||||
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-3 text-center mb-4 mb-md-0">
|
||||
<div style="width: 80px; height: 80px; margin: 0 auto 1rem; background: linear-gradient(135deg, #C89D6E 0%, #A67C52 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 32px;">
|
||||
📞
|
||||
</div>
|
||||
<h4>1단계: 무료 상담</h4>
|
||||
<p class="text-muted small">상황 파악 및<br/>현재 문제점 확인</p>
|
||||
</div>
|
||||
<div class="col-md-3 text-center mb-4 mb-md-0">
|
||||
<div style="width: 80px; height: 80px; margin: 0 auto 1rem; background: linear-gradient(135deg, #C89D6E 0%, #A67C52 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 32px;">
|
||||
📋
|
||||
</div>
|
||||
<h4>2단계: 세무진단</h4>
|
||||
<p class="text-muted small">자료 분석 및<br/>최적 방안 도출</p>
|
||||
</div>
|
||||
<div class="col-md-3 text-center mb-4 mb-md-0">
|
||||
<div style="width: 80px; height: 80px; margin: 0 auto 1rem; background: linear-gradient(135deg, #C89D6E 0%, #A67C52 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 32px;">
|
||||
💡
|
||||
</div>
|
||||
<h4>3단계: 맞춤제안</h4>
|
||||
<p class="text-muted small">절세 전략 및<br/>실행 계획 제시</p>
|
||||
</div>
|
||||
<div class="col-md-3 text-center">
|
||||
<div style="width: 80px; height: 80px; margin: 0 auto 1rem; background: linear-gradient(135deg, #C89D6E 0%, #A67C52 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 32px;">
|
||||
✅
|
||||
</div>
|
||||
<h4>4단계: 실행지원</h4>
|
||||
<p class="text-muted small">지속적 관리 및<br/>추가 상담 제공</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<p class="text-muted mb-3">상담은 온라인 또는 오프라인으로 진행되며, 완전히 비밀이 보장됩니다.</p>
|
||||
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 신청하기</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 자주 묻는 질문 (DB 연동) -->
|
||||
@if (Model.ActiveFaqs.Count > 0)
|
||||
{
|
||||
<section class="py-5" style="background: #F9F7F3;">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="section-title">자주 묻는 질문</h2>
|
||||
<p class="text-muted">상담 전 궁금하신 사항을 먼저 확인해 보세요</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-accordion">
|
||||
@foreach (var faqItem in Model.ActiveFaqs)
|
||||
{
|
||||
<details class="faq-item">
|
||||
<summary class="faq-question">@faqItem.Question</summary>
|
||||
<div class="faq-answer">
|
||||
@faqItem.Answer
|
||||
</div>
|
||||
</details>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<p class="text-muted mb-3">더 궁금한 점이 있으시면 바로 문의해 주세요</p>
|
||||
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 문의하기</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
<!-- 최종 CTA -->
|
||||
<section class="py-5" style="background: linear-gradient(135deg, #2E5C4E 0%, #1F3A30 100%); color: white;">
|
||||
<div class="container text-center">
|
||||
@if (season != null)
|
||||
{
|
||||
<h2 class="mb-3 fw-bold" style="font-size: 2.5rem;">@season.Name 마감이 다가옵니다!</h2>
|
||||
<p class="fs-5 mb-5" style="opacity: 0.95; max-width: 500px; margin-left: auto; margin-right: auto;">
|
||||
마감 <strong>D-@(season.DaysUntilDeadline)일</strong> — 지금 바로 상담을 신청하세요.<br/>
|
||||
빠른 검토로 불이익 없이 신고를 완료합니다.
|
||||
</p>
|
||||
<div class="d-flex gap-3 justify-content-center flex-wrap">
|
||||
<a href="/taxbaik/contact" class="btn btn-warning btn-lg">⏰ @season.CtaText</a>
|
||||
<a href="https://pf.kakao.com/_xoxchTX" target="_blank" rel="noopener noreferrer" class="btn btn-light btn-lg">카카오채널로 문의</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h2 class="mb-3 fw-bold" style="font-size: 2.5rem;">세금 고민은 이제 끝!</h2>
|
||||
<p class="fs-5 mb-5" style="opacity: 0.95; max-width: 500px; margin-left: auto; margin-right: auto;">
|
||||
무료 상담으로 현재 상황을 진단하고<br/>
|
||||
맞춤형 절세 전략을 받아보세요.
|
||||
</p>
|
||||
<div class="d-flex gap-3 justify-content-center flex-wrap">
|
||||
<a href="/taxbaik/contact" class="btn btn-warning btn-lg">상담 신청하기</a>
|
||||
<a href="https://pf.kakao.com/_xoxchTX" target="_blank" rel="noopener noreferrer" class="btn btn-light btn-lg">카카오채널로 문의</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,76 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Seasonal;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly BlogService _blogService;
|
||||
private readonly SeasonalMarketingService _seasonalMarketingService;
|
||||
private readonly AnnouncementService _announcementService;
|
||||
private readonly FaqService _faqService;
|
||||
|
||||
public List<BlogPost> RecentPosts { get; set; } = [];
|
||||
public List<BlogPost> SeasonalPosts { get; set; } = [];
|
||||
public CurrentSeasonDto? CurrentSeason { get; set; }
|
||||
public List<Announcement> ActiveAnnouncements { get; set; } = [];
|
||||
public List<Faq> ActiveFaqs { get; set; } = [];
|
||||
|
||||
public IndexModel(
|
||||
BlogService blogService,
|
||||
SeasonalMarketingService seasonalMarketingService,
|
||||
AnnouncementService announcementService,
|
||||
FaqService faqService)
|
||||
{
|
||||
_blogService = blogService;
|
||||
_seasonalMarketingService = seasonalMarketingService;
|
||||
_announcementService = announcementService;
|
||||
_faqService = faqService;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
CurrentSeason = _seasonalMarketingService.GetCurrentSeason();
|
||||
|
||||
var announcementsTask = LoadSafeAsync(() => _announcementService.GetActiveAsync());
|
||||
var faqsTask = LoadSafeAsync(() => _faqService.GetActiveAsync());
|
||||
var blogTask = LoadBlogAsync();
|
||||
|
||||
await Task.WhenAll(announcementsTask, faqsTask, blogTask);
|
||||
|
||||
ActiveAnnouncements = (await announcementsTask)?.ToList() ?? [];
|
||||
ActiveFaqs = (await faqsTask)?.ToList() ?? [];
|
||||
}
|
||||
|
||||
private async Task LoadBlogAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentSeason is not null && !string.IsNullOrEmpty(CurrentSeason.RelatedCategorySlug))
|
||||
{
|
||||
var (seasonal, latest) = await _blogService.GetSeasonalPostsAsync(
|
||||
CurrentSeason.RelatedCategorySlug, seasonalCount: 2, totalCount: 3);
|
||||
SeasonalPosts = seasonal.ToList();
|
||||
RecentPosts = latest.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 3);
|
||||
RecentPosts = posts.ToList();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
RecentPosts = [];
|
||||
SeasonalPosts = [];
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<T>?> LoadSafeAsync<T>(Func<Task<IEnumerable<T>>> loader)
|
||||
{
|
||||
try { return await loader(); }
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@page "/inquiry"
|
||||
@{
|
||||
Response.Redirect("/taxbaik/contact");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@page "/portal/external-callback"
|
||||
@model TaxBaik.Web.Pages.Portal.ExternalCallbackModel
|
||||
@{
|
||||
ViewData["Title"] = "포털 인증 처리";
|
||||
}
|
||||
|
||||
<section class="container py-5">
|
||||
<div class="alert alert-info">인증을 처리하는 중입니다...</div>
|
||||
</section>
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Portal;
|
||||
|
||||
public class ExternalCallbackModel : PageModel
|
||||
{
|
||||
private readonly PortalUserService _portalUserService;
|
||||
private readonly ClientService _clientService;
|
||||
|
||||
public ExternalCallbackModel(PortalUserService portalUserService, ClientService clientService)
|
||||
{
|
||||
_portalUserService = portalUserService;
|
||||
_clientService = clientService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(string provider)
|
||||
{
|
||||
var external = await HttpContext.AuthenticateAsync(PortalOAuthDefaults.ExternalScheme);
|
||||
if (external?.Principal is null)
|
||||
return RedirectToPage("/Portal/Login");
|
||||
|
||||
var email = external.Principal.FindFirstValue(ClaimTypes.Email);
|
||||
var name = external.Principal.FindFirstValue(ClaimTypes.Name) ?? "고객";
|
||||
var providerId = external.Principal.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
return RedirectToPage("/Portal/Login");
|
||||
|
||||
var existing = await _portalUserService.GetByProviderAsync(provider, providerId);
|
||||
if (existing is null && !string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
existing = await _portalUserService.GetByEmailAsync(email);
|
||||
if (existing is null)
|
||||
{
|
||||
int? clientId = null;
|
||||
var linkedClient = await _clientService.GetByEmailAsync(email);
|
||||
if (linkedClient is null && !string.IsNullOrWhiteSpace(external.Principal.FindFirstValue("phone")))
|
||||
linkedClient = await _clientService.GetByPhoneAsync(external.Principal.FindFirstValue("phone")!);
|
||||
if (linkedClient is not null)
|
||||
clientId = linkedClient.Id;
|
||||
|
||||
await _portalUserService.RegisterOAuthAsync(
|
||||
name,
|
||||
email,
|
||||
external.Principal.FindFirstValue("phone") ?? "",
|
||||
provider,
|
||||
providerId,
|
||||
clientId);
|
||||
existing = await _portalUserService.GetByEmailAsync(email);
|
||||
}
|
||||
else if (!string.Equals(existing.Provider, provider, StringComparison.OrdinalIgnoreCase) ||
|
||||
!string.Equals(existing.ProviderId, providerId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await _portalUserService.LinkOAuthAsync(existing, provider, providerId, name, email);
|
||||
}
|
||||
}
|
||||
|
||||
if (existing is not null && !existing.ClientId.HasValue && !string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
var linkedClient = await _clientService.GetByEmailAsync(email);
|
||||
if (linkedClient is null && !string.IsNullOrWhiteSpace(external.Principal.FindFirstValue("phone")))
|
||||
linkedClient = await _clientService.GetByPhoneAsync(external.Principal.FindFirstValue("phone")!);
|
||||
if (linkedClient is not null)
|
||||
{
|
||||
await _portalUserService.AttachClientAsync(existing, linkedClient.Id);
|
||||
existing.ClientId = linkedClient.Id;
|
||||
}
|
||||
}
|
||||
|
||||
if (existing is null)
|
||||
return RedirectToPage("/Portal/Login");
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(ClaimTypes.NameIdentifier, existing.Id.ToString()),
|
||||
new(ClaimTypes.Name, existing.Name),
|
||||
new(ClaimTypes.Email, existing.Email),
|
||||
new("portal_user_id", existing.Id.ToString())
|
||||
};
|
||||
|
||||
if (existing.ClientId.HasValue)
|
||||
claims.Add(new("client_id", existing.ClientId.Value.ToString()));
|
||||
|
||||
await HttpContext.SignInAsync(
|
||||
PortalAuthDefaults.Scheme,
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, PortalAuthDefaults.Scheme)),
|
||||
new AuthenticationProperties { IsPersistent = true });
|
||||
|
||||
await HttpContext.SignOutAsync(PortalOAuthDefaults.ExternalScheme);
|
||||
return RedirectToPage("/Portal/Index");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
@page "/portal"
|
||||
@model TaxBaik.Web.Pages.Portal.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "마이 포털 - 세무사 백원숙";
|
||||
ViewData["Description"] = "고객님의 세무 신고 일정과 상담 이력을 실시간으로 확인하실 수 있는 마이페이지입니다.";
|
||||
}
|
||||
|
||||
<div class="bg-light py-5">
|
||||
<div class="container">
|
||||
<!-- 상단 헤더 & 환영 문구 -->
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-5 pb-4 border-bottom">
|
||||
<div>
|
||||
<p class="text-primary fw-bold mb-1">TaxBaik My Portal</p>
|
||||
<h1 class="display-6 fw-bold text-dark">안녕하세요, @(User.Identity?.Name)님!</h1>
|
||||
@if (Model.ClientInfo != null)
|
||||
{
|
||||
<p class="text-muted mb-0">
|
||||
<i class="bi bi-building"></i> @(string.IsNullOrEmpty(Model.ClientInfo.CompanyName) ? "개인 고객" : Model.ClientInfo.CompanyName)
|
||||
| <i class="bi bi-telephone"></i> @Model.ClientInfo.Phone
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
<div class="mt-3 mt-sm-0">
|
||||
<form method="post" action="/taxbaik/portal/logout" class="d-inline">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm">
|
||||
<i class="bi bi-box-arrow-right"></i> 로그아웃
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.ClientInfo == null)
|
||||
{
|
||||
<!-- 연동 대기 경고 -->
|
||||
<div class="card border-warning shadow-sm mb-5">
|
||||
<div class="card-body p-5 text-center">
|
||||
<div class="mb-4">
|
||||
<span class="display-1 text-warning"><i class="bi bi-exclamation-triangle-fill"></i></span>
|
||||
</div>
|
||||
<h3 class="fw-bold text-dark mb-3">고객 정보 연동 대기 중</h3>
|
||||
<p class="text-muted max-width-md mx-auto mb-4">
|
||||
가입하신 계정 정보(이메일/연락처)와 일치하는 세무 대리 고객 레코드를 찾지 못했습니다.<br />
|
||||
세무사 측에서 고객 등록을 완료하거나 관리자 백오피스에서 이메일/전화번호가 일치하도록 지정하면 자동으로 포털 데이터가 활성화됩니다.
|
||||
</p>
|
||||
<a href="/taxbaik/contact" class="btn btn-primary px-4 py-2">
|
||||
<i class="bi bi-chat-dots"></i> 세무사에게 문의하기
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row g-4">
|
||||
<!-- 왼쪽: 세무 신고 현황 (Tax Filings) -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card glass-card mb-4">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="h5 fw-bold text-dark mb-0">
|
||||
<i class="bi bi-calendar-check text-primary me-2"></i> 나의 세무 신고 현황
|
||||
</h3>
|
||||
<span class="badge bg-secondary">총 @(Model.Filings.Count)건</span>
|
||||
</div>
|
||||
|
||||
@if (!Model.Filings.Any())
|
||||
{
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="bi bi-folder-x display-4 d-block mb-3 text-secondary"></i>
|
||||
등록된 세무 신고 일정이 없습니다.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col">신고 종류</th>
|
||||
<th scope="col">신고 기한</th>
|
||||
<th scope="col">진행 상태</th>
|
||||
<th scope="col">메모</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var filing in Model.Filings)
|
||||
{
|
||||
var dDay = (filing.DueDate - DateTime.Today).Days;
|
||||
var statusClass = filing.Status switch
|
||||
{
|
||||
"filed" => "bg-success-subtle text-success",
|
||||
"overdue" => "bg-danger-subtle text-danger",
|
||||
_ => "bg-warning-subtle text-warning-emphasis"
|
||||
};
|
||||
var statusLabel = filing.Status switch
|
||||
{
|
||||
"filed" => "신고 완료",
|
||||
"overdue" => "기한 초과",
|
||||
_ => $"D-{dDay}"
|
||||
};
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<span class="fw-bold text-dark">@filing.FilingType</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>@filing.DueDate.ToString("yyyy-MM-dd")</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge @statusClass px-2.5 py-1.5 fs-7">@statusLabel</span>
|
||||
</td>
|
||||
<td class="text-muted small">
|
||||
@(string.IsNullOrEmpty(filing.Memo) ? "-" : filing.Memo)
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 오른쪽: 상담 이력 요약 (Consulting Activities) -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card glass-card">
|
||||
<div class="card-body p-4">
|
||||
<h3 class="h5 fw-bold text-dark mb-4">
|
||||
<i class="bi bi-chat-text text-primary me-2"></i> 최근 상담 및 지원 이력
|
||||
</h3>
|
||||
|
||||
@if (!Model.Consultations.Any())
|
||||
{
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="bi bi-chat-square-dots display-4 d-block mb-3 text-secondary"></i>
|
||||
최근 상담 이력이 없습니다.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="timeline ps-2">
|
||||
@foreach (var activity in Model.Consultations)
|
||||
{
|
||||
<div class="timeline-item-modern">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="badge bg-primary-subtle text-primary small">@activity.ActivityType</span>
|
||||
<small class="text-muted">@activity.ActivityDate.ToString("yyyy-MM-dd")</small>
|
||||
</div>
|
||||
<p class="text-dark small mb-1 fw-semibold">@activity.Description</p>
|
||||
@if (!string.IsNullOrEmpty(activity.Outcome))
|
||||
{
|
||||
<div class="bg-light p-2 rounded small text-muted mt-1">
|
||||
<strong>결과:</strong> @activity.Outcome
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Portal;
|
||||
|
||||
[Authorize(AuthenticationSchemes = PortalAuthDefaults.Scheme)]
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly TaxFilingService _taxFilingService;
|
||||
private readonly ConsultingActivityService _consultingActivityService;
|
||||
private readonly ClientService _clientService;
|
||||
|
||||
public IndexModel(
|
||||
TaxFilingService taxFilingService,
|
||||
ConsultingActivityService consultingActivityService,
|
||||
ClientService clientService)
|
||||
{
|
||||
_taxFilingService = taxFilingService;
|
||||
_consultingActivityService = consultingActivityService;
|
||||
_clientService = clientService;
|
||||
}
|
||||
|
||||
public Client? ClientInfo { get; private set; }
|
||||
public List<TaxFiling> Filings { get; private set; } = new();
|
||||
public List<ConsultingActivity> Consultations { get; private set; } = new();
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
var clientIdClaim = User.FindFirst("client_id");
|
||||
if (clientIdClaim != null && int.TryParse(clientIdClaim.Value, out var clientId))
|
||||
{
|
||||
ClientInfo = await _clientService.GetByIdAsync(clientId);
|
||||
if (ClientInfo != null)
|
||||
{
|
||||
var filingsData = await _taxFilingService.GetByClientIdAsync(clientId);
|
||||
Filings = filingsData.OrderBy(f => f.DueDate).ToList();
|
||||
|
||||
var consultationsData = await _consultingActivityService.GetByClientIdAsync(clientId);
|
||||
Consultations = consultationsData.OrderByDescending(c => c.ActivityDate).ToList();
|
||||
}
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@page "/portal/login"
|
||||
@model TaxBaik.Web.Pages.Portal.LoginModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 포털 로그인";
|
||||
ViewData["Description"] = "고객 포털 로그인 페이지입니다.";
|
||||
ViewData["CanonicalUrl"] = $"{Request.Scheme}://{Request.Host}/taxbaik/portal/login";
|
||||
}
|
||||
|
||||
<section class="container py-5" style="max-width: 560px;">
|
||||
<h1 class="h3 fw-bold mb-4">고객 포털 로그인</h1>
|
||||
<div class="alert alert-secondary">
|
||||
포털 인증은 다음 단계에서 이메일/비밀번호와 소셜 로그인으로 연결됩니다.
|
||||
</div>
|
||||
@if (!string.IsNullOrWhiteSpace(Model.ErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">@Model.ErrorMessage</div>
|
||||
}
|
||||
<form method="post" class="vstack gap-3">
|
||||
<div>
|
||||
<label class="form-label">이메일</label>
|
||||
<input class="form-control" asp-for="Email" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">비밀번호</label>
|
||||
<input class="form-control" asp-for="Password" type="password" />
|
||||
</div>
|
||||
<button class="btn btn-dark" type="submit">로그인</button>
|
||||
</form>
|
||||
<div class="d-grid gap-2 mt-4">
|
||||
<form method="post" asp-page-handler="Google">
|
||||
<button class="btn btn-outline-dark w-100" type="submit">Google로 로그인</button>
|
||||
</form>
|
||||
<form method="post" asp-page-handler="Naver">
|
||||
<button class="btn btn-outline-success w-100" type="submit">Naver로 로그인</button>
|
||||
</form>
|
||||
<form method="post" asp-page-handler="Kakao">
|
||||
<button class="btn btn-outline-warning w-100" type="submit">Kakao로 로그인</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,56 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Portal;
|
||||
|
||||
public class LoginModel : PageModel
|
||||
{
|
||||
private readonly PortalAuthService _portalAuthService;
|
||||
|
||||
[BindProperty]
|
||||
public string Email { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
public LoginModel(PortalAuthService portalAuthService)
|
||||
{
|
||||
_portalAuthService = portalAuthService;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Email) || string.IsNullOrWhiteSpace(Password))
|
||||
{
|
||||
ErrorMessage = "이메일과 비밀번호를 입력하세요.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
var signedIn = await _portalAuthService.SignInAsync(Email, Password);
|
||||
if (!signedIn)
|
||||
{
|
||||
ErrorMessage = "로그인 정보를 확인할 수 없습니다.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
return RedirectToPage("/Portal/Index");
|
||||
}
|
||||
|
||||
public IActionResult OnPostGoogle() => Challenge(BuildProps("google"), PortalOAuthDefaults.GoogleScheme);
|
||||
|
||||
public IActionResult OnPostNaver() => Challenge(BuildProps("naver"), PortalOAuthDefaults.NaverScheme);
|
||||
|
||||
public IActionResult OnPostKakao() => Challenge(BuildProps("kakao"), PortalOAuthDefaults.KakaoScheme);
|
||||
|
||||
private static AuthenticationProperties BuildProps(string provider) =>
|
||||
new() { RedirectUri = $"/taxbaik/portal/external-callback?provider={provider}" };
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@page "/portal/register"
|
||||
@model TaxBaik.Web.Pages.Portal.RegisterModel
|
||||
@{
|
||||
ViewData["Title"] = "고객 포털 회원가입";
|
||||
ViewData["Description"] = "고객 포털 회원가입 페이지입니다.";
|
||||
ViewData["CanonicalUrl"] = $"{Request.Scheme}://{Request.Host}/taxbaik/portal/register";
|
||||
}
|
||||
|
||||
<section class="container py-5" style="max-width: 640px;">
|
||||
<h1 class="h3 fw-bold mb-4">고객 포털 회원가입</h1>
|
||||
<div class="alert alert-secondary">
|
||||
가입 흐름은 다음 단계에서 이메일/전화번호 검증과 소셜 로그인으로 확장합니다.
|
||||
</div>
|
||||
<form method="post" class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">이름</label>
|
||||
<input class="form-control" asp-for="Name" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">연락처</label>
|
||||
<input class="form-control" asp-for="Phone" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">이메일</label>
|
||||
<input class="form-control" asp-for="Email" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">비밀번호</label>
|
||||
<input class="form-control" asp-for="Password" type="password" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button class="btn btn-dark" type="submit">가입하기</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -0,0 +1,75 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages.Portal;
|
||||
|
||||
public class RegisterModel : PageModel
|
||||
{
|
||||
private readonly PortalUserService _portalUserService;
|
||||
private readonly ClientService _clientService;
|
||||
|
||||
[BindProperty]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string Phone { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string Email { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
[BindProperty]
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
public RegisterModel(PortalUserService portalUserService, ClientService clientService)
|
||||
{
|
||||
_portalUserService = portalUserService;
|
||||
_clientService = clientService;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Name) || string.IsNullOrWhiteSpace(Email))
|
||||
{
|
||||
ErrorMessage = "이름과 이메일을 입력하세요.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Password) || Password.Length < 8)
|
||||
{
|
||||
ErrorMessage = "비밀번호는 8자 이상이어야 합니다.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
var existing = await _portalUserService.GetByEmailAsync(Email);
|
||||
if (existing is not null)
|
||||
{
|
||||
ErrorMessage = "이미 등록된 이메일입니다.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
int? clientId = null;
|
||||
var linkedClient = await _clientService.GetByEmailAsync(Email);
|
||||
if (linkedClient is null && !string.IsNullOrWhiteSpace(Phone))
|
||||
linkedClient = await _clientService.GetByPhoneAsync(Phone);
|
||||
if (linkedClient is not null)
|
||||
clientId = linkedClient.Id;
|
||||
|
||||
await _portalUserService.RegisterLocalAsync(
|
||||
Name,
|
||||
Email,
|
||||
Phone,
|
||||
PortalAuthService.HashPassword(Password),
|
||||
clientId: clientId);
|
||||
|
||||
return RedirectToPage("/Portal/Login");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.PrivacyModel
|
||||
@{
|
||||
ViewData["Title"] = "개인정보처리방침 | 백원숙 세무회계";
|
||||
ViewData["Description"] = "백원숙 세무회계의 개인정보 수집·이용·보관에 관한 방침을 안내합니다.";
|
||||
}
|
||||
|
||||
<div class="container py-5" style="max-width:800px">
|
||||
<h1 class="h3 fw-bold mb-4">개인정보처리방침</h1>
|
||||
<p class="text-muted mb-4">최종 수정일: 2026년 6월 27일</p>
|
||||
|
||||
<p>백원숙 세무회계(이하 "사무소")는 「개인정보 보호법」에 따라 고객의 개인정보를 보호하고, 관련 불만을 신속하게 처리하기 위해 아래와 같이 개인정보처리방침을 수립·공개합니다.</p>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">1. 수집하는 개인정보 항목</h2>
|
||||
<ul>
|
||||
<li><strong>필수:</strong> 성명, 연락처(전화번호)</li>
|
||||
<li><strong>선택:</strong> 이메일 주소, 문의 내용</li>
|
||||
<li><strong>자동 수집:</strong> 접속 IP 주소, 접속 일시</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">2. 개인정보의 수집·이용 목적</h2>
|
||||
<ul>
|
||||
<li>세무·부동산·가족자산 상담 신청 접수 및 답변</li>
|
||||
<li>서비스 이용 문의에 대한 회신</li>
|
||||
<li>서비스 품질 향상을 위한 통계 분석 (비식별화 처리)</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">3. 개인정보의 보유 및 이용 기간</h2>
|
||||
<p>상담 완료 후 <strong>3년</strong>간 보관 후 파기합니다. 단, 관계 법령에 따라 보관이 필요한 경우 해당 기간 동안 보관합니다.</p>
|
||||
<ul>
|
||||
<li>전자상거래 기록: 5년 (전자상거래 등에서의 소비자 보호에 관한 법률)</li>
|
||||
<li>세금계산서 관련 자료: 5년 (부가가치세법)</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">4. 개인정보의 제3자 제공</h2>
|
||||
<p>사무소는 원칙적으로 고객의 개인정보를 외부에 제공하지 않습니다. 다만, 다음의 경우에는 예외로 합니다.</p>
|
||||
<ul>
|
||||
<li>고객이 동의한 경우</li>
|
||||
<li>법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 수사기관이 요구하는 경우</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">5. 개인정보의 파기</h2>
|
||||
<p>보유 기간이 경과하거나 처리 목적이 달성된 경우 지체 없이 파기합니다.</p>
|
||||
<ul>
|
||||
<li><strong>전자 파일 형태:</strong> 복구 불가능한 방법으로 영구 삭제</li>
|
||||
<li><strong>종이 문서:</strong> 분쇄기 파기 또는 소각</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">6. 정보주체의 권리·의무</h2>
|
||||
<p>고객은 언제든지 다음 권리를 행사할 수 있습니다.</p>
|
||||
<ul>
|
||||
<li>개인정보 열람 요구</li>
|
||||
<li>오류 등이 있을 경우 정정 요구</li>
|
||||
<li>삭제 요구</li>
|
||||
<li>처리 정지 요구</li>
|
||||
</ul>
|
||||
<p>권리 행사는 아래 연락처로 서면, 전화, 이메일로 요청하시면 지체 없이 조치하겠습니다.</p>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">7. 개인정보 보호책임자</h2>
|
||||
<table class="table table-bordered mt-2" style="max-width:400px">
|
||||
<tbody>
|
||||
<tr><th>성명</th><td>백원숙</td></tr>
|
||||
<tr><th>직책</th><td>세무사</td></tr>
|
||||
<tr><th>연락처</th><td>010-4122-8268</td></tr>
|
||||
<tr><th>이메일</th><td>taxbaik5668@gmail.com</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">8. 개인정보 처리방침 변경</h2>
|
||||
<p>이 개인정보처리방침은 2026년 6월 27일부터 적용되며, 변경 시 홈페이지 공지를 통해 안내합니다.</p>
|
||||
|
||||
<div class="mt-5">
|
||||
<a href="/taxbaik" class="btn btn-outline-primary">홈으로 돌아가기</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
public class PrivacyModel : PageModel
|
||||
{
|
||||
public void OnGet() { }
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
@page
|
||||
@{
|
||||
ViewData["Title"] = "주요 서비스 | 백원숙 세무회계";
|
||||
ViewData["Description"] = "사업자 세무, 부동산 세금, 종합소득세 등 전문 상담 서비스";
|
||||
}
|
||||
|
||||
<!-- Breadcrumb Navigation -->
|
||||
<nav aria-label="breadcrumb" class="py-3" style="background: #F9F7F3; border-bottom: 1px solid #D9D3C4;">
|
||||
<div class="container">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item"><a href="/taxbaik/" class="text-decoration-none">홈</a></li>
|
||||
<li class="breadcrumb-item active">서비스</li>
|
||||
</ol>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="mb-4">
|
||||
<a href="/taxbaik/" class="btn btn-sm btn-outline-secondary">← 홈으로 돌아가기</a>
|
||||
</div>
|
||||
<h1 class="fw-bold mb-5 text-center">주요 서비스</h1>
|
||||
|
||||
<!-- 사업자 세무 -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 mb-4">
|
||||
<h2 class="fw-bold mb-3">사업자 세무</h2>
|
||||
<p class="lead">사업 운영 중 발생하는 모든 세무 문제를 전문적으로 처리합니다.</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">✓ 회계 기장 및 장부 관리</li>
|
||||
<li class="mb-2">✓ 세금계산서 발급 및 관리</li>
|
||||
<li class="mb-2">✓ 경비 처리 및 절세 전략</li>
|
||||
<li class="mb-2">✓ 원천징수 관리</li>
|
||||
<li class="mb-2">✓ 결산 및 세무신고</li>
|
||||
</ul>
|
||||
<p class="text-muted mt-4"><small>상담료: 7만~15만원</small></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">이런 분들이 찾습니다</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">💼 법인 및 개인사업자</li>
|
||||
<li class="mb-2">🛍️ 온라인쇼핑몰 운영자</li>
|
||||
<li class="mb-2">🏢 자영업자</li>
|
||||
<li class="mb-2">📊 첫 사업을 시작한 분</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 부동산 세금 -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 order-md-2 mb-4">
|
||||
<h2 class="fw-bold mb-3">부동산 세금</h2>
|
||||
<p class="lead">부동산 거래 시 발생하는 세금을 절감하고 최적화합니다.</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">✓ 양도세 계산 및 절감 전략</li>
|
||||
<li class="mb-2">✓ 취득세 및 등록세 최소화</li>
|
||||
<li class="mb-2">✓ 임대소득세 신고</li>
|
||||
<li class="mb-2">✓ 1주택 비과세 판정</li>
|
||||
<li class="mb-2">✓ 다주택자 세금 최적화</li>
|
||||
</ul>
|
||||
<p class="text-muted mt-4"><small>상담료: 10만~20만원</small></p>
|
||||
</div>
|
||||
<div class="col-md-6 order-md-1">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">이런 분들이 찾습니다</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">🏠 부동산 양도 예정자</li>
|
||||
<li class="mb-2">🏘️ 다주택 소유자</li>
|
||||
<li class="mb-2">🏠 전월세 임대인</li>
|
||||
<li class="mb-2">🔄 상속 부동산 처리</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 종합소득세 -->
|
||||
<section class="mb-5 pb-5 border-bottom">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 mb-4">
|
||||
<h2 class="fw-bold mb-3">종합소득세</h2>
|
||||
<p class="lead">프리랜서, 보험설계사 등 다양한 소득에 최적화된 신고를 지원합니다.</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">✓ 기준경비율 적용 최적화</li>
|
||||
<li class="mb-2">✓ 실제경비 계산 및 증명</li>
|
||||
<li class="mb-2">✓ 직업별 맞춤 절세 전략</li>
|
||||
<li class="mb-2">✓ 기납부세액 환급 신청</li>
|
||||
<li class="mb-2">✓ 근로소득 + 기타소득 통합 신고</li>
|
||||
</ul>
|
||||
<p class="text-muted mt-4"><small>상담료: 7만~15만원</small></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">이런 분들이 찾습니다</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">💻 프리랜서</li>
|
||||
<li class="mb-2">🎓 강사 및 컨설턴트</li>
|
||||
<li class="mb-2">💰 보험설계사</li>
|
||||
<li class="mb-2">📈 부동산중개사</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 가족자산 & 증여세 -->
|
||||
<section class="mb-5">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 order-md-2 mb-4">
|
||||
<h2 class="fw-bold mb-3">가족자산 & 증여세</h2>
|
||||
<p class="lead">세대 간 자산 이전 시 최소한의 세금으로 전략적 이전을 지원합니다.</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">✓ 증여세 기초공제 활용</li>
|
||||
<li class="mb-2">✓ 배우자 공제 전략</li>
|
||||
<li class="mb-2">✓ 단계적 증여 계획</li>
|
||||
<li class="mb-2">✓ 자녀 교육자금 증여</li>
|
||||
<li class="mb-2">✓ 상속세 절감 전략</li>
|
||||
</ul>
|
||||
<p class="text-muted mt-4"><small>상담료: 15만~25만원</small></p>
|
||||
</div>
|
||||
<div class="col-md-6 order-md-1">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">이런 분들이 찾습니다</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">👨👩👧👦 자녀에게 증여 예정</li>
|
||||
<li class="mb-2">🏠 부동산 상속 예정</li>
|
||||
<li class="mb-2">💼 기업 승계 계획</li>
|
||||
<li class="mb-2">💰 자산관리 중장기 계획</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA -->
|
||||
<section class="bg-primary text-white py-5 rounded mt-5 mb-5">
|
||||
<div class="text-center">
|
||||
<h2 class="fw-bold mb-3">전문 상담받으세요</h2>
|
||||
<p class="lead mb-4">정확한 진단 후 맞춤형 솔루션을 제시합니다</p>
|
||||
<a href="/taxbaik/contact" class="btn btn-warning btn-lg">무료 상담 신청</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 관련 페이지 네비게이션 -->
|
||||
<section class="text-center py-5">
|
||||
<h4 class="fw-bold mb-4">다른 페이지 보기</h4>
|
||||
<div class="row g-3 justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
🏠 홈<br/>
|
||||
<small class="text-muted">최신 정보 및 블로그</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/about" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
👤 세무사 소개<br/>
|
||||
<small class="text-muted">자격 및 상담 철학</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="/taxbaik/blog" class="btn btn-outline-primary btn-sm w-100 py-3">
|
||||
📝 세무 정보<br/>
|
||||
<small class="text-muted">절세팁 및 신고 가이드</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<footer class="bg-light border-top mt-5 py-4">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">백원숙 세무회계</h6>
|
||||
<p class="small text-muted">
|
||||
사업자 기장, 부동산 양도세·증여세,<br />
|
||||
종합소득세 전문 상담
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">연락처</h6>
|
||||
<p class="small">
|
||||
📞 <a href="tel:010-4122-8268" class="text-decoration-none">010-4122-8268</a><br />
|
||||
📧 <a href="mailto:taxbaik5668@gmail.com" class="text-decoration-none">taxbaik5668@gmail.com</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">채널</h6>
|
||||
<p class="small">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-sm btn-warning me-2">카카오톡</a>
|
||||
<a href="https://www.instagram.com/taxtory5668/" target="_blank" class="btn btn-sm btn-outline-secondary">Instagram</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-3" />
|
||||
<div class="text-center small text-muted">
|
||||
<p>© 2026 백원숙 세무회계. All rights reserved.</p>
|
||||
<a href="/taxbaik/privacy" class="text-decoration-none text-muted me-2">개인정보처리방침</a>
|
||||
<a href="/taxbaik/terms" class="text-decoration-none text-muted">이용약관</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,34 @@
|
||||
<header class="sticky-top bg-white border-bottom site-header">
|
||||
<nav class="navbar navbar-expand-lg navbar-light container-fluid px-3 py-2">
|
||||
<a class="navbar-brand fw-bold" href="/taxbaik">
|
||||
<span class="text-primary">백원숙</span> 세무회계
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse mt-3 mt-lg-0" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto gap-2 align-items-lg-center">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/taxbaik">홈</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/taxbaik/about">소개</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/taxbaik/services">서비스</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/taxbaik/blog">블로그</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-primary btn-sm ms-lg-2 w-100 w-lg-auto" href="/taxbaik/contact">상담신청</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ms-3 d-none d-md-block">
|
||||
<a href="tel:010-4122-8268" class="text-decoration-none text-dark fw-500">
|
||||
📞 010-4122-8268
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -0,0 +1,3 @@
|
||||
@using TaxBaik.Web
|
||||
@namespace TaxBaik.Web.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.SitemapModel
|
||||
@{
|
||||
Response.ContentType = "application/xml";
|
||||
}<?xml version="1.0" encoding="utf-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
@foreach (var url in Model.Urls)
|
||||
{
|
||||
<url>
|
||||
<loc>@url</loc>
|
||||
<lastmod>@DateTime.UtcNow:yyyy-MM-dd</lastmod>
|
||||
</url>
|
||||
}
|
||||
</urlset>
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
public class SitemapModel : PageModel
|
||||
{
|
||||
private readonly BlogService _blogService;
|
||||
public List<string> Urls { get; set; } = [];
|
||||
|
||||
public SitemapModel(BlogService blogService)
|
||||
{
|
||||
_blogService = blogService;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
var baseUrl = "http://178.104.200.7/taxbaik";
|
||||
Urls.AddRange(new[]
|
||||
{
|
||||
$"{baseUrl}",
|
||||
$"{baseUrl}/about",
|
||||
$"{baseUrl}/services",
|
||||
$"{baseUrl}/contact",
|
||||
$"{baseUrl}/blog"
|
||||
});
|
||||
|
||||
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 1000);
|
||||
foreach (var post in posts)
|
||||
{
|
||||
Urls.Add($"{baseUrl}/blog/{post.Slug}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.TermsModel
|
||||
@{
|
||||
ViewData["Title"] = "이용약관 | 백원숙 세무회계";
|
||||
ViewData["Description"] = "백원숙 세무회계 홈페이지 이용약관을 안내합니다.";
|
||||
}
|
||||
|
||||
<div class="container py-5" style="max-width:800px">
|
||||
<h1 class="h3 fw-bold mb-4">이용약관</h1>
|
||||
<p class="text-muted mb-4">최종 수정일: 2026년 6월 27일 / 시행일: 2026년 6월 27일</p>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제1조 (목적)</h2>
|
||||
<p>이 약관은 백원숙 세무회계(이하 "사무소")가 운영하는 홈페이지(taxbaik.com, 이하 "사이트")에서 제공하는 온라인 상담 신청 및 정보 서비스 이용과 관련하여 사무소와 이용자의 권리·의무 및 책임사항을 규정함을 목적으로 합니다.</p>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제2조 (정의)</h2>
|
||||
<ul>
|
||||
<li>"서비스"란 사이트에서 제공하는 세무 정보 제공, 상담 신청, 블로그 콘텐츠 열람 등 일체의 서비스를 말합니다.</li>
|
||||
<li>"이용자"란 사이트에 접속하여 서비스를 이용하는 자를 말합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제3조 (약관의 효력 및 변경)</h2>
|
||||
<p>이 약관은 사이트 내 게시함으로써 효력이 발생합니다. 사무소는 필요한 경우 약관을 변경할 수 있으며, 변경된 약관은 사이트 공지 후 7일이 경과한 날로부터 효력이 발생합니다.</p>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제4조 (서비스 제공)</h2>
|
||||
<p>사무소는 다음 서비스를 제공합니다.</p>
|
||||
<ul>
|
||||
<li>세무·부동산·가족자산 관련 정보 콘텐츠 제공</li>
|
||||
<li>온라인 상담 신청 접수</li>
|
||||
<li>시즌별 세무 정보 안내</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제5조 (서비스 이용 시 주의사항)</h2>
|
||||
<ul>
|
||||
<li>사이트에서 제공하는 세무 정보는 일반적인 안내 목적으로, 개별 사안에 대한 법적 효력을 갖지 않습니다.</li>
|
||||
<li>구체적인 세무 처리는 반드시 전문가 상담을 통해 진행하시기 바랍니다.</li>
|
||||
<li>이용자는 상담 신청 시 허위 정보를 제공해서는 안 됩니다.</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제6조 (면책 조항)</h2>
|
||||
<p>사무소는 다음 사항에 대해 책임을 지지 않습니다.</p>
|
||||
<ul>
|
||||
<li>천재지변 또는 이에 준하는 불가항력으로 서비스를 제공할 수 없는 경우</li>
|
||||
<li>이용자의 귀책 사유로 인한 서비스 이용 장애</li>
|
||||
<li>사이트에서 제공하는 일반 정보를 개별 사안에 적용함으로써 발생하는 손해</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제7조 (저작권)</h2>
|
||||
<p>사이트에 게시된 모든 콘텐츠(글, 이미지, 디자인 등)의 저작권은 사무소 또는 원저작자에게 있으며, 무단 복제·배포를 금합니다.</p>
|
||||
|
||||
<h2 class="h5 fw-bold mt-4 mb-2">제8조 (준거법 및 재판관할)</h2>
|
||||
<p>이 약관에 관한 분쟁은 대한민국 법령을 적용하며, 관할 법원은 민사소송법에 따른 관할 법원으로 합니다.</p>
|
||||
|
||||
<div class="mt-5">
|
||||
<a href="/taxbaik" class="btn btn-outline-primary">홈으로 돌아가기</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
public class TermsModel : PageModel
|
||||
{
|
||||
public void OnGet() { }
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@(ViewData["Title"] ?? "백원숙 세무회계 - 세무사 전문 상담")</title>
|
||||
<meta name="description" content="@(ViewData["Description"] ?? "백원숙 세무회계 - 사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담. 맞춤형 세무 절세 컨설팅 제공.")" />
|
||||
<meta name="keywords" content="백원숙 세무회계, 세무사, 사업자 기장, 양도소득세, 증여세, 상속세, 종합소득세, 절세 상담, 세무 대리" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="@(ViewData["Title"] ?? "백원숙 세무회계 - 세무사 전문 상담")" />
|
||||
<meta property="og:description" content="@(ViewData["Description"] ?? "백원숙 세무회계 - 사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담. 맞춤형 세무 절세 컨설팅 제공.")" />
|
||||
<meta property="og:image" content="@(ViewData["OgImage"] ?? "http://178.104.200.7/taxbaik/images/og-image.jpg")" />
|
||||
<meta property="og:url" content="@(ViewData["OgUrl"] ?? "http://178.104.200.7/taxbaik/")" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:title" content="@(ViewData["Title"] ?? "백원숙 세무회계 - 세무사 전문 상담")" />
|
||||
<meta property="twitter:description" content="@(ViewData["Description"] ?? "백원숙 세무회계 - 사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담. 맞춤형 세무 절세 컨설팅 제공.")" />
|
||||
<meta property="twitter:image" content="@(ViewData["OgImage"] ?? "http://178.104.200.7/taxbaik/images/og-image.jpg")" />
|
||||
|
||||
<!-- 검색엔진 등록용 소유권 인증 메타 태그 (발급받으신 토큰이 있으면 아래 content에 넣어 주시면 됩니다) -->
|
||||
<!-- <meta name="naver-site-verification" content="네이버_서치어드바이저_토큰_입력" /> -->
|
||||
<!-- <meta name="google-site-verification" content="구글_서치콘솔_토큰_입력" /> -->
|
||||
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta name="theme-color" content="#C89D6E" />
|
||||
<link rel="icon" type="image/svg+xml" href="/taxbaik/favicon.svg" />
|
||||
<link rel="alternate icon" href="/taxbaik/favicon.ico" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
|
||||
<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 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" />
|
||||
|
||||
<!-- 구조화된 데이터 (JSON-LD Schema Markup) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@@context": "https://schema.org",
|
||||
"@@type": "ProfessionalService",
|
||||
"name": "백원숙 세무회계",
|
||||
"description": "사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담 세무사",
|
||||
"url": "http://178.104.200.7/taxbaik/",
|
||||
"telephone": "010-4122-8268",
|
||||
"email": "taxbaik5668@gmail.com",
|
||||
"address": {
|
||||
"@@type": "PostalAddress",
|
||||
"addressCountry": "KR"
|
||||
},
|
||||
"sameAs": [
|
||||
"https://www.instagram.com/taxtory5668/",
|
||||
"http://pf.kakao.com/_xoxchTX"
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-25KRKY45D7"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-25KRKY45D7');
|
||||
</script>
|
||||
</head>
|
||||
<body class="with-mobile-cta">
|
||||
<partial name="_Header" />
|
||||
<main role="main" class="pb-5">
|
||||
@RenderBody()
|
||||
</main>
|
||||
<footer class="bg-light border-top mt-5 py-5">
|
||||
<div class="container">
|
||||
<div class="row g-5">
|
||||
<div class="col-md-3">
|
||||
<h6 class="fw-bold mb-3">백원숙 세무회계</h6>
|
||||
<p class="small text-muted">
|
||||
사업자 기장, 부동산 양도세·증여세,<br />
|
||||
종합소득세 전문 상담
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h6 class="fw-bold mb-3">메뉴</h6>
|
||||
<ul class="list-unstyled small">
|
||||
<li class="mb-2"><a href="/taxbaik/" class="text-decoration-none text-muted">홈</a></li>
|
||||
<li class="mb-2"><a href="/taxbaik/about" class="text-decoration-none text-muted">세무사 소개</a></li>
|
||||
<li class="mb-2"><a href="/taxbaik/services" class="text-decoration-none text-muted">전문 서비스</a></li>
|
||||
<li class="mb-2"><a href="/taxbaik/blog" class="text-decoration-none text-muted">세무 정보</a></li>
|
||||
<li><a href="/taxbaik/contact" class="text-decoration-none text-muted">상담 신청</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h6 class="fw-bold mb-3">연락처</h6>
|
||||
<p class="small">
|
||||
📞 <a href="tel:010-4122-8268" class="text-decoration-none text-muted">010-4122-8268</a><br />
|
||||
📧 <a href="mailto:taxbaik5668@gmail.com" class="text-decoration-none text-muted">taxbaik5668@gmail.com</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h6 class="fw-bold mb-3">채널</h6>
|
||||
<p class="small">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-sm btn-warning me-2">카카오톡</a>
|
||||
<a href="https://www.instagram.com/taxtory5668/" target="_blank" class="btn btn-sm btn-outline-secondary">Instagram</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-4" />
|
||||
<div class="text-center small text-muted">
|
||||
<p>© 2026 백원숙 세무회계. All rights reserved.</p>
|
||||
<div class="mb-2">
|
||||
<a href="/taxbaik/privacy" class="text-decoration-none text-muted me-2">개인정보처리방침</a>
|
||||
<span class="text-muted">|</span>
|
||||
<a href="/taxbaik/terms" class="text-decoration-none text-muted ms-2 me-2">이용약관</a>
|
||||
<span class="text-muted">|</span>
|
||||
<a href="/taxbaik/portal" class="text-decoration-none text-muted ms-2">고객 포털</a>
|
||||
</div>
|
||||
@if (Context.RequestServices.GetService(typeof(VersionInfo)) is VersionInfo version)
|
||||
{
|
||||
<div class="mt-2 text-muted" style="font-size: 0.75rem; opacity: 0.6;">
|
||||
v@(version.Version) · @version.Built
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Mobile Fixed CTA -->
|
||||
<div class="mobile-cta-bar d-lg-none">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn-kakao-mobile">
|
||||
💬 카카오 상담하기
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" defer></script>
|
||||
<script src="~/js/site.js" asp-append-version="true" defer></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
Reference in New Issue
Block a user