Add UI basics.
This commit is contained in:
@@ -17,11 +17,9 @@ export async function handleQuickBookRequest(
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
if (process.platform !== "win32") {
|
||||
res
|
||||
.status(500)
|
||||
.json({
|
||||
error: "QuickBooks Desktop integration is only available on Windows",
|
||||
});
|
||||
res.status(500).json({
|
||||
error: "QuickBooks Desktop integration is only available on Windows",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "@ant-design/v5-patch-for-react-19";
|
||||
import { Layout } from "antd";
|
||||
import { Layout, Skeleton } from "antd";
|
||||
import { User } from "firebase/auth";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
@@ -16,10 +16,15 @@ import { auth } from "./util/firebase";
|
||||
import { NotificationProvider } from "./util/notificationContext";
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [user, setUser] = useState<User | boolean | null>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Only set up the listener once when component mounts
|
||||
if (auth.currentUser) {
|
||||
setUser(auth.currentUser);
|
||||
} else {
|
||||
setUser(false);
|
||||
}
|
||||
const unsubscribe = auth.onAuthStateChanged((user: User | null) => {
|
||||
setUser(user);
|
||||
//Send back to the main process so that it knows we are authenticated.
|
||||
@@ -41,19 +46,25 @@ const App: React.FC = () => {
|
||||
<HashRouter>
|
||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||
<NotificationProvider>
|
||||
<Layout>
|
||||
{!user ? (
|
||||
<SignInForm />
|
||||
) : (
|
||||
<>
|
||||
<NavigationHeader />
|
||||
<UpdateAvailable />
|
||||
<Routes>
|
||||
<Route path="/" element={<Settings />} />
|
||||
</Routes>
|
||||
</>
|
||||
)}
|
||||
</Layout>
|
||||
<Skeleton loading={user === null} active>
|
||||
<Layout style={{ minHeight: "100vh" }}>
|
||||
{!user ? (
|
||||
<SignInForm />
|
||||
) : (
|
||||
<>
|
||||
<Layout.Header>
|
||||
<NavigationHeader />
|
||||
</Layout.Header>
|
||||
<Layout.Content style={{ padding: "0 24px" }}>
|
||||
<UpdateAvailable />
|
||||
<Routes>
|
||||
<Route path="/" element={<Settings />} />
|
||||
</Routes>
|
||||
</Layout.Content>
|
||||
</>
|
||||
)}
|
||||
</Layout>
|
||||
</Skeleton>
|
||||
</NotificationProvider>
|
||||
</ErrorBoundary>
|
||||
</HashRouter>
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
import { Button } from "antd";
|
||||
import ipcTypes from "../../../../util/ipcTypes.json";
|
||||
|
||||
const Home: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Home</h1>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
window.electron.ipcRenderer.send(
|
||||
ipcTypes.toMain.debug.decodeEstimate,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Test Decode Estimate
|
||||
</Button>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
window.electron.ipcRenderer.send(
|
||||
ipcTypes.toMain.updates.checkForUpdates,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Check for Update
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { selectWatcherStatus } from "@renderer/redux/app.slice";
|
||||
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
||||
import { auth } from "@renderer/util/firebase";
|
||||
import { Badge, Layout, Menu } from "antd";
|
||||
import { MenuItemType } from "antd/es/menu/interface";
|
||||
import { signOut } from "firebase/auth";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NavLink } from "react-router";
|
||||
|
||||
@@ -10,6 +12,13 @@ const NavigationHeader: React.FC = () => {
|
||||
const isWatcherStarted = useAppSelector(selectWatcherStatus);
|
||||
const menuItems: MenuItemType[] = [
|
||||
{ label: <NavLink to="/">{t("navigation.home")}</NavLink>, key: "home" },
|
||||
{
|
||||
label: t("navigation.signout"),
|
||||
key: "watchlist",
|
||||
onClick: (): void => {
|
||||
signOut(auth);
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: <NavLink to="/settings">{t("navigation.settings")}</NavLink>,
|
||||
// key: "settings",
|
||||
|
||||
@@ -41,9 +41,10 @@ const SettingsWatchedPaths: React.FC = () => {
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Timeline>
|
||||
{watchedPaths?.map((path, index) => (
|
||||
<Timeline.Item key={index}>
|
||||
<Timeline
|
||||
items={watchedPaths.map((path, index) => ({
|
||||
key: index,
|
||||
children: (
|
||||
<Space align="baseline">
|
||||
{path}
|
||||
<Button
|
||||
@@ -54,9 +55,9 @@ const SettingsWatchedPaths: React.FC = () => {
|
||||
onClick={() => handleRemovePath(path)}
|
||||
/>
|
||||
</Space>
|
||||
</Timeline.Item>
|
||||
))}
|
||||
</Timeline>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,12 +25,10 @@ const SettingsWatcher: React.FC = () => {
|
||||
interval: 0,
|
||||
});
|
||||
|
||||
console.log("*** ~ pollingState:", pollingState);
|
||||
const getPollingStateFromStore = (): void => {
|
||||
window.electron.ipcRenderer
|
||||
.invoke(ipcTypes.toMain.settings.watcher.getpolling)
|
||||
.then((storePollingState: { enabled: boolean; interval: number }) => {
|
||||
console.log("*** ~ .then ~ storePollingState:", storePollingState);
|
||||
setPollingState(storePollingState);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Divider } from "antd";
|
||||
import { Col, Row } from "antd";
|
||||
import SettingsWatchedPaths from "./Settings.WatchedPaths";
|
||||
import SettingsWatcher from "./Settings.Watcher";
|
||||
import Home from "../Home/Home";
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<Home />
|
||||
<Divider />
|
||||
<SettingsWatchedPaths />
|
||||
<SettingsWatcher />
|
||||
</div>
|
||||
<Row gutter={[32, 32]}>
|
||||
<Col span={24}>
|
||||
<SettingsWatchedPaths />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<SettingsWatcher />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
export default Settings;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { auth } from "@renderer/util/firebase";
|
||||
import type { FormProps } from "antd";
|
||||
import { Button, Checkbox, Form, Input } from "antd";
|
||||
import { Alert, Button, Checkbox, Form, Input } from "antd";
|
||||
import log from "electron-log/renderer";
|
||||
import { signInWithEmailAndPassword } from "firebase/auth";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import errorTypeCheck from "../../../../util/errorTypeCheck";
|
||||
|
||||
type FieldType = {
|
||||
@@ -12,14 +14,16 @@ type FieldType = {
|
||||
};
|
||||
|
||||
const SignInForm: React.FC = () => {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { t } = useTranslation();
|
||||
const onFinish: FormProps<FieldType>["onFinish"] = async (values) => {
|
||||
log.info("Form submitted successfully:", values);
|
||||
const { username, password } = values;
|
||||
try {
|
||||
const result = await signInWithEmailAndPassword(auth, username, password);
|
||||
log.debug("Login result", result);
|
||||
} catch (error) {
|
||||
log.error("Login error", errorTypeCheck(error));
|
||||
setError(t("auth.login.error"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,40 +47,30 @@ const SignInForm: React.FC = () => {
|
||||
<Form.Item<FieldType>
|
||||
label="Username"
|
||||
name="username"
|
||||
rules={[{ required: true, message: "Please input your username!" }]}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item<FieldType>
|
||||
label="Password"
|
||||
name="password"
|
||||
rules={[{ required: true, message: "Please input your password!" }]}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
<Form.Item<FieldType>
|
||||
name="remember"
|
||||
valuePropName="checked"
|
||||
label={null}
|
||||
>
|
||||
<Checkbox>Remember me</Checkbox>
|
||||
</Form.Item>
|
||||
<Form.Item label={null}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
{t("auth.login.login")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
if (auth.currentUser) {
|
||||
log.info(auth.currentUser);
|
||||
} else {
|
||||
log.warn("No user is currently signed in.");
|
||||
}
|
||||
}}
|
||||
>
|
||||
Log user info
|
||||
</Button>
|
||||
{error && (
|
||||
<Form.Item label={null}>
|
||||
<Alert message={error} type="error" />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label={null}>
|
||||
<Button>{t("auth.login.resetpassword")}</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
{
|
||||
"translation": {
|
||||
"auth": {
|
||||
"login": {
|
||||
"error": "The username and password combination provided is not valid.",
|
||||
"login": "Log In",
|
||||
"resetpassword": "Reset Password"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"home": "Home",
|
||||
"settings": "Settings"
|
||||
"settings": "Settings",
|
||||
"signout": "Sign Out"
|
||||
},
|
||||
"settings": {
|
||||
"actions": {
|
||||
|
||||
@@ -45,6 +45,55 @@
|
||||
<folder_node>
|
||||
<name>translation</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>auth</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>login</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>error</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>login</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>resetpassword</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>navigation</name>
|
||||
<children>
|
||||
@@ -74,6 +123,19 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>signout</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
|
||||
Reference in New Issue
Block a user