diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index db5dcbd..cd279bd 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -25,8 +25,8 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use std::sync::Mutex; -use tauri::{Menu, Submenu, MenuItem, CustomMenuItem, State}; use serde::Serialize; +use tauri::{CustomMenuItem, Menu, MenuItem, State, Submenu}; struct Holders(Mutex>); @@ -55,12 +55,15 @@ fn greet(name: &str) -> String { } #[tauri::command(async)] -fn fetch_holders(search: &str, holders: State) -> Vec { +fn fetch_holders(search: &str, holders: State) -> Result, String> { // std::thread::sleep(Duration::from_millis(200)); let mut hldrs = holders.0.lock().unwrap(); - for i in 0..10_000 { - let pass_type = PassType { name: format!("annual"), code: format!("Annual") }; + for i in 0..1_000 { + let pass_type = PassType { + name: format!("annual"), + code: format!("Annual"), + }; let holder_data = HolderData { id: i + 64, first_name: format!("{search}{i}"), @@ -75,13 +78,14 @@ fn fetch_holders(search: &str, holders: State) -> Vec { hldrs.push(holder_data); } - hldrs.to_vec() + Ok(hldrs.to_vec()) } fn main() { - let new_todo = CustomMenuItem::new("settings".to_string(), "Settings"); - let close = CustomMenuItem::new("quit".to_string(), "Quit"); - let submenu = Submenu::new("File", Menu::new().add_item(new_todo).add_item(close)); + let settings = CustomMenuItem::new("settings".to_string(), "Settings..."); + // let close = CustomMenuItem::new("quit".to_string(), "Quit"); + let about = CustomMenuItem::new("about".to_string(), "About..."); + let submenu = Submenu::new("File", Menu::new().add_item(settings).add_native_item(MenuItem::Separator).add_item(about)); let menu = Menu::new() .add_native_item(MenuItem::Copy) .add_submenu(submenu); @@ -89,12 +93,11 @@ fn main() { tauri::Builder::default() .manage(Holders(Default::default())) .menu(menu) - .on_menu_event(|event| { - match event.menu_item_id() { - "settings" => event.window().emit("settings", "").unwrap(), - "quit" => std::process::exit(0), - _ => (), - } + .on_menu_event(|event| match event.menu_item_id() { + "settings" => event.window().emit("settings", "").unwrap(), + "about" => event.window().emit("about", "").unwrap(), + // "quit" => std::process::exit(0), + _ => (), }) .invoke_handler(tauri::generate_handler![greet, fetch_holders]) .run(tauri::generate_context!()) diff --git a/src/App.tsx b/src/App.tsx index 99015e3..4e8ebfd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,17 +1,24 @@ import { useState, FormEvent, useReducer } from "react"; -// import { Dispatch, SetStateAction, createContext, ReactNode, useContext } from "react"; -import { emit, listen } from '@tauri-apps/api/event' - -import { SearchBar } from "./components/SearchBar"; -import { PassInfo } from "./components/PassInfo"; -import { SearchResults } from "./components/SearchResults"; - -import { Button } from 'primereact/button'; +import { listen } from '@tauri-apps/api/event' import 'primeicons/primeicons.css'; import { Divider } from "primereact/divider"; -import { AddPass } from "./screens/AddPass"; + import { fetchHolders } from "./api/api"; +import { SearchBar } from "./components/SearchBar"; +import { SearchResults } from "./components/SearchResults"; +import { PassInteraction } from "./components/PassInteraction"; +import { AddPass } from "./screens/AddPass"; +import { Settings } from "./screens/Settings"; +import { ViewPass } from "./screens/ViewPass"; +import { About } from "./screens/About"; + +export enum Screen { + Dashboard, + ViewPass, + Settings, + About, +} export interface PassType { name: string, @@ -40,50 +47,34 @@ export const blankHolder: HolderData = { notes: "", } -export type HolderAction = - | { type: "replace"; data: HolderData } - | { type: "set_first_name"; data: string } - | { type: "set_last_name"; data: string } - | { type: "set_town"; data: string } - | { type: "set_passtype"; data: PassType | null } - | { type: "set_active"; data: boolean } - | { type: "set_notes"; data: string } - -export enum Screen { - Dashboard, - ViewPass, - Settings, +export enum Msg { + Replace = "REPLACE", + SetFirstName = "SET_FIRST_NAME", + SetLastName = "SET_LAST_NAME", + SetTown = "SET_TOWN", + SetPasstype = "SET_PASSTYPE", + SetActive = "SET_ACTIVE", + SetNotes = "SET_NOTES", } +export type HolderAction = + | { type: Msg.Replace; data: HolderData } + | { type: Msg.SetFirstName; data: string } + | { type: Msg.SetLastName; data: string } + | { type: Msg.SetTown; data: string } + | { type: Msg.SetPasstype; data: PassType | null } + | { type: Msg.SetActive; data: boolean } + | { type: Msg.SetNotes; data: string } + function holderReducer(holder: HolderData, action: HolderAction): HolderData { switch (action.type) { - case "replace": { - return action.data - } - case "set_first_name": { - return { ...holder, - first_name: action.data, } - } - case "set_last_name": { - return { ...holder, - last_name: action.data, } - } - case "set_town": { - return { ...holder, - town: action.data, } - } - case "set_passtype": { - return { ...holder, - passtype: action.data, } - } - case "set_active": { - return { ...holder, - active: action.data, } - } - case "set_notes": { - return { ...holder, - notes: action.data, } - } + case Msg.Replace: { return action.data } + case Msg.SetFirstName: { return { ...holder, first_name: action.data } } + case Msg.SetLastName: { return { ...holder, last_name: action.data } } + case Msg.SetTown: { return { ...holder, town: action.data } } + case Msg.SetPasstype: { return { ...holder, passtype: action.data } } + case Msg.SetActive: { return { ...holder, active: action.data } } + case Msg.SetNotes: { return { ...holder, notes: action.data } } } } @@ -100,73 +91,44 @@ function App() { setPassholders(await res); } - function PassInteraction() { - return ( -
-
- ) - } - - const Dashboard = + const Dashboard =
- +
- - - {addPass ? : } + + + {addPass ? ( + + ) : ( + + )}
- - const ViewPass = -
-
- -
-
- const Settings = -
-
-
-
+ listen('settings', () => { setScreen(Screen.Settings) }) + listen('about', () => { setScreen(Screen.About) }) - listen('settings', () => { - setScreen(Screen.Settings) - }) - switch (screen) { case Screen.Dashboard: return Dashboard; case Screen.ViewPass: - return ViewPass; + return ViewPass({ setScreen, selectedHolder, setSelectedHolder }); case Screen.Settings: - return Settings; + return Settings({ setScreen }); + case Screen.About: + return About({ setScreen }); } } diff --git a/src/components/PassInfo.tsx b/src/components/PassInfo.tsx index 204890f..2e36456 100644 --- a/src/components/PassInfo.tsx +++ b/src/components/PassInfo.tsx @@ -1,4 +1,4 @@ -import { HolderData, HolderAction, PassType } from "../App" +import { HolderData, HolderAction, PassType, Msg } from "../App" import { ScrollPanel } from "primereact/scrollpanel"; import { InputText } from "primereact/inputtext"; @@ -19,29 +19,33 @@ interface ChildProps { setSelectedHolder: React.Dispatch, } +interface InputFieldProps { + label: string, + value: string, + onChange: (value: string) => void, +} + +const InputField: React.FC = ({ label, value, onChange }) => { + return ( + <> +
{label}
+ onChange(e.target.value)} /> + + ); +}; + export function PassInfo({ selectedHolder, setSelectedHolder }: ChildProps) { return ( -
First Name:
- setSelectedHolder({ - type: 'set_first_name', - data: e.target.value, - })} /> - -
Last Name:
- setSelectedHolder({ - type: 'set_last_name', - data: e.target.value, - })} /> - -
Town:
- setSelectedHolder({ - type: 'set_town', - data: e.target.value, - })} /> + setSelectedHolder({ type: Msg.SetFirstName, data: value })} /> + + setSelectedHolder({ type: Msg.SetLastName, data: value })} /> + + setSelectedHolder({ type: Msg.SetTown, data: value })} />
Passtype:
{ setSelectedHolder({ - type: 'set_passtype', + type: Msg.SetPasstype, data: e.value, }) }} /> diff --git a/src/components/PassInteraction.tsx b/src/components/PassInteraction.tsx new file mode 100644 index 0000000..a568226 --- /dev/null +++ b/src/components/PassInteraction.tsx @@ -0,0 +1,33 @@ +import { Button } from "primereact/button" +import { Divider } from "primereact/divider" + +import { HolderData } from "../App" +import { Screen } from "../App" + +interface ChildProps { + selectedHolder: HolderData, + setScreen: React.Dispatch>, + setAddPass: React.Dispatch>, +} + +export const PassInteraction = ({selectedHolder, setScreen, setAddPass}: ChildProps) => { + return ( +
+
+ ) +} \ No newline at end of file diff --git a/src/components/SearchResults.tsx b/src/components/SearchResults.tsx index 0572551..02dfbc4 100644 --- a/src/components/SearchResults.tsx +++ b/src/components/SearchResults.tsx @@ -1,4 +1,4 @@ -import { HolderData, HolderAction, blankHolder } from "../App"; +import { HolderData, HolderAction, blankHolder, Msg } from "../App"; import { DataTable } from "primereact/datatable"; import { Column } from "primereact/column"; // import { useContext } from "react"; @@ -28,7 +28,7 @@ export function SearchResults({ passholders, selectedHolder, setSelectedHolder } scrollable scrollHeight="87%" paginator rows={32} // virtualScrollerOptions={{ itemSize: 46 }} // broken value={passholders} metaKeySelection={false} selectionMode="single" selection={selectedHolder} - onSelectionChange={(e) => setSelectedHolder({ type: "replace", data: e.value || blankHolder })} dataKey="id" + onSelectionChange={(e) => setSelectedHolder({ type: Msg.Replace, data: e.value || blankHolder })} dataKey="id" > diff --git a/src/screens/About.tsx b/src/screens/About.tsx new file mode 100644 index 0000000..f8ebc95 --- /dev/null +++ b/src/screens/About.tsx @@ -0,0 +1,21 @@ +import { Button } from "primereact/button" + +import { Screen } from "../App" + +interface ChildProps { + setScreen: React.Dispatch>, +} + +export function About({ setScreen }: ChildProps) { + return ( +
+
+
Passtracker is a tracker for passes.
+
+
+ ) +} \ No newline at end of file diff --git a/src/screens/AddPass.tsx b/src/screens/AddPass.tsx index a6e9d44..c16722b 100644 --- a/src/screens/AddPass.tsx +++ b/src/screens/AddPass.tsx @@ -1,4 +1,5 @@ -import { HolderData, HolderAction, PassType, blankHolder } from "../App" +import { useFormik } from 'formik'; +import * as Yup from 'yup'; import { ScrollPanel } from "primereact/scrollpanel"; import { InputText } from "primereact/inputtext"; @@ -6,8 +7,7 @@ import { Dropdown } from "primereact/dropdown"; import { Button } from "primereact/button"; import { Divider } from "primereact/divider"; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; +import { HolderData, HolderAction, PassType, blankHolder, Msg } from "../App" const payMethods = [ { name: "Credit", code: "credit" }, @@ -30,7 +30,24 @@ interface ChildProps { setAddPass: React.Dispatch, } -export function AddPass({ selectedHolder, setSelectedHolder, setAddPass }: ChildProps) { +interface InputFieldProps { + label: string, + value: string, + onChange: (value: string) => void, +} + +const InputField: React.FC = ({ label, value, onChange }) => { + return ( + <> +
{label}
+ onChange(e.target.value)} + /> + + ) +} + +export const AddPass = ({ selectedHolder, setSelectedHolder, setAddPass }: ChildProps) => { const formik = useFormik({ initialValues: { lastFour: '', @@ -39,10 +56,10 @@ export function AddPass({ selectedHolder, setSelectedHolder, setAddPass }: Child signature: '', }, validationSchema: Yup.object().shape({ - lastFour: Yup.string().required('Last Four is required'), - amountPaid: Yup.string().required('Amount Paid is required'), - paymentMethod: Yup.string().required('Payment method is required'), - signature: Yup.string().required('Employee Signature is required'), + lastFour: Yup.number(), + amountPaid: Yup.number().positive().required('required'), + paymentMethod: Yup.string().required('required'), + signature: Yup.string().required('required'), }), onSubmit: values => { console.log('Form submitted:', values); @@ -53,34 +70,16 @@ export function AddPass({ selectedHolder, setSelectedHolder, setAddPass }: Child return (
-
First Name:
- setSelectedHolder({ - type: 'set_first_name', - data: e.target.value, - })} + setSelectedHolder({ type: Msg.SetFirstName, data: e })} /> -
Last Name:
- setSelectedHolder({ - type: 'set_last_name', - data: e.target.value, - })} + setSelectedHolder({ type: Msg.SetLastName, data: e })} /> -
Town:
- setSelectedHolder({ - type: 'set_town', - data: e.target.value, - })} + setSelectedHolder({ type: Msg.SetTown, data: e })} />
Passtype:
@@ -91,7 +90,7 @@ export function AddPass({ selectedHolder, setSelectedHolder, setAddPass }: Child optionLabel="code" onChange={(e) => { setSelectedHolder({ - type: 'set_passtype', + type: Msg.SetPasstype, data: e.value, }); }} @@ -107,43 +106,50 @@ export function AddPass({ selectedHolder, setSelectedHolder, setAddPass }: Child formik.setFieldValue('payMethod', e.value); }} /> +
-
Last Four:
+
+ Last Four: +
+ {formik.touched.lastFour && formik.errors.lastFour && ( +
{formik.errors.lastFour}
+ )} - {formik.touched.lastFour && formik.errors.lastFour && ( -
{formik.errors.lastFour}
- )} -
Amount Paid:
+
+ Amount Paid: +
+ {formik.touched.amountPaid && formik.errors.amountPaid && ( +
{formik.errors.amountPaid}
+ )} - {formik.touched.amountPaid && formik.errors.amountPaid && ( -
{formik.errors.amountPaid}
- )} -
Employee Signature:
+
+ Employee Signature: +
+ {formik.touched.signature && formik.errors.signature && ( +
{formik.errors.signature}
+ )} - {formik.touched.signature && formik.errors.signature && ( -
{formik.errors.signature}
- )}