[] 사이드 뷰 개발

This commit is contained in:
SEAN-59 2025-07-03 21:16:55 +09:00
parent 3cbe24216d
commit 6342324108
7 changed files with 120 additions and 24 deletions

View File

@ -51,7 +51,8 @@ builder.Services.AddScoped<SecureService>();
builder.Services.AddScoped<StorageService>(); builder.Services.AddScoped<StorageService>();
builder.Services.AddScoped<QueryParamService>(); builder.Services.AddScoped<QueryParamService>();
builder.Services.AddScoped<LoadingService>(); builder.Services.AddScoped<LoadingService>();
builder.Services.AddScoped<UserStateService>();
builder.Services.AddSingleton<UserStateService>();
// builder.Services.AddSingleton<LoggerService>(sp // builder.Services.AddSingleton<LoggerService>(sp

View File

@ -14,6 +14,9 @@ public partial class MainLayout : LayoutComponentBase, IDisposable
[Inject] LoadingService LoadingService { get; set; } = default!; [Inject] LoadingService LoadingService { get; set; } = default!;
[Inject] UserStateService UserStateService { get; set; } = default!; [Inject] UserStateService UserStateService { get; set; } = default!;
private bool _isLoading = false;
// 경로의 시작 부분 // 경로의 시작 부분
// protected bool isHidePrjTop => Navigation.ToBaseRelativePath(Navigation.Uri).StartsWith("auth", StringComparison.OrdinalIgnoreCase); // protected bool isHidePrjTop => Navigation.ToBaseRelativePath(Navigation.Uri).StartsWith("auth", StringComparison.OrdinalIgnoreCase);
protected bool isIntro => Navigation.ToBaseRelativePath(Navigation.Uri).StartsWith("am/intro", StringComparison.OrdinalIgnoreCase) protected bool isIntro => Navigation.ToBaseRelativePath(Navigation.Uri).StartsWith("am/intro", StringComparison.OrdinalIgnoreCase)
@ -28,23 +31,50 @@ public partial class MainLayout : LayoutComponentBase, IDisposable
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
LoggerService.Write("MainLayout OnInitializedAsync 시작");
LoadingService.OnChange += StateHasChanged; LoadingService.OnChange += StateHasChanged;
Navigation.LocationChanged += HandleLocationChanged; Navigation.LocationChanged += HandleLocationChanged;
HandleLocationChanged(this, new LocationChangedEventArgs(Navigation.Uri, false)); HandleLocationChanged(this, new LocationChangedEventArgs(Navigation.Uri, false));
LoggerService.Write("MainLayout: UserStateService.GetUserDataAsync 호출 전");
await UserStateService.GetUserDataAsync();
LoggerService.Write($"MainLayout: UserStateService.isLogin 상태: {UserStateService.isLogin}");
// 학원 정보 로드 및 실패 시 처리
var academyResult = await UserStateService.GetAcademy();
LoadingService.ShowLoading();
if (academyResult.success)
{
if (academyResult.simpleAcademy != null && academyResult.simpleAcademy.Any())
{
UserStateService.academyItems = academyResult.simpleAcademy.ToArray();
LoggerService.Write($"MainLayout: academyItems 로드 성공. {UserStateService.academyItems.Length}개");
}
else
{
LoggerService.Write("MainLayout: 로드된 학원 정보가 없습니다.");
}
}
else
{
LoggerService.Write("MainLayout: 서버 세션에서 학원 정보 불러오기 실패. 사용자 상태 초기화.");
await UserStateService.ClearUserStateAsnyc();
}
if (isAcademy) if (isAcademy)
{ {
if(!UserStateService.isLogin || UserStateService.UserData == null) if(!UserStateService.isLogin || UserStateService.UserData == null)
{ {
LoggerService.Write("로그인 상태가 아닙니다. 초기로 돌립니다."); LoggerService.Write("로그인 상태가 아닙니다. 초기로 돌립니다.");
if (isIntro) if (isIntro) { }
{
} //await UserStateService.ClearUserStateAsnyc();
else else
{ {
await UserStateService.ClearUserStateAsnyc(); await UserStateService.ClearUserStateAsnyc();
LoadingService.HideLoading();
Navigation.NavigateTo("/"); Navigation.NavigateTo("/");
} }
} }
LoadingService.HideLoading();
} }
} }

View File

@ -2,16 +2,40 @@ using System.Text.Json;
using Front.Program.Services; using Front.Program.Services;
using Front.Program.Models; using Front.Program.Models;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using System.ComponentModel; // INotifyPropertyChanged를 위해 추가
using System.Runtime.CompilerServices; // CallerMemberName을 위해 추가
namespace Front.Program.ViewModels; namespace Front.Program.ViewModels;
public class UserStateService(StorageService _storageService, SecureService _secureService, APIService _APIService, public class UserStateService(StorageService _storageService, SecureService _secureService, APIService _APIService,
IJSRuntime _js) IJSRuntime _js) : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Models.SimpleAcademy? _currentAcademy;
public Models.SimpleAcademy? CurrentAcademy
{
get => _currentAcademy;
set
{
if (_currentAcademy != value)
{
_currentAcademy = value;
OnPropertyChanged();
}
}
}
public UserData UserData { get; set; } = new UserData(); public UserData UserData { get; set; } = new UserData();
public bool isFirstCheck { get; set; } = false; public bool isFirstCheck { get; set; } = false;
public bool isLogin { get; set; } = false; public bool isLogin { get; set; } = false;
public Models.SimpleAcademy[] academyItems = Array.Empty<Models.SimpleAcademy>(); public Models.SimpleAcademy[] academyItems = Array.Empty<Models.SimpleAcademy>();
@ -52,20 +76,24 @@ public class UserStateService(StorageService _storageService, SecureService _sec
try try
{ {
LoggerService.Write("GetUserDataAsync 호출됨"); LoggerService.Write("GetUserDataAsync 호출됨");
// 로그인 상태가 아니라면 애초에 할 필요 없음 var isLoginFromStorage = await _storageService.GetItemAsync("IsLogin");
if (await _storageService.GetItemAsync("IsLogin") != "true") LoggerService.Write($"Storage에서 읽은 IsLogin 값: {isLoginFromStorage ?? "null"}");
// 로그인 상태가 아니라면 애초에 할 필요 없음
if (isLoginFromStorage != "true")
{ {
isLogin = false; isLogin = false;
return false; return false;
} }
isLogin = true;
var userDataForm = await GetUserDataFromStorageAsync(); var userDataForm = await GetUserDataFromStorageAsync();
if (userDataForm.success && userDataForm.userData != null) if (userDataForm.success && userDataForm.userData != null)
{ {
// 사용자 데이터가 성공적으로 로드되었을 때의 로직 // 사용자 데이터가 성공적으로 로드되었을 때의 로직
UserData = userDataForm.userData; UserData = userDataForm.userData;
isLogin = true;
return true; return true;
} }
else else
@ -75,7 +103,6 @@ public class UserStateService(StorageService _storageService, SecureService _sec
{ {
// 서버에서 사용자 데이터를 성공적으로 로드했을 때의 로직 // 서버에서 사용자 데이터를 성공적으로 로드했을 때의 로직
UserData = userDataFromServer.userData; UserData = userDataFromServer.userData;
isLogin = true;
return true; return true;
} }
else else
@ -130,7 +157,14 @@ public class UserStateService(StorageService _storageService, SecureService _sec
{ {
var data = await _APIService.GetConnectServerAsnyc<List<SimpleAcademy>>("/api/v1/in/user/academy"); var data = await _APIService.GetConnectServerAsnyc<List<SimpleAcademy>>("/api/v1/in/user/academy");
if (data is { success: true, json: not null }) if (data is { success: true, json: not null })
{
await _storageService.SetItemAsync("UsAcDt", data.json); await _storageService.SetItemAsync("UsAcDt", data.json);
if (data.data != null && data.data.Any())
{
academyItems = data.data.ToArray();
CurrentAcademy = data.data.First(); // 첫 번째 학원을 현재 학원으로 설정
}
}
return (data.success, data.data); return (data.success, data.data);
} }
} }

View File

@ -5,9 +5,9 @@
</div> </div>
<div> <div>
<!-- 회원 이름이 오는 곳 --> <!-- 회원 이름이 오는 곳 -->
<div class="text-gray-900 text-base font-medium">AcaMate</div> <div class="text-gray-900 text-base font-medium">@UserName</div>
<!-- 회원 유형이 올 곳 --> <!-- 회원 유형이 올 곳 -->
<p class="text-gray-600 text-sm">Parent</p> <p class="text-gray-600 text-sm">@UserType</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,13 @@
using Front.Program.ViewModels;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace Front.Program.Views.Academy.Common; namespace Front.Program.Views.Academy.Common;
public partial class LeftSideAcademy : ComponentBase public partial class LeftSideAcademy : ComponentBase
{ {
[Inject]
private UserStateService UserStateService { get; set; }
private string UserName => UserStateService.UserData?.Name ?? "AcaMate";
private string UserType => UserStateService.UserData?.Type ?? "Parent";
} }

View File

@ -2,7 +2,7 @@
<div id="AcademyDrop" class="relative flex items-center gap-4 text-[#111418] flex-1"> <div id="AcademyDrop" class="relative flex items-center gap-4 text-[#111418] flex-1">
<button class="flex items-center gap-2 hover:text-blue-600" @onclick="ToggleAcademyDropdown"> <button class="flex items-center gap-2 hover:text-blue-600" @onclick="ToggleAcademyDropdown">
<h2 class="hidden md:block text-text-title text-lg font-bold leading-tight tracking-[-0.015em]">AcaMate</h2> <h2 class="hidden md:block text-text-title text-lg font-bold leading-tight tracking-[-0.015em]">@currentAcademyName</h2>
<img src="Resources/Images/Icon/Down.png" alt="아래" <img src="Resources/Images/Icon/Down.png" alt="아래"
class="w-6 h-6 object-cover transition-transform duration-200 @(isAcademyDropdownOpen ? "rotate-180" : "")" /> class="w-6 h-6 object-cover transition-transform duration-200 @(isAcademyDropdownOpen ? "rotate-180" : "")" />
</button> </button>
@ -10,15 +10,12 @@
@if (isAcademyDropdownOpen) @if (isAcademyDropdownOpen)
{ {
<div class="absolute top-full left-0 mt-2 w-64 bg-white rounded-lg shadow-lg py-2 z-50"> <div class="absolute top-full left-0 mt-2 w-64 bg-white rounded-lg shadow-lg py-2 z-50">
<div class="px-4 py-2 border-b border-gray-200">
<h3 class="font-semibold text-gray-900">학원 정보</h3>
</div>
<div class="max-h-60 overflow-y-auto"> <div class="max-h-60 overflow-y-auto">
@foreach (var academy in academyItems) @foreach (var academy in academyItems)
{ {
<a href="/am/@academy.bid" class="block px-4 py-2 text-text-title hover:bg-gray-100"> <div @onclick="() => SelectAcademy(academy)" class="cursor-pointer block px-4 py-2 text-text-title hover:bg-gray-100">
@academy.name @academy.name
</a> </div>
} }
</div> </div>
</div> </div>

View File

@ -1,3 +1,5 @@
using System.ComponentModel;
using Front.Program.ViewModels; using Front.Program.ViewModels;
using Front.Program.Services; using Front.Program.Services;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -8,23 +10,49 @@ using Front.Program.Models;
namespace Front.Program.Views.Academy.Common; namespace Front.Program.Views.Academy.Common;
public partial class TopNavAcademy : ComponentBase public partial class TopNavAcademy : ComponentBase, IDisposable
{ {
[Inject] UserStateService UserStateService { get; set; } = default!; [Inject] UserStateService UserStateService { get; set; } = default!;
[Inject] NavigationManager NavigationManager { get; set; } = default!;
protected bool isOpen = false; protected bool isOpen = false;
protected bool isAcademyDropdownOpen = false; protected bool isAcademyDropdownOpen = false;
protected Models.SimpleAcademy[] academyItems = Array.Empty<Models.SimpleAcademy>();
protected override async Task OnInitializedAsync() // 계산된 속성으로 변경
protected Models.SimpleAcademy[] academyItems => UserStateService.academyItems;
protected string currentAcademyName => UserStateService.CurrentAcademy?.name ?? "학원을 선택하세요";
protected override void OnInitialized()
{ {
LoggerService.Write("TOPNAV_OnInitializedAsync"); UserStateService.PropertyChanged += OnUserStateServicePropertyChanged;
}
public void Dispose()
{
UserStateService.PropertyChanged -= OnUserStateServicePropertyChanged;
}
private void OnUserStateServicePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(UserStateService.CurrentAcademy))
{
StateHasChanged();
}
} }
protected void ToggleAcademyDropdown() { protected void ToggleAcademyDropdown() {
isAcademyDropdownOpen = !isAcademyDropdownOpen; isAcademyDropdownOpen = !isAcademyDropdownOpen;
} }
private void SelectAcademy(SimpleAcademy academy)
{
UserStateService.CurrentAcademy = academy; // 현재 선택된 학원 업데이트
isAcademyDropdownOpen = false;
NavigationManager.NavigateTo($"/am/main?bid={academy.bid}");
}
private void OnClickOutside() private void OnClickOutside()
{ {
if (isAcademyDropdownOpen) isAcademyDropdownOpen = false; if (isAcademyDropdownOpen) isAcademyDropdownOpen = false;