1+ import React , { useState , useEffect } from 'react' ;
2+ import axiosInstance from './../../../utils/axiosInstance' ; // Using the custom axios instance
3+ import {
4+ CButton ,
5+ CForm ,
6+ CFormInput ,
7+ CFormLabel ,
8+ CCard ,
9+ CCardBody ,
10+ CCardHeader ,
11+ CAlert ,
12+ CFormSelect
13+ } from '@coreui/react' ;
14+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
15+ import { faFolderOpen , faClock , faPlay } from '@fortawesome/free-solid-svg-icons' ;
16+
17+ const BackupManagerFrontend = ( ) => {
18+ const [ backupDir , setBackupDir ] = useState ( '' ) ;
19+ const [ hours , setHours ] = useState ( '02' ) ;
20+ const [ minutes , setMinutes ] = useState ( '00' ) ;
21+ const [ ampm , setAmPm ] = useState ( 'AM' ) ;
22+ const [ feedback , setFeedback ] = useState ( { message : '' , type : '' } ) ;
23+ const [ isLoading , setIsLoading ] = useState ( false ) ;
24+
25+ // Clear feedback message after 5 seconds
26+ useEffect ( ( ) => {
27+ if ( feedback . message ) {
28+ const timer = setTimeout ( ( ) => {
29+ setFeedback ( { message : '' , type : '' } ) ;
30+ } , 5000 ) ;
31+ return ( ) => clearTimeout ( timer ) ;
32+ }
33+ } , [ feedback ] ) ;
34+
35+ // Fetch saved configuration on component mount
36+ useEffect ( ( ) => {
37+ const fetchConfig = async ( ) => {
38+ try {
39+ setIsLoading ( true ) ;
40+ const response = await axiosInstance . get ( '/backupauto/config' ) ;
41+ const config = response . data ;
42+
43+ // Set backup directory
44+ if ( config . backupDir ) {
45+ setBackupDir ( config . backupDir ) ;
46+ }
47+
48+ // Parse cron schedule to get hours and minutes
49+ if ( config . cronSchedule ) {
50+ // Cron format: minutes hours * * *
51+ const cronParts = config . cronSchedule . split ( ' ' ) ;
52+ const minutes = cronParts [ 0 ] ;
53+ const hours = cronParts [ 1 ] ;
54+
55+ // Convert 24-hour format to 12-hour format
56+ let hour = parseInt ( hours , 10 ) ;
57+ let period = 'AM' ;
58+
59+ if ( hour >= 12 ) {
60+ period = 'PM' ;
61+ hour = hour === 12 ? 12 : hour - 12 ;
62+ }
63+ hour = hour === 0 ? 12 : hour ;
64+
65+ setHours ( hour . toString ( ) . padStart ( 2 , '0' ) ) ;
66+ setMinutes ( minutes . padStart ( 2 , '0' ) ) ;
67+ setAmPm ( period ) ;
68+ }
69+ } catch ( error ) {
70+ console . error ( 'Error fetching backup configuration:' , error ) ;
71+ showFeedback ( 'Failed to load saved configuration' , 'danger' ) ;
72+ } finally {
73+ setIsLoading ( false ) ;
74+ }
75+ } ;
76+
77+ fetchConfig ( ) ;
78+ } , [ ] ) ;
79+
80+ // Generate hours options (1-12)
81+ const hoursOptions = Array . from ( { length : 12 } , ( _ , i ) => {
82+ const hour = i + 1 ;
83+ return { value : hour . toString ( ) . padStart ( 2 , '0' ) , label : hour . toString ( ) . padStart ( 2 , '0' ) } ;
84+ } ) ;
85+
86+ // Generate minutes options (00-59)
87+ const minutesOptions = Array . from ( { length : 60 } , ( _ , i ) => {
88+ const minute = i ;
89+ return { value : minute . toString ( ) . padStart ( 2 , '0' ) , label : minute . toString ( ) . padStart ( 2 , '0' ) } ;
90+ } ) ;
91+
92+ const showFeedback = ( message , type ) => {
93+ setFeedback ( { message, type } ) ;
94+ } ;
95+
96+ // Convert 12-hour format to 24-hour for cron
97+ const convertTo24HourFormat = ( ) => {
98+ let hour = parseInt ( hours , 10 ) ;
99+
100+ if ( ampm === 'PM' && hour < 12 ) {
101+ hour += 12 ;
102+ } else if ( ampm === 'AM' && hour === 12 ) {
103+ hour = 0 ;
104+ }
105+
106+ return {
107+ hours : hour . toString ( ) . padStart ( 2 , '0' ) ,
108+ minutes : minutes
109+ } ;
110+ } ;
111+
112+ const handleUpdateDirectory = async ( ) => {
113+ if ( ! backupDir . trim ( ) ) {
114+ showFeedback ( 'Please enter a valid backup directory path' , 'danger' ) ;
115+ return ;
116+ }
117+
118+ setIsLoading ( true ) ;
119+ try {
120+ const response = await axiosInstance . post ( '/backupauto/update-directory' , { backupDir } ) ;
121+ showFeedback ( response . data , 'success' ) ;
122+ } catch ( error ) {
123+ showFeedback ( `Error updating directory: ${ error . response ?. data || error . message } ` , 'danger' ) ;
124+ } finally {
125+ setIsLoading ( false ) ;
126+ }
127+ } ;
128+
129+ const handleUpdateSchedule = async ( ) => {
130+ setIsLoading ( true ) ;
131+ try {
132+ // Convert to 24-hour format for cron
133+ const time24 = convertTo24HourFormat ( ) ;
134+
135+ // Create cron schedule - minutes hours * * *
136+ const cronSchedule = `${ time24 . minutes } ${ time24 . hours } * * *` ;
137+
138+ const response = await axiosInstance . post ( '/backupauto/update-schedule' , { cronSchedule } ) ;
139+ showFeedback ( response . data , 'success' ) ;
140+ } catch ( error ) {
141+ showFeedback ( `Error updating schedule: ${ error . response ?. data || error . message } ` , 'danger' ) ;
142+ } finally {
143+ setIsLoading ( false ) ;
144+ }
145+ } ;
146+
147+ const handleManualBackup = async ( ) => {
148+ if ( ! backupDir . trim ( ) ) {
149+ showFeedback ( 'Please configure a backup directory first' , 'warning' ) ;
150+ return ;
151+ }
152+
153+ setIsLoading ( true ) ;
154+ try {
155+ const response = await axiosInstance . post ( '/backupauto/backup' ) ;
156+ showFeedback ( response . data , 'success' ) ;
157+ } catch ( error ) {
158+ showFeedback ( `Error triggering backup: ${ error . response ?. data || error . message } ` , 'danger' ) ;
159+ } finally {
160+ setIsLoading ( false ) ;
161+ }
162+ } ;
163+
164+ return (
165+ < div className = "p-5" >
166+ < h2 className = "text-2xl mb-4" > Backup Manager</ h2 >
167+
168+ { feedback . message && (
169+ < CAlert color = { feedback . type } className = "mb-4" >
170+ { feedback . message }
171+ </ CAlert >
172+ ) }
173+
174+ < CCard className = "mb-4" >
175+ < CCardHeader >
176+ < div > Backup Configuration</ div >
177+ </ CCardHeader >
178+ < CCardBody >
179+ < CForm className = "mb-4" >
180+ < CFormLabel > Backup Directory</ CFormLabel >
181+ < div className = "d-flex" >
182+ < CFormInput
183+ type = "text"
184+ value = { backupDir }
185+ onChange = { ( e ) => setBackupDir ( e . target . value ) }
186+ placeholder = "Enter backup directory path"
187+ disabled = { isLoading }
188+ />
189+ < CButton
190+ color = "primary"
191+ onClick = { handleUpdateDirectory }
192+ className = "ms-2"
193+ disabled = { isLoading }
194+ >
195+ < FontAwesomeIcon icon = { faFolderOpen } /> Set Directory
196+ </ CButton >
197+ </ div >
198+ </ CForm >
199+
200+ < CForm className = "mb-4" >
201+ < CFormLabel > Backup Time</ CFormLabel >
202+ < div className = "d-flex align-items-center" >
203+ < div className = "d-flex" style = { { width : '220px' } } >
204+ < CFormSelect
205+ value = { hours }
206+ onChange = { ( e ) => setHours ( e . target . value ) }
207+ disabled = { isLoading }
208+ className = "me-1"
209+ style = { { width : '70px' } }
210+ >
211+ { hoursOptions . map ( option => (
212+ < option key = { option . value } value = { option . value } > { option . label } </ option >
213+ ) ) }
214+ </ CFormSelect >
215+ < span className = "mx-1 mt-2" > :</ span >
216+ < CFormSelect
217+ value = { minutes }
218+ onChange = { ( e ) => setMinutes ( e . target . value ) }
219+ disabled = { isLoading }
220+ className = "me-1"
221+ style = { { width : '70px' } }
222+ >
223+ { minutesOptions . map ( option => (
224+ < option key = { option . value } value = { option . value } > { option . label } </ option >
225+ ) ) }
226+ </ CFormSelect >
227+ < CFormSelect
228+ value = { ampm }
229+ onChange = { ( e ) => setAmPm ( e . target . value ) }
230+ disabled = { isLoading }
231+ style = { { width : '70px' } }
232+ >
233+ < option value = "AM" > AM</ option >
234+ < option value = "PM" > PM</ option >
235+ </ CFormSelect >
236+ </ div >
237+ < CButton
238+ color = "success"
239+ onClick = { handleUpdateSchedule }
240+ className = "ms-3"
241+ disabled = { isLoading }
242+ >
243+ < FontAwesomeIcon icon = { faClock } /> Set Schedule
244+ </ CButton >
245+ </ div >
246+ < small className = "text-muted mt-2 d-block" >
247+ Current schedule: Daily at { hours } :{ minutes } { ampm }
248+ </ small >
249+ </ CForm >
250+
251+ < CButton
252+ color = "danger"
253+ onClick = { handleManualBackup }
254+ disabled = { isLoading }
255+ className = "mt-3"
256+ >
257+ < FontAwesomeIcon icon = { faPlay } /> Trigger Backup Now
258+ </ CButton >
259+ </ CCardBody >
260+ </ CCard >
261+ </ div >
262+ ) ;
263+ } ;
264+
265+ export default BackupManagerFrontend ;
0 commit comments