diff --git a/src/renderer/src/components/Sidebar.tsx b/src/renderer/src/components/Sidebar.tsx
index c119b3f..e964a85 100644
--- a/src/renderer/src/components/Sidebar.tsx
+++ b/src/renderer/src/components/Sidebar.tsx
@@ -212,15 +212,14 @@ function Sidebar({ apps, userName, userEmail, userRole, userCreatedAt, userLastL
{collapsed &&
}
{/* 네비게이션 아이템 */}
- {apps.map((item) => {
- const unavailable = item.canLaunch === false || item.canView === false
+ {apps.filter((item) => item.canView !== false).map((item) => {
+ const unavailable = item.canLaunch === false
return (
{
- if (unavailable) return
onAppChange(item.appKey)
}}
title={collapsed ? item.displayName : undefined}
diff --git a/src/renderer/src/pages/Dashboard.tsx b/src/renderer/src/pages/Dashboard.tsx
index c9bc018..dfb452e 100644
--- a/src/renderer/src/pages/Dashboard.tsx
+++ b/src/renderer/src/pages/Dashboard.tsx
@@ -213,11 +213,11 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
if (cancelled || !payload?.apps?.length) return
setApps(payload.apps)
const currentApp = payload.apps.find((app) => app.appKey === activeApp)
- const currentAppAvailable = currentApp?.canLaunch !== false && currentApp?.canView !== false
- const firstAvailableApp = payload.apps.find((app) => app.canView !== false && app.canLaunch !== false)
+ const currentAppAvailable = currentApp?.canView !== false
+ const firstAvailableApp = payload.apps.find((app) => app.canView !== false)
const lastLaunchedAppKey = window.localStorage.getItem(LAST_LAUNCHED_APP_STORAGE_KEY)
const lastLaunchedApp = lastLaunchedAppKey
- ? payload.apps.find((app) => app.appKey === lastLaunchedAppKey && app.canView !== false && app.canLaunch !== false)
+ ? payload.apps.find((app) => app.appKey === lastLaunchedAppKey && app.canView !== false)
: undefined
if (!initialAppSelectionDoneRef.current) {
@@ -286,8 +286,9 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
installation = { installed: false, version: null, path: null }
}
- if (IS_DEV && appConfig.devAppPath && installation.installed) {
+ if (IS_DEV && appConfig.devAppPath) {
if (!cancelled) {
+ setInstalledOverrides((prev) => prev[activeApp] === undefined ? prev : { ...prev, [activeApp]: true })
setAppStatus({
appKey: activeApp,
displayName: appConfig.displayName,
@@ -688,7 +689,6 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
공지사항
- {appNotices.length}건
{appNotices.map((n, i) => (
diff --git a/src/renderer/src/pages/LoginForm.tsx b/src/renderer/src/pages/LoginForm.tsx
index 6a91cc8..efc7dd5 100644
--- a/src/renderer/src/pages/LoginForm.tsx
+++ b/src/renderer/src/pages/LoginForm.tsx
@@ -329,6 +329,31 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
}
}
+ const retryTailscaleAndContinue = async (): Promise
=> {
+ if (!pendingTailscaleConfirm) return
+ const pending = pendingTailscaleConfirm
+ setTailscaleInstalling(true)
+ setError('')
+
+ try {
+ await ensureLauncherTailscale(pending.email, pending.password)
+ setPendingTailscaleConfirm(null)
+ setLoading(true)
+ onLoadingChange(true)
+ await completeLogin(pending.session, pending.email, pending.keepLogin, Date.now())
+ } catch (retryError) {
+ setPendingTailscaleConfirm({
+ ...pending,
+ reason: retryError instanceof TailscaleInstallRequiredError ? 'install-required' : 'connection-failed',
+ message: retryError instanceof Error ? retryError.message : 'Tailscale 연결에 실패했습니다.',
+ })
+ } finally {
+ setTailscaleInstalling(false)
+ setLoading(false)
+ onLoadingChange(false)
+ }
+ }
+
const installTailscaleAndContinue = async (): Promise => {
if (!pendingTailscaleConfirm || !window.launcherTailscale?.install) return
const pending = pendingTailscaleConfirm
@@ -452,7 +477,7 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
{pendingTailscaleConfirm.reason === 'install-required'
? 'Autonomy Studio는 플랫폼 연결을 위해 Tailscale을 사용합니다. 설치를 진행하려면 관리자 권한이 필요합니다.'
- : '플랫폼 원격 제어 연결을 설정하지 못했습니다. 로그인은 계속 진행할 수 있지만, 원격 플랫폼 연결 기능은 제한될 수 있습니다.'}
+ : 'Tailscale은 설치되어 있지만 연결 정보를 가져오거나 서버에 접속하는 과정에서 실패했습니다. 잠시 후 다시 시도할 수 있습니다.'}
@@ -474,17 +499,18 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
type="button"
onClick={() => void installTailscaleAndContinue()}
className="h-9 cursor-pointer rounded-md border border-[#5588ff] bg-[#5588ff] px-4 text-[12px] font-bold text-white transition hover:bg-[#4778ea] disabled:cursor-default disabled:opacity-60"
- disabled={tailscaleInstalling}
+ disabled={tailscaleInstalling || !window.launcherTailscale?.install}
>
{tailscaleInstalling ? '설치 중...' : '설치'}
) : (
void continueWithoutTailscale()}
- className="h-9 cursor-pointer rounded-md border border-[#5588ff] bg-[#5588ff] px-4 text-[12px] font-bold text-white transition hover:bg-[#4778ea]"
+ onClick={() => void retryTailscaleAndContinue()}
+ className="h-9 cursor-pointer rounded-md border border-[#5588ff] bg-[#5588ff] px-4 text-[12px] font-bold text-white transition hover:bg-[#4778ea] disabled:cursor-default disabled:opacity-60"
+ disabled={tailscaleInstalling}
>
- 계속 진행
+ {tailscaleInstalling ? '재시도 중...' : '다시 시도'}
)}