| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- import { View, Row } from 'app/design/view'
- import { Text } from 'app/design/typography'
- import DropdownMenu from 'app/ui/atoms/dropdown-menu';
- import { NeoButton } from 'app/design/controls'
- import Link from 'app/ui/atoms/link'
- import Card from 'app/ui/molecules/page/card'
- import Progress from 'app/ui/atoms/progress'
- import Scroll from 'app/ui/molecules/page/scroll'
- import { memo } from 'react'
- import { useReducer } from 'react'
- import { fetcher } from 'app/lib/fetcher'
- import { BlockByDataInt as BlockByData } from 'app/components/block';
- import { useBottomSheetData } from 'app/context/bottomsheet';
- import { DragContext, DragItem, DragControl } from 'app/ui/molecules/form-controls/droppable'
- import { useTranslation } from 'react-i18next'
- function CourseStructure({ data }) {
- const { t } = useTranslation();
- const { setBottomSheetData } = useBottomSheetData();
- const isEditable = data.isEditable;
- const courseId = data.entry_id;
- const initialState = {
- action: null,
- modal: null,
- data: data,
- };
- const [state, dispatch] = useReducer(reducer, initialState);
- function reducer(state, action) {
- switch (action.type) {
- case 'SET_DATA':
- return {
- ...state,
- action: null,
- modal: null,
- data: action.data
- };
- default:
- return state;
- }
- }
- // Plain functions: only used from handlers/JSX, so identity does not matter
- // (React Compiler memoizes them). Declared in dependency order.
- const resetData = async () => {
- const response = await fetcher(`/api.php?r=bx_courses/entity_structure_l1_block/¶ms[]=${courseId}`);
- dispatch({ type: 'SET_DATA', data: response.data[0].data });
- };
- const handleUpdate = () => {
- setTimeout(() => {
- setBottomSheetData(false);
- resetData();
- }, 100);
- };
- const handleAddModule = async (event) => {
- event?.preventDefault?.();
- const fetchedData = await fetcher(`/api.php?r=system/perfom_action_api/TemplServiceGrid/¶ms[]=&o=bx_courses_cnt_structure_manage&a=add&parent_id=0&entry_id=${courseId}`);
- const content = { content: fetchedData.data, designbox_id: 0 };
- setBottomSheetData({ title: content.content[0]?.title || " ", content: <View className='px-1'><BlockByData onFormEmpty={handleUpdate} block={content} /></View> });
- };
- const handleManage = async (item, id) => {
- if (item.action == "edit") {
- const fetchedData = await fetcher(`/api.php?r=system/perfom_action_api/TemplServiceGrid/¶ms[]=&o=bx_courses_cnt_structure_manage&a=edit&parent_id=0&entry_id=${courseId}&id=${id}`);
- const content = { content: fetchedData.data, designbox_id: 0 };
- setBottomSheetData({ title: content.content[0]?.title || " ", content: <View className='px-1'><BlockByData onFormEmpty={handleUpdate} block={content} /></View> });
- }
- if (item.action == "delete") {
- await fetcher(`/api.php?r=system/perfom_action_api/TemplServiceGrid/¶ms[]=&o=bx_courses_cnt_structure_manage&a=delete&parent_id=0&entry_id=${courseId}&ids[]=${id}`);
- resetData();
- }
- };
- const handleModulesSort = async (result) => {
- if (!result.destination) return;
- const updatedData = [...state.data.items];
- const [removed] = updatedData.splice(result.source.index, 1);
- updatedData.splice(result.destination.index, 0, removed);
- updatedData.forEach((item, index) => {
- item.order = index;
- });
- dispatch({ type: 'SET_DATA', data: { ...state.data, items: updatedData }});
- await fetcher(`/api.php?r=system/perfom_action_api/TemplServiceGrid/¶ms[]=&o=bx_courses_cnt_structure_manage&a=reorder&parent_id=0&entry_id=${courseId}&` + updatedData.map(item => `bx_courses_cnt_structure_manage_row[]=${item.id}`).join('&'));
- };
- const renderModuleItem = (index, item, dragHandleProps) => {
- let icon = "Check";
- let color = "gray-600";
- let colorButton = "emerald-400";
- let colorButtonText = "emerald-400";
- if (item.status == "in process") {
- icon = "Hourglass";
- color = "red-400";
- colorButton = "white";
- colorButtonText = "red-400";
- }
- if (item.status == "not started") {
- icon = "Bookmark";
- color = "gray-400";
- colorButton = "gray-500";
- colorButtonText = "white";
- }
- const manageMenu = [
- { title: t('Edit module'), action: "edit" },
- { title: t('Delete module'), action: "delete" },
- ];
- return (
- <View className='m-2 w-72' key={item.index}>
- <Card rounded=' rounded-none sm:rounded-2xl ' margin={'bg-' + color + ' max-w-screen-lg mx-auto w-full p-3 sm:p-4 mb-1 sm:mb-4 '}>
- <Link href={item.link}>
- <View className={`mb-2 bg-${color}`}>
- {!isEditable && <Progress value={item.percent} />}
- <Row className='justify-between'>
- <View className='my-2 text-xs '><Text className="text-white">{t('Module {{index}}', { index: item.index })}</Text></View>
- {isEditable && <Row className='items-center justify-center gap-x-2'><DropdownMenu items={manageMenu} onSelect={(oItem) => { handleManage(oItem, item.id) }} buttonProps={{ controlSize: 'small', borderShape: 'circle', image: 'Cog', accessibilityLabel: t('Settings') }} /><DragControl dragHandleProps={dragHandleProps} accessibilityLabel={t('Reorder')} className="u-neo-btn-link hit-area-4"><NeoButton controlSize="small" borderShape="circle" image="ArrowLeftRight" interactive /></DragControl></Row>}
- </Row>
- <View className='h-12'>
- <Text className="text-white text-lg leading-tight tracking-tight font-bold text-secondary-foreground " numberOfLines={2}>{item.title}</Text>
- </View>
- </View>
- <Row className='gap-x-2 items-end mt-4'>
- <NeoButton controlSize="mini" borderShape="capsule" image={icon} label={item.status} />
- {item?.counters?.map((item2, index) => {
- return (
- <NeoButton key={`cnt-${index}`} controlSize="mini" borderShape="capsule" label={`${item2.cn_progress} ${item2.cn_title}`} />
- )
- })}
- </Row>
- </Link>
- </Card>
- </View>
- )
- }
- return (
- <DragContext onSort={handleModulesSort} renderItem={renderModuleItem} direction='horizontal'>
- <Scroll horizontal={true} step={250} className='w-full'>
-
- {
- state.data.items && state.data.items.map((item, index) => {
- return (
- <DragItem data={item} index={index} key={index} renderItem={renderModuleItem} isDragEnabled={isEditable} />
- )
- })
- }
- {isEditable && (<View className='m-2 w-72'>
- <Card rounded=' rounded-none sm:rounded-2xl ' >
- <View className='h-40 items-center justify-center'>
- <NeoButton controlSize="small" borderShape="capsule" image="Plus" label={t('Add new')} classNames={{ root: 'self-center' }} onPress={(event) => { handleAddModule(event, courseId) }} />
- </View>
- </Card>
- </View>)
- }
-
- </Scroll></DragContext>)
- }
- export default memo(CourseStructure);
|