diff --git a/src/main/index.js b/src/main/index.js index 862f80c..9166ea9 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,82 +1,62 @@ -import { app, shell, BrowserWindow, ipcMain } from 'electron' -import { join } from 'path' -import { electronApp, optimizer, is } from '@electron-toolkit/utils' -import icon from '../../resources/icon.png?asset' - -import { MenuBuilder } from './menuMaker' -import FileService from '../services/file-service' +import { app, BrowserWindow, ipcMain } from 'electron' +import path from 'path' +import fs from 'fs' +import { promises as fsPromises } from 'fs' function createWindow() { - // Create the browser window. const mainWindow = new BrowserWindow({ width: 900, height: 670, show: false, autoHideMenuBar: true, - ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { - preload: join(__dirname, '../preload/index.js'), - sandbox: false + preload: path.join(__dirname, '..', 'preload', 'index.js'), // Link to preload script + nodeIntegration: false, // Disable nodeIntegration for security + contextIsolation: true // Enable context isolation for security } }) + mainWindow.loadFile('index.html') + mainWindow.on('ready-to-show', () => { mainWindow.show() }) +} - mainWindow.webContents.setWindowOpenHandler((details) => { - shell.openExternal(details.url) - return { action: 'deny' } - }) - - FileService.ensureSettingsFile() - - const menuBuilder = new MenuBuilder({ mainWindow: mainWindow, setMenuVisibilityAlways: true }) - menuBuilder.buildMenu() - - // HMR for renderer base on electron-vite cli. - // Load the remote URL for development or the local html file for production. - if (is.dev && process.env['ELECTRON_RENDERER_URL']) { - mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) - } else { - mainWindow.loadFile(join(__dirname, '../renderer/index.html')) +ipcMain.on('save-timesheet', async (event, data) => { + const filePath = path.join(app.getPath('userData'), 'timesheets.csv') + const csvData = [ + 'Date,Term,Week,Name,In Time,Out Time,Break', + `${data.date},${data.term},${data.week},${data.name},${data.inTime},${data.outTime},${data.break}` + ].join('\n') + + try { + await fsPromises.access(filePath) + await fsPromises.appendFile(filePath, csvData + '\n') + event.reply('timesheet-saved', 'Data saved successfully') + } catch (err) { + if (err.code === 'ENOENT') { + await fsPromises.writeFile(filePath, csvData + '\n') + event.reply('timesheet-saved', 'File created and data saved') + } else { + console.error('Error saving timesheet data:', err) + event.reply('timesheet-saved', 'Error saving data') + } } -} +}) -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. app.whenReady().then(() => { - // Set app user model id for windows - electronApp.setAppUserModelId('com.electron') - - // Default open or close DevTools by F12 in development - // and ignore CommandOrControl + R in production. - // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils - app.on('browser-window-created', (_, window) => { - optimizer.watchWindowShortcuts(window) - }) - - // IPC test - ipcMain.on('ping', () => console.log('pong')) - createWindow() - app.on('activate', function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) createWindow() + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } }) }) -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) - -// In this file you can include the rest of your app"s specific main process -// code. You can also put them in separate files and require them here. diff --git a/src/preload/index.js b/src/preload/index.js index 8d62cb9..fb35bfd 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -2,19 +2,28 @@ import { contextBridge } from 'electron' import { electronAPI } from '@electron-toolkit/preload' // Custom APIs for renderer -const api = {} +const api = { + saveTimesheet: (data) => { + // Send the 'save-timesheet' event to the main process + window.electron.ipcRenderer.send('save-timesheet', data) + } +} // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. if (process.contextIsolated) { try { + // Expose the electronAPI to the renderer process securely contextBridge.exposeInMainWorld('electron', electronAPI) + + // Expose custom API to the renderer process contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) } } else { + // For non-context isolated environments (less secure) window.electron = electronAPI window.api = api } diff --git a/src/renderer/src/components/Form.jsx b/src/renderer/src/components/Form.jsx new file mode 100644 index 0000000..95f1915 --- /dev/null +++ b/src/renderer/src/components/Form.jsx @@ -0,0 +1,155 @@ +import PropTypes from 'prop-types' +import { useState, useEffect } from 'react' + +const Form = ({ onSubmit, staticData, setStaticData }) => { + const [formData, setFormData] = useState({ + name: '', + inTime: '', + outTime: '', + break: '' + }) + + const handleInputChange = (e) => { + const { name, value } = e.target + setFormData((prev) => ({ ...prev, [name]: value })) + } + + const handleLeftColumnChange = (e) => { + const { name, value } = e.target + setStaticData((prev) => ({ ...prev, [name]: value })) + } + + const handleSubmit = (e) => { + e.preventDefault() + + // Prepare the data to send + const dataToSave = { + ...staticData, // Add static data (date, term, week) + ...formData // Add form data (name, inTime, outTime, break) + } + + onSubmit(dataToSave) // Pass data to parent component (Home) + + // Clear form data after submission + setFormData({ name: '', inTime: '', outTime: '', break: '' }) + } + + useEffect(() => { + // Populate form with static data when component mounts + setFormData((prev) => ({ + ...prev, + date: staticData.date, + term: staticData.term, + week: staticData.week + })) + }, [staticData]) // Update whenever staticData changes + + return ( +
+ ) +} + +Form.propTypes = { + onSubmit: PropTypes.func.isRequired, + staticData: PropTypes.object.isRequired, + setStaticData: PropTypes.func.isRequired +} + +export default Form diff --git a/src/renderer/src/pages/home.jsx b/src/renderer/src/pages/home.jsx index b1b3f76..e7a847a 100644 --- a/src/renderer/src/pages/home.jsx +++ b/src/renderer/src/pages/home.jsx @@ -1,14 +1,38 @@ import { useState } from 'react' - import Tabs from '../components/Tabs' +import Form from '../components/Form' import { MAINTABS } from '../constants/app-constants' const Home = () => { const [activeTab, setActiveTab] = useState(MAINTABS[0].name) + // Manage editable static data in state + const [staticData, setStaticData] = useState({ + date: new Date().toISOString().split('T')[0], // Today's date + term: 'Spring 2025', // Example term + week: '3' // Example week + }) + + const handleFormSubmit = (data) => { + console.log('Form Data:', data) + + // Ensure the api is correctly accessed from the preload + if (window.api && window.api.saveTimesheet) { + window.api.saveTimesheet(data) // This should now call the exposed function + } else { + console.error('saveTimesheet function not found') + } + } + return ( -