@@ -29,6 +29,7 @@ import {
2929 useMediaQuery ,
3030 useTheme ,
3131} from '@mui/material' ;
32+ import DownloadIcon from '@mui/icons-material/Download' ;
3233import dayjs from 'dayjs' ;
3334import customParseFormat from 'dayjs/plugin/customParseFormat' ;
3435import { useTranslation } from 'next-i18next' ;
@@ -208,6 +209,105 @@ const ImportCsv = () => {
208209 link . click ( ) ;
209210 document . body . removeChild ( link ) ;
210211 } ;
212+ const handleUploadCSVDownload = ( ) => {
213+
214+
215+ // Function to flatten tasks recursively
216+ const flattenTasks = ( taskList : any [ ] ) : any [ ] => {
217+ let result : any [ ] = [ ] ;
218+ taskList . forEach ( ( task ) => {
219+ result . push ( task ) ;
220+ if ( task . children && task . children . length > 0 ) {
221+ result = result . concat ( flattenTasks ( task . children ) ) ;
222+ }
223+ } ) ;
224+ return result ;
225+ } ;
226+
227+ // Flatten all tasks
228+ const allTasks = flattenTasks ( userProjectDetails ) ;
229+
230+ if ( allTasks . length === 0 ) {
231+ alert ( 'No data to export' ) ;
232+ return ;
233+ }
234+
235+ // Find max number of learning resources
236+ const maxLearningResources = Math . max (
237+ ...allTasks . map ( task => task . learningResources ?. length || 0 )
238+ ) ;
239+
240+ // Build header
241+ const baseHeaders = [ 'name' , 'externalId' , 'type' , 'hasAParentTask' , 'parentTaskId' ] ;
242+ const learningResourceHeaders : string [ ] = [ ] ;
243+
244+ for ( let i = 1 ; i <= maxLearningResources ; i ++ ) {
245+ learningResourceHeaders . push (
246+ `learningResources${ i } -name` ,
247+ `learningResources${ i } -type` ,
248+ `learningResources${ i } -id`
249+ ) ;
250+ }
251+
252+ const headers = [ ...baseHeaders , ...learningResourceHeaders , 'startDate' , 'endDate' ] ;
253+
254+ // Helper function to escape CSV values
255+ const escapeCSV = ( value : any ) : string => {
256+ if ( value === null || value === undefined ) return '' ;
257+ const stringValue = String ( value ) ;
258+ if ( stringValue . includes ( ',' ) || stringValue . includes ( '"' ) || stringValue . includes ( '\n' ) ) {
259+ return `"${ stringValue . replace ( / " / g, '""' ) } "` ;
260+ }
261+ return stringValue ;
262+ } ;
263+
264+ // Convert tasks to CSV rows
265+ const csvRows = allTasks . map ( ( task ) => {
266+ const row : any = {
267+ name : task . name || '' ,
268+ externalId : task . externalId || '' ,
269+ type : task . type || '' ,
270+ hasAParentTask : task . metaInformation ?. hasAParentTask || '' ,
271+ parentTaskId : task . metaInformation ?. parentTaskId || '' ,
272+ startDate : task . metaInformation ?. startDate || '' ,
273+ endDate : task . metaInformation ?. endDate || ''
274+ } ;
275+
276+ // Add learning resources
277+ if ( task . learningResources && task . learningResources . length > 0 ) {
278+ task . learningResources . forEach ( ( lr : any , index : number ) => {
279+ const num = index + 1 ;
280+ row [ `learningResources${ num } -name` ] = lr . name || '' ;
281+ row [ `learningResources${ num } -type` ] = lr . type || '' ;
282+ row [ `learningResources${ num } -id` ] = lr . id || '' ;
283+ } ) ;
284+ }
285+
286+ // Fill in empty learning resource columns
287+ for ( let i = ( task . learningResources ?. length || 0 ) + 1 ; i <= maxLearningResources ; i ++ ) {
288+ row [ `learningResources${ i } -name` ] = '' ;
289+ row [ `learningResources${ i } -type` ] = '' ;
290+ row [ `learningResources${ i } -id` ] = '' ;
291+ }
292+
293+ return headers . map ( header => escapeCSV ( row [ header ] ) ) . join ( ',' ) ;
294+ } ) ;
295+
296+ // Combine header and rows
297+ const csv = [ headers . join ( ',' ) , ...csvRows ] . join ( '\n' ) ;
298+
299+ // Create blob and download
300+ const blob = new Blob ( [ csv ] , { type : 'text/csv;charset=utf-8;' } ) ;
301+ const link = document . createElement ( 'a' ) ;
302+ const url = URL . createObjectURL ( blob ) ;
303+ link . setAttribute ( 'href' , url ) ;
304+ link . setAttribute ( 'download' , `${ subject } _${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .csv` ) ;
305+ link . style . visibility = 'hidden' ;
306+ document . body . appendChild ( link ) ;
307+ link . click ( ) ;
308+ document . body . removeChild ( link ) ;
309+ URL . revokeObjectURL ( url ) ;
310+ } ;
211311
212312 const handleUpload = async ( ) => {
213313 if ( selectedFile ) {
@@ -516,6 +616,22 @@ const ImportCsv = () => {
516616 { t ( 'COURSE_PLANNER.ADD_NEW_TOPIC' ) }
517617 </ Button >
518618 ) }
619+
620+
621+ { userProjectDetails ?. length > 0 && ( < Button
622+ variant = "contained"
623+ startIcon = { < DownloadIcon /> }
624+ onClick = { handleUploadCSVDownload }
625+ sx = { {
626+ textTransform : 'none' ,
627+ //// backgroundColor: '#1976d2',
628+ '&:hover' : {
629+ // backgroundColor: '#1565c0',
630+ }
631+ } }
632+ >
633+ { t ( 'COURSE_PLANNER.DOWNLOAD_CSV' ) }
634+ </ Button > ) }
519635 { /* <Button
520636 sx={{
521637 borderRadius: '8px',
0 commit comments