Initial commit: Learning Lab Express

Includes fixes found while reviewing the app:
- Results: open each result by a type+agent+id key, so a video and a
  material draft with the same id no longer open each other's popup
- AuthGate: clear the query cache during render, before pages build their
  queries, and remount pages on organization switch (pages no longer stay
  on skeletons for up to a minute after sign-in or refresh)
- Instruction: keep only failed rules after a partial approve, so retrying
  does not create the successful schedules twice; drop the monthly note
  once count or frequency is edited
- Parser/schedules: never schedule a count below one
- Audio: ignore the end event of a cancelled utterance when changing speed
- Downloads: revoke the object URL later so Safari/Firefox can read it
- Results: refetch SCORM content when any part's revision changes; show a
  failed video as failed instead of "still rendering"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iqbal
2026-10-23 10:42:12 +07:00
commit f219b26f71
41 changed files with 8172 additions and 0 deletions

49
src/main.tsx Normal file
View File

@@ -0,0 +1,49 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ClerkProvider } from '@clerk/react'
import { enUS, idID } from '@clerk/localizations'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { BrowserRouter } from 'react-router-dom'
import { EngineError } from './api/client'
import App from './App'
import { useLang } from './i18n'
import './styles/mockup.css'
import './styles/app.css'
import './styles/theme.css'
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!clerkPubKey) {
throw new Error('Missing VITE_CLERK_PUBLISHABLE_KEY (set it, or keep ../learning-lab/backend/.env available)')
}
const queryClient = new QueryClient({
defaultOptions: {
// Retrying a 4xx (auth, credits, validation) only repeats the same answer.
queries: { retry: (n, e) => n < 2 && !(e instanceof EngineError && e.status >= 400 && e.status < 500) },
},
})
// Clerk's sign-in and organization screens match the theme in styles/theme.css.
const clerkAppearance = {
variables: { colorPrimary: '#1570ef', colorText: '#101828', fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", borderRadius: '12px' },
}
// Clerk's own sign-in and organization screens follow the app language.
function Root() {
const lang = useLang()
return (
<ClerkProvider publishableKey={clerkPubKey} afterSignOutUrl="/" localization={lang === 'id' ? idID : enUS} appearance={clerkAppearance}>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<App />
</BrowserRouter>
</QueryClientProvider>
</ClerkProvider>
)
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Root />
</StrictMode>,
)