remove topics..

This commit is contained in:
2026-08-04 11:20:33 +09:00
parent 99cfef1004
commit b98339d299
5 changed files with 43 additions and 32 deletions
+24 -8
View File
@@ -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: '-'