Skip to content

Added Form #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 34 additions & 54 deletions src/main/index.js
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 10 additions & 1 deletion src/preload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
155 changes: 155 additions & 0 deletions src/renderer/src/components/Form.jsx
Original file line number Diff line number Diff line change
@@ -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
className="grid grid-cols-2 gap-8 p-8 bg-gray-100 rounded shadow-md max-w-4xl mx-auto text-black"
onSubmit={handleSubmit}
>
{/* Left Column */}
<div className="flex flex-col gap-4">
<div>
<label className="block font-bold text-black">Date:</label>
<input
type="date"
name="date"
value={staticData.date}
onChange={handleLeftColumnChange}
className="p-2 w-full border rounded text-black"
/>
</div>
<div>
<label className="block font-bold text-black">Term:</label>
<input
type="text"
name="term"
value={staticData.term}
onChange={handleLeftColumnChange}
className="p-2 w-full border rounded text-black"
/>
</div>
<div>
<label className="block font-bold text-black">Week:</label>
<input
type="text"
name="week"
value={staticData.week}
onChange={handleLeftColumnChange}
className="p-2 w-full border rounded text-black"
/>
</div>
</div>

{/* Right Column */}
<div className="flex flex-col gap-4">
<div>
<label className="block font-bold text-black">Name:</label>
<select
name="name"
value={formData.name}
onChange={handleInputChange}
className="p-2 w-full border rounded text-black"
required
>
<option value="" disabled>
Select Name
</option>
<option value="John Doe">John Doe</option>
<option value="Jane Smith">Jane Smith</option>
<option value="Michael Brown">Michael Brown</option>
</select>
</div>
<div>
<label className="block font-bold text-black">In Time:</label>
<input
type="time"
name="inTime"
value={formData.inTime}
onChange={handleInputChange}
className="p-2 w-full border rounded text-black"
required
/>
</div>
<div>
<label className="block font-bold text-black">Out Time:</label>
<input
type="time"
name="outTime"
value={formData.outTime}
onChange={handleInputChange}
className="p-2 w-full border rounded text-black"
required
/>
</div>
<div>
<label className="block font-bold text-black">Break (minutes):</label>
<input
type="number"
name="break"
value={formData.break}
onChange={handleInputChange}
className="p-2 w-full border rounded text-black"
required
/>
</div>
<button
type="submit"
className="p-2 bg-[#800000] text-white rounded hover:bg-[#660000] duration-300"
>
Save
</button>
</div>
</form>
)
}

Form.propTypes = {
onSubmit: PropTypes.func.isRequired,
staticData: PropTypes.object.isRequired,
setStaticData: PropTypes.func.isRequired
}

export default Form
28 changes: 26 additions & 2 deletions src/renderer/src/pages/home.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<div className="container mx-auto p-4">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} tabList={MAINTABS} />
{activeTab === 'Add Records' && (
<div className="mt-4">
<h2 className="text-xl font-bold mb-4">Add Attendance Record</h2>
<Form onSubmit={handleFormSubmit} staticData={staticData} setStaticData={setStaticData} />
</div>
)}
</div>
)
}
Expand Down