Skip to content
Snippets Groups Projects
Commit 24078f2b authored by Katarína Platková's avatar Katarína Platková
Browse files

Merge branch 'editor-init' into 'dp-platkova'

Editor db init

See merge request inject/frontend!258
parents 810de2c0 9c085ccf
No related branches found
No related tags found
No related merge requests found
Showing
with 319 additions and 7 deletions
......@@ -50,6 +50,8 @@
"d3-selection": "^3.0.0",
"d3-time": "^3.1.0",
"d3-time-format": "^4.1.0",
"dexie": "^4.0.7",
"dexie-react-hooks": "^1.1.7",
"lodash": "4.17.21",
"normalize.css": "8.0.1",
"react": "18.2.0",
......
import { Button } from '@blueprintjs/core'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import type { FC } from 'react'
import { memo, useCallback } from 'react'
import { deleteLearningObjective } from '../indexeddb/operations'
import type { LearningObjectiveInfo } from '../indexeddb/types'
interface LearningObjectiveProps {
objective: LearningObjectiveInfo
}
const LearningObjectiveItem: FC<LearningObjectiveProps> = ({ objective }) => {
const { notify } = useNotifyContext()
const handleDeleteButton = useCallback(
async (objective: LearningObjectiveInfo) => {
try {
await deleteLearningObjective(objective.id)
} catch (err) {
notify(
`Failed to delete learning objective '${objective.name}': ${err}`,
{
intent: 'danger',
}
)
}
},
[notify]
)
return (
<div>
{objective.name}{' '}
<Button
type='button'
icon='trash'
onClick={() => handleDeleteButton(objective)}
/>
</div>
)
}
export default memo(LearningObjectiveItem)
import LearningObjectiveItem from '@/editor/LearningObjectives/LearningObjective'
import { db } from '@/editor/indexeddb/db'
import type { LearningObjectiveInfo } from '@/editor/indexeddb/types'
import { useLiveQuery } from 'dexie-react-hooks'
import { memo } from 'react'
const LearningObjectives = () => {
const learningObjectives = useLiveQuery(
() => db.learningObjectives.toArray(),
[],
[]
)
return (
<>
{learningObjectives?.map((objective: LearningObjectiveInfo) => (
<LearningObjectiveItem key={objective.id} objective={objective} />
))}
</>
)
}
export default memo(LearningObjectives)
import { addLearningObjective } from '@/editor/indexeddb/operations'
import { Button, InputGroup } from '@blueprintjs/core'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import { memo, useCallback, useRef } from 'react'
const LearningObjectivesForm = () => {
const nameRef = useRef<HTMLInputElement>(null)
const { notify } = useNotifyContext()
const handleAddButton = useCallback(
async (name: string) => {
try {
await addLearningObjective({ name })
if (nameRef.current) {
nameRef.current.value = ''
}
} catch (err) {
notify(`Failed to add learning objective '${name}': ${err}`, {
intent: 'danger',
})
}
},
[notify]
)
return (
<>
<InputGroup placeholder='Name' inputRef={nameRef} />
<Button
type='button'
onClick={() => handleAddButton(nameRef.current?.value || '')}
>
Add
</Button>
</>
)
}
export default memo(LearningObjectivesForm)
import type { Path } from '@/router'
import { useNavigate } from '@/router'
import { Button } from '@blueprintjs/core'
import type { FC } from 'react'
interface NavbarButtonProps {
path: Path
name: string
}
const NavbarButton: FC<NavbarButtonProps> = ({ path, name }) => {
const nav = useNavigate()
return (
<Button type='button' onClick={() => nav(path)} alignText='left' minimal>
{name}
</Button>
)
}
export default NavbarButton
import NavbarButton from './NavbarButton'
const Navbar = () => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<NavbarButton path='/editor/create/participants' name='Participants' />
<NavbarButton
path='/editor/create/learning-objectives'
name='Learning objectives'
/>
<NavbarButton path='/editor/create/injects' name='Injects' />
</div>
)
export default Navbar
import Dexie, { type EntityTable } from 'dexie'
import type { LearningObjectiveInfo } from './types'
const dbName = 'EditorDatabase'
const dbVersion = 1
const db = new Dexie(dbName) as Dexie & {
learningObjectives: EntityTable<LearningObjectiveInfo, 'id'>
}
db.version(dbVersion).stores({
learningObjectives: '++id, &name',
})
export { db }
import { db } from './db'
import type { LearningObjectiveInfo } from './types'
// learning objectives operations
export const addLearningObjective = async (
objective: Omit<LearningObjectiveInfo, 'id'>
) =>
await db.transaction('rw', db.learningObjectives, async () => {
await db.learningObjectives.add(objective)
})
export const deleteLearningObjective = async (id: string) =>
await db.learningObjectives.delete(id)
import type { LearningObjective } from '@inject/graphql/fragments/LearningObjective.generated'
export type LearningObjectiveInfo = Pick<LearningObjective, 'id' | 'name'>
......@@ -3,11 +3,8 @@ import { Button, InputGroup, NonIdealState, Spinner } from '@blueprintjs/core'
import { css } from '@emotion/css'
import useApolloClient from '@inject/graphql/client/useApolloClient'
import { useLogin } from '@inject/graphql/mutations/Login.generated'
import type {
Identity} from '@inject/graphql/queries/Identity.generated';
import {
IdentityDocument,
} from '@inject/graphql/queries/Identity.generated'
import type { Identity } from '@inject/graphql/queries/Identity.generated'
import { IdentityDocument } from '@inject/graphql/queries/Identity.generated'
import type { MutableRefObject } from 'react'
import { useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
......
......@@ -2,8 +2,10 @@ import { Suspense, lazy } from 'react'
const GraphiQLPage = lazy(() => import('@/logic/GraphiQL'))
export const GraphiQL = () => <Suspense>
export const GraphiQL = () => (
<Suspense>
<GraphiQLPage />
</Suspense>
</Suspense>
)
export default GraphiQL
import { useSetPageTitle } from '@/utils'
import { Outlet } from 'react-router-dom'
const Layout = () => {
useSetPageTitle('Editor')
return <Outlet />
}
export default Layout
import { useSetPageTitle } from '@/utils'
import EditorView from '@/views/EditorView'
import { Outlet } from 'react-router-dom'
const Layout = () => {
useSetPageTitle('Editor - create definition')
return (
<EditorView>
<Outlet />
</EditorView>
)
}
export default Layout
import LearningObjectives from '@/editor/LearningObjectives'
import LearningObjectivesForm from '@/editor/LearningObjectivesForm'
import { memo } from 'react'
const LearningObjectivesPage = () => (
<>
<h1>Expected outcomes</h1>
<p>Description.</p>
<LearningObjectivesForm />
<LearningObjectives />
</>
)
export default memo(LearningObjectivesPage)
import InjectLogo from '@/assets/inject-logo--vertical-black.svg?react'
import { useNavigate } from '@/router'
import { useSetPageTitle } from '@/utils'
import { Button } from '@blueprintjs/core'
import { css } from '@emotion/css'
import Container from '@inject/shared/components/Container'
const introduction = css`
display: flex;
flex-direction: column;
text-align: center;
gap: 1rem;
margin: 0 auto;
max-width: 200px;
width: 100%;
`
const EditorIndexPage = () => {
useSetPageTitle('Editor')
const nav = useNavigate()
return (
<Container makeFullHeight>
<InjectLogo
style={{
width: '100%',
height: '200px',
margin: 'auto',
}}
/>
<div className={introduction}>
<h1>Editor</h1>
<p>Placeholder.</p>
<Button
type='button'
intent='primary'
icon='plus'
onClick={() => nav('/editor/create/introduction')}
>
Create
</Button>
</div>
</Container>
)
}
export default EditorIndexPage
......@@ -12,6 +12,11 @@ export type Path =
| `/analyst/:exerciseId/emails/:tab/:threadId`
| `/analyst/:exerciseId/milestones`
| `/analyst/:exerciseId/tools`
| `/editor`
| `/editor/create/injects`
| `/editor/create/introduction`
| `/editor/create/learning-objectives`
| `/editor/create/participants`
| `/exercise-panel`
| `/graphiql`
| `/instructor`
......
import ExitButton from '@/components/ExitButton'
import type { Section } from '@/components/Sidebar'
import Sidebar from '@/components/Sidebar'
import useHideButton from '@/components/Sidebar/useHideButton'
import Navbar from '@/editor/Navbar'
import NotificationDropdown from '@inject/shared/notification/NotificationDropdown'
import type { FC, PropsWithChildren } from 'react'
import { memo, useMemo } from 'react'
const EditorView: FC<PropsWithChildren> = ({ children }) => {
const { hide: hideLeftBar, node: hideButton } = useHideButton()
const sections: Section[] = useMemo(
() => [
{
name: 'Options',
node: (
<>
{hideButton}
<NotificationDropdown hideLabel={hideLeftBar} fill />
<ExitButton hideLabel={hideLeftBar} />
</>
),
},
{
name: 'Steps',
node: !hideLeftBar && <Navbar />,
},
],
[hideButton, hideLeftBar]
)
return (
<>
<div style={{ height: '100%', display: 'flex' }}>
<Sidebar
position='left'
sections={sections}
hideNames={hideLeftBar}
showLogo
/>
<div style={{ overflow: 'auto', flex: 1 }}>{children}</div>
</div>
</>
)
}
export default memo(EditorView)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment