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,155 @@
|
||||
@page "/admin/blog/{id:int}/edit"
|
||||
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.DTOs
|
||||
@using TaxBaik.WasmClient.Components.Admin.Pages.Blog
|
||||
@inject IBlogBrowserClient BlogClient
|
||||
@inject ICategoryBrowserClient CategoryClient
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
|
||||
<PageTitle>포스트 수정</PageTitle>
|
||||
|
||||
<AdminCrudPageShell Title="포스트 수정"
|
||||
Eyebrow="Content"
|
||||
Subtitle="블로그 포스트를 수정합니다."
|
||||
Loading="@isLoading"
|
||||
SkeletonContent="@EditorSkeleton"
|
||||
OnCancel="@GoBack">
|
||||
@if (post == null)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-4">포스트를 찾을 수 없습니다.</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudPaper Class="pa-4 mt-4" Elevation="1">
|
||||
<BlogForm Model="model" Categories="categories" SubmitText="저장" OnSubmit="SavePost" />
|
||||
<div class="mt-4">
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Error" @onclick="DeletePost">삭제</MudButton>
|
||||
</div>
|
||||
</MudPaper>
|
||||
}
|
||||
</AdminCrudPageShell>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private TaxBaik.Application.DTOs.BlogPostResponseDto? post;
|
||||
private IReadOnlyList<Domain.Entities.Category> categories = [];
|
||||
private BlogForm.BlogFormModel model = new();
|
||||
private bool isLoading = true;
|
||||
|
||||
private RenderFragment EditorSkeleton => builder =>
|
||||
{
|
||||
builder.OpenComponent<AdminSkeletonRows>(0);
|
||||
builder.AddAttribute(1, "Rows", 5);
|
||||
builder.AddAttribute(2, "Columns", 3);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
post = await BlogClient.GetByIdAsync(Id);
|
||||
if (post != null)
|
||||
{
|
||||
categories = await CategoryClient.GetAllAsync();
|
||||
MapPostToModel(post);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"포스트 로드 실패: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapPostToModel(TaxBaik.Application.DTOs.BlogPostResponseDto post)
|
||||
{
|
||||
model.Title = post.Title;
|
||||
model.Content = post.Content;
|
||||
model.CategoryId = post.CategoryId;
|
||||
model.Tags = post.Tags;
|
||||
model.SeoTitle = post.SeoTitle;
|
||||
model.SeoDescription = post.SeoDescription;
|
||||
model.IsPublished = post.IsPublished;
|
||||
}
|
||||
|
||||
private void GoBack()
|
||||
{
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
}
|
||||
|
||||
private async Task SavePost()
|
||||
{
|
||||
if (post == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await BlogClient.UpdateAsync(post.Id, new CreateBlogPostDto
|
||||
{
|
||||
Title = model.Title,
|
||||
Content = model.Content,
|
||||
CategoryId = model.CategoryId,
|
||||
Tags = model.Tags,
|
||||
SeoTitle = model.SeoTitle,
|
||||
SeoDescription = model.SeoDescription,
|
||||
IsPublished = model.IsPublished
|
||||
});
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
Snackbar.Add("저장 실패: 포스트를 저장하지 못했습니다.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Snackbar.Add("포스트가 저장되었습니다.", Severity.Success);
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeletePost()
|
||||
{
|
||||
if (post == null)
|
||||
return;
|
||||
|
||||
var result = await DialogService.ShowMessageBox(
|
||||
"포스트 삭제",
|
||||
"정말 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
|
||||
"삭제", "취소");
|
||||
|
||||
if (result != true)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var deleted = await BlogClient.DeleteAsync(post.Id);
|
||||
if (!deleted)
|
||||
{
|
||||
Snackbar.Add("삭제 실패: 포스트를 삭제하지 못했습니다.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user