remove topics..
This commit is contained in:
@@ -778,15 +778,18 @@ function LocalPcWorkspaceMockup({
|
|||||||
const storedParameters = readStoredModuleParameters()
|
const storedParameters = readStoredModuleParameters()
|
||||||
return modules.map((module) => {
|
return modules.map((module) => {
|
||||||
const override = storedParameters[getModuleParameterStorageKey(module)]
|
const override = storedParameters[getModuleParameterStorageKey(module)]
|
||||||
if (!override) return module
|
const defaultParameters = runtimeModules.find((item) => item.folderName === module.folderName)?.defaultParameters
|
||||||
|
?.filter((parameter): parameter is string => typeof parameter === 'string') ?? []
|
||||||
return {
|
return {
|
||||||
...module,
|
...module,
|
||||||
parameters: serializeModuleParameters(override).split(/\s+/).filter(Boolean)
|
parameters: override
|
||||||
|
? serializeModuleParameters(override).split(/\s+/).filter(Boolean)
|
||||||
|
: defaultParameters.length ? defaultParameters : module.parameters
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [courseKey, weekNumber])
|
}, [courseKey, runtimeModules, weekNumber])
|
||||||
const curriculumModuleKey = runtimeModules
|
const curriculumModuleKey = runtimeModules
|
||||||
.map((module) => `${module.id}:${module.name}:${module.folderName}:${module.sortOrder}:${JSON.stringify(module.prebuiltArtifacts ?? [])}`)
|
.map((module) => `${module.id}:${module.name}:${module.folderName}:${module.sortOrder}:${JSON.stringify(module.prebuiltArtifacts ?? [])}:${JSON.stringify(module.defaultParameters ?? [])}`)
|
||||||
.join('|')
|
.join('|')
|
||||||
const {
|
const {
|
||||||
availableModules,
|
availableModules,
|
||||||
@@ -2929,6 +2932,7 @@ export default function App(): React.JSX.Element {
|
|||||||
const connectedPlatformRef = useRef<PlatformOption | null>(null)
|
const connectedPlatformRef = useRef<PlatformOption | null>(null)
|
||||||
const runningPlatformsRef = useRef<PlatformOption[]>([])
|
const runningPlatformsRef = useRef<PlatformOption[]>([])
|
||||||
const platformStatusRequestSequenceRef = useRef(0)
|
const platformStatusRequestSequenceRef = useRef(0)
|
||||||
|
const [checkedRemotePlatformStatusIds, setCheckedRemotePlatformStatusIds] = useState<Set<string>>(() => new Set())
|
||||||
const forcedPlatformTerminationHandlerRef = useRef<(payload: PlatformControlTerminatedPayload) => void>(() => undefined)
|
const forcedPlatformTerminationHandlerRef = useRef<(payload: PlatformControlTerminatedPayload) => void>(() => undefined)
|
||||||
const [workspaceWeekState, setWorkspaceWeekState] = useState<CurriculumWeek | null>(null)
|
const [workspaceWeekState, setWorkspaceWeekState] = useState<CurriculumWeek | null>(null)
|
||||||
const [sessionConfirmAction, setSessionConfirmAction] = useState<'disconnect' | 'release' | null>(null)
|
const [sessionConfirmAction, setSessionConfirmAction] = useState<'disconnect' | 'release' | null>(null)
|
||||||
@@ -3329,6 +3333,11 @@ export default function App(): React.JSX.Element {
|
|||||||
return status ? { ...platform, ...status } : platform
|
return status ? { ...platform, ...status } : platform
|
||||||
}
|
}
|
||||||
setPlatformOptions((current) => current.map(mergePlatformStatus))
|
setPlatformOptions((current) => current.map(mergePlatformStatus))
|
||||||
|
setCheckedRemotePlatformStatusIds((current) => {
|
||||||
|
const next = new Set(current)
|
||||||
|
platforms.forEach((platform) => next.add(platform.id))
|
||||||
|
return next
|
||||||
|
})
|
||||||
const lostControlPlatformIds = new Set(
|
const lostControlPlatformIds = new Set(
|
||||||
runningPlatformsRef.current
|
runningPlatformsRef.current
|
||||||
.filter((platform) => {
|
.filter((platform) => {
|
||||||
@@ -3499,15 +3508,19 @@ export default function App(): React.JSX.Element {
|
|||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [authReady, authSession?.accessToken, fetchWithFreshAuth])
|
}, [authReady, authSession?.accessToken, fetchWithFreshAuth])
|
||||||
|
const isRemotePlatformStatusPending = (platform: PlatformOption): boolean => (
|
||||||
|
platform.platformKey !== 'futuredrive-local-pc' && !checkedRemotePlatformStatusIds.has(platform.id)
|
||||||
|
)
|
||||||
const filteredPlatformOptions = platformOptions.filter((platform) => {
|
const filteredPlatformOptions = platformOptions.filter((platform) => {
|
||||||
if (platformFilter === 'available') return platform.state === 'ready'
|
const pending = isRemotePlatformStatusPending(platform)
|
||||||
if (platformFilter === 'unavailable') return platform.state !== 'ready'
|
if (platformFilter === 'available') return platform.state === 'ready' && !pending
|
||||||
|
if (platformFilter === 'unavailable') return platform.state !== 'ready' && !pending
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
const platformFilterCounts = {
|
const platformFilterCounts = {
|
||||||
all: platformOptions.length,
|
all: platformOptions.length,
|
||||||
available: platformOptions.filter((platform) => platform.state === 'ready').length,
|
available: platformOptions.filter((platform) => platform.state === 'ready' && !isRemotePlatformStatusPending(platform)).length,
|
||||||
unavailable: platformOptions.filter((platform) => platform.state !== 'ready').length
|
unavailable: platformOptions.filter((platform) => platform.state !== 'ready' && !isRemotePlatformStatusPending(platform)).length
|
||||||
}
|
}
|
||||||
const localPcPlatform = platformOptions.find((platform) => platform.platformKey === 'futuredrive-local-pc')
|
const localPcPlatform = platformOptions.find((platform) => platform.platformKey === 'futuredrive-local-pc')
|
||||||
const openPlatformSettings = (platform: PlatformOption): void => {
|
const openPlatformSettings = (platform: PlatformOption): void => {
|
||||||
@@ -3992,6 +4005,8 @@ export default function App(): React.JSX.Element {
|
|||||||
setLocalPcLayoutOpen(false)
|
setLocalPcLayoutOpen(false)
|
||||||
setRecentSessionModalOpen(false)
|
setRecentSessionModalOpen(false)
|
||||||
setPlatformModalMode('select')
|
setPlatformModalMode('select')
|
||||||
|
platformStatusRequestSequenceRef.current += 1
|
||||||
|
setCheckedRemotePlatformStatusIds(new Set())
|
||||||
await loadCurriculum()
|
await loadCurriculum()
|
||||||
setPlatformModalOpen(true)
|
setPlatformModalOpen(true)
|
||||||
}
|
}
|
||||||
@@ -4507,6 +4522,7 @@ export default function App(): React.JSX.Element {
|
|||||||
counts={platformFilterCounts}
|
counts={platformFilterCounts}
|
||||||
workspaceWeekTitle={workspaceWeek.title}
|
workspaceWeekTitle={workspaceWeek.title}
|
||||||
platformLoading={platformLoading}
|
platformLoading={platformLoading}
|
||||||
|
checkedRemotePlatformStatusIds={checkedRemotePlatformStatusIds}
|
||||||
isPlatformUnavailable={isPlatformUnavailable}
|
isPlatformUnavailable={isPlatformUnavailable}
|
||||||
getPlatformDotClass={getPlatformDotClass}
|
getPlatformDotClass={getPlatformDotClass}
|
||||||
onFilterChange={setPlatformFilter}
|
onFilterChange={setPlatformFilter}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ type LearningInfoWeek = {
|
|||||||
title: string
|
title: string
|
||||||
tags: string[]
|
tags: string[]
|
||||||
progress: number
|
progress: number
|
||||||
topics: string[]
|
|
||||||
duration: string
|
duration: string
|
||||||
detail: {
|
detail: {
|
||||||
requiresSourceSubmission?: boolean
|
requiresSourceSubmission?: boolean
|
||||||
@@ -60,21 +59,7 @@ export default function LearningInfoCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-[17px] overflow-hidden rounded-lg border border-[#dfe3ed] bg-[#fbfcff]">
|
<div className="mb-[17px] mt-4 overflow-hidden rounded-lg border border-[#dfe3ed] bg-[#fbfcff]">
|
||||||
<div className="border-b border-[#edf0f5] px-3.5 py-3">
|
|
||||||
<span className="flex items-center gap-2 text-[12px] font-bold text-[#151827]">
|
|
||||||
<BookOpen size={14} className="text-[#6d7788]" />
|
|
||||||
학습 주제
|
|
||||||
</span>
|
|
||||||
<div className="mt-2 grid gap-1.5">
|
|
||||||
{week.topics.map((topic, index) => (
|
|
||||||
<div key={topic} className="flex items-center gap-2 rounded-md px-1 py-0.5 text-[12px] font-medium text-[#4f596b]">
|
|
||||||
<span className="grid h-5 w-5 shrink-0 place-items-center rounded bg-[#ede9ff] text-[10px] font-bold text-[#714ef6]">{index + 1}</span>
|
|
||||||
<span className="min-w-0 truncate">{topic}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{[
|
{[
|
||||||
{ label: '예상 학습 시간', value: week.duration, Icon: Clock3 },
|
{ label: '예상 학습 시간', value: week.duration, Icon: Clock3 },
|
||||||
...(week.detail.requiresSourceSubmission
|
...(week.detail.requiresSourceSubmission
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AlertCircle, CheckCircle2, Link2, Lock, Power, Settings } from 'lucide-react'
|
import { AlertCircle, CheckCircle2, Link2, LoaderCircle, Lock, Power, Settings } from 'lucide-react'
|
||||||
import type { LucideIcon } from 'lucide-react'
|
import type { LucideIcon } from 'lucide-react'
|
||||||
import type React from 'react'
|
import type React from 'react'
|
||||||
import type { PlatformOption } from './types'
|
import type { PlatformOption } from './types'
|
||||||
@@ -60,6 +60,7 @@ type PlatformOptionCardProps = {
|
|||||||
Icon: LucideIcon
|
Icon: LucideIcon
|
||||||
dotClassName: string
|
dotClassName: string
|
||||||
unavailable: boolean
|
unavailable: boolean
|
||||||
|
statusPending: boolean
|
||||||
displayStatus: string
|
displayStatus: string
|
||||||
primaryAction: string
|
primaryAction: string
|
||||||
actionDisabled: boolean
|
actionDisabled: boolean
|
||||||
@@ -73,6 +74,7 @@ export function PlatformOptionCard({
|
|||||||
Icon,
|
Icon,
|
||||||
dotClassName,
|
dotClassName,
|
||||||
unavailable,
|
unavailable,
|
||||||
|
statusPending,
|
||||||
displayStatus,
|
displayStatus,
|
||||||
primaryAction,
|
primaryAction,
|
||||||
actionDisabled,
|
actionDisabled,
|
||||||
@@ -106,7 +108,9 @@ export function PlatformOptionCard({
|
|||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-[12px] font-medium text-[#5f6878]">{platform.description}</p>
|
<p className="mt-1 text-[12px] font-medium text-[#5f6878]">{platform.description}</p>
|
||||||
<div className="mt-2 flex items-center gap-1.5 text-[11px] font-bold text-[#5f6878]">
|
<div className="mt-2 flex items-center gap-1.5 text-[11px] font-bold text-[#5f6878]">
|
||||||
{unavailable
|
{statusPending
|
||||||
|
? <LoaderCircle size={13} className="animate-spin text-[#7a879a]" />
|
||||||
|
: unavailable
|
||||||
? <AlertCircle size={13} className="text-[#dc2626]" />
|
? <AlertCircle size={13} className="text-[#dc2626]" />
|
||||||
: ready
|
: ready
|
||||||
? <CheckCircle2 size={13} className="text-[#2fbda0]" />
|
? <CheckCircle2 size={13} className="text-[#2fbda0]" />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type PlatformSelectionPanelProps = {
|
|||||||
counts: Record<PlatformFilter, number>
|
counts: Record<PlatformFilter, number>
|
||||||
workspaceWeekTitle: string
|
workspaceWeekTitle: string
|
||||||
platformLoading: boolean
|
platformLoading: boolean
|
||||||
|
checkedRemotePlatformStatusIds: ReadonlySet<string>
|
||||||
isPlatformUnavailable: (platform: PlatformOption) => boolean
|
isPlatformUnavailable: (platform: PlatformOption) => boolean
|
||||||
getPlatformDotClass: (platform: PlatformOption) => string
|
getPlatformDotClass: (platform: PlatformOption) => string
|
||||||
onFilterChange: (filter: PlatformFilter) => void
|
onFilterChange: (filter: PlatformFilter) => void
|
||||||
@@ -36,6 +37,7 @@ export default function PlatformSelectionPanel({
|
|||||||
counts,
|
counts,
|
||||||
workspaceWeekTitle,
|
workspaceWeekTitle,
|
||||||
platformLoading,
|
platformLoading,
|
||||||
|
checkedRemotePlatformStatusIds,
|
||||||
isPlatformUnavailable,
|
isPlatformUnavailable,
|
||||||
getPlatformDotClass,
|
getPlatformDotClass,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
@@ -81,14 +83,17 @@ export default function PlatformSelectionPanel({
|
|||||||
const { id, status, action, state, iconKey } = platform
|
const { id, status, action, state, iconKey } = platform
|
||||||
const Icon = PLATFORM_ICONS[iconKey] ?? Monitor
|
const Icon = PLATFORM_ICONS[iconKey] ?? Monitor
|
||||||
const locked = state === 'locked'
|
const locked = state === 'locked'
|
||||||
const unavailable = isPlatformUnavailable(platform)
|
const statusPending = platform.platformKey !== 'futuredrive-local-pc' && !checkedRemotePlatformStatusIds.has(id)
|
||||||
const actionDisabled = locked || unavailable
|
const unavailable = !statusPending && isPlatformUnavailable(platform)
|
||||||
|
const actionDisabled = statusPending || locked || unavailable
|
||||||
const runningInSelectionList = runningPlatforms.some((runningPlatform) => runningPlatform.id === platform.id)
|
const runningInSelectionList = runningPlatforms.some((runningPlatform) => runningPlatform.id === platform.id)
|
||||||
const displayStatus = unavailable && !status.includes('연결 끊김') && !status.includes('연결 불가')
|
const displayStatus = statusPending
|
||||||
|
? '확인 중'
|
||||||
|
: unavailable && !status.includes('연결 끊김') && !status.includes('연결 불가')
|
||||||
? '연결 끊김'
|
? '연결 끊김'
|
||||||
: status
|
: status
|
||||||
const hasControl = platform.control?.status === 'ACTIVE'
|
const hasControl = platform.control?.status === 'ACTIVE'
|
||||||
const primaryAction = hasControl ? '연결' : action
|
const primaryAction = statusPending ? '확인 중' : hasControl ? '연결' : action
|
||||||
const platformActionDisabled = runningInSelectionList || actionDisabled
|
const platformActionDisabled = runningInSelectionList || actionDisabled
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -96,8 +101,9 @@ export default function PlatformSelectionPanel({
|
|||||||
key={id}
|
key={id}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
Icon={Icon}
|
Icon={Icon}
|
||||||
dotClassName={getPlatformDotClass(platform)}
|
dotClassName={statusPending ? 'bg-[#94a3b8] shadow-[0_0_6px_rgba(148,163,184,0.45)]' : getPlatformDotClass(platform)}
|
||||||
unavailable={unavailable}
|
unavailable={unavailable}
|
||||||
|
statusPending={statusPending}
|
||||||
displayStatus={displayStatus}
|
displayStatus={displayStatus}
|
||||||
primaryAction={primaryAction}
|
primaryAction={primaryAction}
|
||||||
actionDisabled={platformActionDisabled}
|
actionDisabled={platformActionDisabled}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function makeUninstalledModuleRows(curriculumModules: CurriculumWeek['mod
|
|||||||
templateRepositoryUrl: module.templateRepositoryUrl ?? null,
|
templateRepositoryUrl: module.templateRepositoryUrl ?? null,
|
||||||
sourceType: module.sourceType ?? null,
|
sourceType: module.sourceType ?? null,
|
||||||
prebuiltArtifacts: module.prebuiltArtifacts ?? null,
|
prebuiltArtifacts: module.prebuiltArtifacts ?? null,
|
||||||
parameters: [],
|
parameters: module.defaultParameters?.filter((parameter): parameter is string => typeof parameter === 'string') ?? [],
|
||||||
cpu: null,
|
cpu: null,
|
||||||
memory: null,
|
memory: null,
|
||||||
runtime: '-'
|
runtime: '-'
|
||||||
|
|||||||
Reference in New Issue
Block a user