Skip to content

Commit

Permalink
Merge branch 'feat/external-assessment-link-as-a-trigger'
Browse files Browse the repository at this point in the history
  • Loading branch information
Logos50607 committed Jan 14, 2025
2 parents 79acf32 + 5cfa7d1 commit bccc64b
Show file tree
Hide file tree
Showing 18 changed files with 2,009 additions and 789 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
"react-grid-layout": "^1.3.4",
"react-hook-form": "^6.11.0",
"react-hot-loader": "^4.13.0",
"react-icons": "^5.4.0",
"react-intl": "5.20.9",
"react-intl-translations-manager": "^5.0.3",
"react-is": "^17.0.2",
Expand Down
257 changes: 257 additions & 0 deletions src/components/program/ExternalLinkAdminModalBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import { CloseOutlined, MoreOutlined, PlusOutlined } from '@ant-design/icons'
import { Flex } from '@chakra-ui/react'
import { Button, Dropdown, Form, Input, InputNumber, Menu, message, Modal, Radio, Select, Space } from 'antd'
import { useForm } from 'antd/lib/form/Form'
import { useApp } from 'lodestar-app-element/src/contexts/AppContext'
import moment, { Moment } from 'moment'
import { useState } from 'react'
import { useIntl } from 'react-intl'
import { DeepPick } from 'ts-deep-pick'
import { handleError } from '../../helpers'
import { commonMessages } from '../../helpers/translation'
import { useMutateProgramContent } from '../../hooks/program'
import { ProgramContent } from '../../types/program'
import DisplayModeSelector, { DisplayMode } from './DisplayModeSelector'
import programMessages from './translation'

type FieldProps = {
title: string
link: {
type: string
url: string
}[]
ratio: number
isOn: boolean
displayMode: DisplayMode
publishedAt: Moment
assessmentId: string
}

const linkMap = {
accessLinks: 'test',
}

const transformParameters = (parameters: { [key: string]: [] }, linkMap: { [key: string]: string }) => {
if (!parameters) return
const result = []
for (const [key, values] of Object.entries(parameters)) {
if (linkMap[key] && Array.isArray(values)) {
result.push(...values.map(value => ({ type: linkMap[key], url: value })))
}
}
return result || []
}

const ExternalLinkAdminModalBlock: React.FC<{
programContent: DeepPick<ProgramContent, '!videos'> &
DeepPick<ProgramContent, 'videos.[].id' | 'videos.[].size' | 'videos.[].duration' | 'videos.[].options'>
displayMode: DisplayMode
programContentId: string
onDisplayModeChange: (displayMode: DisplayMode) => void
onRefetch?: () => void
onClose: () => void
}> = ({ programContent, displayMode, programContentId, onDisplayModeChange, onClose, onRefetch }) => {
const { formatMessage } = useIntl()
const { settings } = useApp()
const [loading, setLoading] = useState(false)
const { updateProgramContent, updateProgramContentBody, deleteProgramContent } = useMutateProgramContent()
const [form] = useForm()

let linkTypeOptions: { id: string; name: string }[] = []
if (!!settings['trigger.program_content']) {
try {
const triggerProgramContentSettings: {
name: string
display: string
parameters: { name: string; type: string; display: string }[]
testTypeOptions: { id: string; name: string }[]
}[] = JSON.parse(settings['trigger.program_content'])
linkTypeOptions =
triggerProgramContentSettings.find(item => item.name === 'externalTestRequirement')?.testTypeOptions || []
} catch (err) {
console.error(err)
}
}

const transformedData = transformParameters(programContent?.programContentBody?.data?.parameters, linkMap)

const handleSubmit = async (values: FieldProps) => {
setLoading(true)
try {
await updateProgramContent({
variables: {
programContentId,
programContentBodyId: programContent?.programContentBody.id,
title: values.title || '',
isNotifyUpdate: false,
pinnedStatus: false,
displayMode: values.displayMode,
publishedAt: values.publishedAt
? values.publishedAt.toDate()
: values.displayMode !== 'conceal'
? new Date()
: null,
},
})
await updateProgramContentBody({
variables: {
programContentId,
data: {
trigger: 'externalTestRequirement',
parameters: {
assessmentId: values.assessmentId,
accessLinks: values.link.filter(item => item.type === 'test').map(item => item.url),
programPackageCompleteRatio: values.ratio,
},
isOn: values.isOn,
},
type: 'link',
},
})
onRefetch?.()
setLoading(false)
onClose()
form.resetFields()
message.success(formatMessage(commonMessages.event.successfullySaved))
Modal.destroyAll()
} catch (err) {
console.log(err)
}
}

return (
<>
<Form
form={form}
layout="vertical"
initialValues={{
title: programContent.title || '',
assessmentId: programContent?.programContentBody?.data?.parameters?.assessmentId,
displayMode: programContent.displayMode,
isOn: programContent?.programContentBody?.data?.isOn ?? true,
ratio: programContent?.programContentBody?.data?.parameters?.programPackageCompleteRatio || 0,
publishedAt: programContent.publishedAt ? moment(programContent.publishedAt) : moment().startOf('minute'),
link: transformedData || [{ amount: 1 }],
}}
onFinish={handleSubmit}
>
<Flex
alignItems={{ base: 'flex-end', md: 'center' }}
justifyContent="space-between"
marginBottom="16px"
flexDirection={{ base: 'column-reverse', md: 'row' }}
>
<Flex flexWrap="wrap" gridGap="2">
{programContent.displayMode && (
<DisplayModeSelector
contentType="exam"
displayMode={displayMode}
onDisplayModeChange={onDisplayModeChange}
/>
)}
</Flex>
<Flex alignItems="center" marginBottom={{ base: '12px', md: '0' }}>
<Button
onClick={() => {
form.resetFields()
onClose()
Modal.destroyAll()
}}
className="mr-2"
>
{formatMessage(programMessages['*'].cancel)}
</Button>
<Button type="primary" htmlType="submit" className="mr-2" loading={!!loading}>
{formatMessage(programMessages['*'].save)}
</Button>
<Dropdown
trigger={['click']}
placement="bottomRight"
overlayStyle={{ zIndex: 9999 }}
overlay={
<Menu>
<Menu.Item
onClick={() => {
window.confirm(formatMessage(programMessages.ProgramContentAdminModal.deleteContentWarning)) &&
deleteProgramContent({ variables: { programContentId: programContentId } })
.then(() => {
onRefetch?.()
})
.catch(err => handleError(err))
}}
>
{formatMessage(programMessages['*'].deleteContent)}
</Menu.Item>
</Menu>
}
>
<MoreOutlined />
</Dropdown>
</Flex>
</Flex>

<Form.Item label={formatMessage(programMessages.ExternalLinkForm.title)} name="title">
<Input />
</Form.Item>
<Form.Item label={formatMessage(programMessages.ExternalLinkForm.examLink)} name="assessmentId">
<Input />
</Form.Item>
<Form.Item label={formatMessage(programMessages.ExternalLinkForm.links)}>
<Form.List name="link">
{(fields, { add, remove }) => (
<div>
{fields.map(field => (
<Space key={field.key} className="d-flex align-items-center justify-content-start">
<Form.Item
label={field.key === 0 ? formatMessage(programMessages.ExternalLinkForm.typeLabel) : undefined}
name={[field.name, 'type']}
fieldKey={[field.key, 'type']}
>
{linkTypeOptions.length > 0 && (
<Select style={{ width: '200px' }}>
{linkTypeOptions.map(option => (
<Select.Option value={option.id} key={option.id}>
{option.name}
</Select.Option>
))}
</Select>
)}
</Form.Item>
<Form.Item
label={field.key === 0 ? formatMessage(programMessages.ExternalLinkForm.linkLabel) : undefined}
name={[field.name, 'url']}
fieldKey={[field.key, 'url']}
>
<Input />
</Form.Item>

{fields.length > 1 && (
<div className={field.key === 0 ? 'mt-2 ml-2' : 'mb-4 ml-2'}>
<CloseOutlined className="cursor-pointer" onClick={() => remove(field.name)} />
</div>
)}
</Space>
))}

<Button icon={<PlusOutlined />} onClick={() => add({ amount: 1 })} disabled={!!loading}>
{formatMessage(programMessages['*'].add)}
</Button>
</div>
)}
</Form.List>
</Form.Item>
<Form.Item label={formatMessage(programMessages.ExternalLinkForm.programPackageCompleteRatio)} name="ratio">
<InputNumber min={0} max={100} formatter={value => `${value}%`} />
</Form.Item>
<Form.Item label={formatMessage(programMessages.ExternalLinkForm.isOn)} name="isOn">
<Radio.Group>
<Radio value={true}>{formatMessage(programMessages['*'].on)}</Radio>
<Radio value={false}>{formatMessage(programMessages['*'].off)}</Radio>
</Radio.Group>
</Form.Item>
</Form>
</>
)
}

export default ExternalLinkAdminModalBlock
Loading

0 comments on commit bccc64b

Please sign in to comment.