merge develop into release
This commit is contained in:
@@ -212,15 +212,14 @@ function Sidebar({ apps, userName, userEmail, userRole, userCreatedAt, userLastL
|
|||||||
{collapsed && <div className="pt-4 mb-2" />}
|
{collapsed && <div className="pt-4 mb-2" />}
|
||||||
|
|
||||||
{/* 네비게이션 아이템 */}
|
{/* 네비게이션 아이템 */}
|
||||||
{apps.map((item) => {
|
{apps.filter((item) => item.canView !== false).map((item) => {
|
||||||
const unavailable = item.canLaunch === false || item.canView === false
|
const unavailable = item.canLaunch === false
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={item.appKey}
|
key={item.appKey}
|
||||||
className={`nav-item${activeApp === item.appKey ? ' active' : ''}${collapsed ? ' nav-item-collapsed' : ''}${unavailable ? ' nav-item-unavailable' : ''}`}
|
className={`nav-item${activeApp === item.appKey ? ' active' : ''}${collapsed ? ' nav-item-collapsed' : ''}${unavailable ? ' nav-item-unavailable' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (unavailable) return
|
|
||||||
onAppChange(item.appKey)
|
onAppChange(item.appKey)
|
||||||
}}
|
}}
|
||||||
title={collapsed ? item.displayName : undefined}
|
title={collapsed ? item.displayName : undefined}
|
||||||
|
|||||||
@@ -213,11 +213,11 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
|
|||||||
if (cancelled || !payload?.apps?.length) return
|
if (cancelled || !payload?.apps?.length) return
|
||||||
setApps(payload.apps)
|
setApps(payload.apps)
|
||||||
const currentApp = payload.apps.find((app) => app.appKey === activeApp)
|
const currentApp = payload.apps.find((app) => app.appKey === activeApp)
|
||||||
const currentAppAvailable = currentApp?.canLaunch !== false && currentApp?.canView !== false
|
const currentAppAvailable = currentApp?.canView !== false
|
||||||
const firstAvailableApp = payload.apps.find((app) => app.canView !== false && app.canLaunch !== false)
|
const firstAvailableApp = payload.apps.find((app) => app.canView !== false)
|
||||||
const lastLaunchedAppKey = window.localStorage.getItem(LAST_LAUNCHED_APP_STORAGE_KEY)
|
const lastLaunchedAppKey = window.localStorage.getItem(LAST_LAUNCHED_APP_STORAGE_KEY)
|
||||||
const lastLaunchedApp = lastLaunchedAppKey
|
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
|
: undefined
|
||||||
|
|
||||||
if (!initialAppSelectionDoneRef.current) {
|
if (!initialAppSelectionDoneRef.current) {
|
||||||
@@ -286,8 +286,9 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
|
|||||||
installation = { installed: false, version: null, path: null }
|
installation = { installed: false, version: null, path: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_DEV && appConfig.devAppPath && installation.installed) {
|
if (IS_DEV && appConfig.devAppPath) {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
|
setInstalledOverrides((prev) => prev[activeApp] === undefined ? prev : { ...prev, [activeApp]: true })
|
||||||
setAppStatus({
|
setAppStatus({
|
||||||
appKey: activeApp,
|
appKey: activeApp,
|
||||||
displayName: appConfig.displayName,
|
displayName: appConfig.displayName,
|
||||||
@@ -688,7 +689,6 @@ function Dashboard({ authSession, onSessionUpdate, onLogout }: DashboardProps):
|
|||||||
<span className="notice-dot" />
|
<span className="notice-dot" />
|
||||||
<span className="notice-label">공지사항</span>
|
<span className="notice-label">공지사항</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[11px] text-[var(--text-muted)]">{appNotices.length}건</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{appNotices.map((n, i) => (
|
{appNotices.map((n, i) => (
|
||||||
|
|||||||
@@ -329,6 +329,31 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const retryTailscaleAndContinue = async (): Promise<void> => {
|
||||||
|
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<void> => {
|
const installTailscaleAndContinue = async (): Promise<void> => {
|
||||||
if (!pendingTailscaleConfirm || !window.launcherTailscale?.install) return
|
if (!pendingTailscaleConfirm || !window.launcherTailscale?.install) return
|
||||||
const pending = pendingTailscaleConfirm
|
const pending = pendingTailscaleConfirm
|
||||||
@@ -452,7 +477,7 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
|
|||||||
<p className="mt-2 text-[12px] font-medium leading-relaxed text-white/62">
|
<p className="mt-2 text-[12px] font-medium leading-relaxed text-white/62">
|
||||||
{pendingTailscaleConfirm.reason === 'install-required'
|
{pendingTailscaleConfirm.reason === 'install-required'
|
||||||
? 'Autonomy Studio는 플랫폼 연결을 위해 Tailscale을 사용합니다. 설치를 진행하려면 관리자 권한이 필요합니다.'
|
? 'Autonomy Studio는 플랫폼 연결을 위해 Tailscale을 사용합니다. 설치를 진행하려면 관리자 권한이 필요합니다.'
|
||||||
: '플랫폼 원격 제어 연결을 설정하지 못했습니다. 로그인은 계속 진행할 수 있지만, 원격 플랫폼 연결 기능은 제한될 수 있습니다.'}
|
: 'Tailscale은 설치되어 있지만 연결 정보를 가져오거나 서버에 접속하는 과정에서 실패했습니다. 잠시 후 다시 시도할 수 있습니다.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-5 py-4">
|
<div className="px-5 py-4">
|
||||||
@@ -474,17 +499,18 @@ function LoginForm({ onLogin, onGoSignup, onGoPasswordReset, onLoadingChange }:
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => void installTailscaleAndContinue()}
|
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"
|
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 ? '설치 중...' : '설치'}
|
{tailscaleInstalling ? '설치 중...' : '설치'}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => void continueWithoutTailscale()}
|
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]"
|
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 ? '재시도 중...' : '다시 시도'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user