Skip to content

Commit

Permalink
chore: copied data from electron (#60)
Browse files Browse the repository at this point in the history
* chore: copied data from electron

* chore: added qr-code

* fix: selector

* chore: axios base query with retry; error handling; qr-code scan

* chore: axios base query with retry; error handling; qr-code scan

* chore: i18n; better bootstrap; redux persist
  • Loading branch information
stephane-segning authored Jul 27, 2024
1 parent 8eaa773 commit befaccd
Show file tree
Hide file tree
Showing 44 changed files with 983 additions and 211 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Lynx Scanner</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions openapi-config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ConfigFile } from '@rtk-query/codegen-openapi';

const baseDir = process.env.GEN_FOLDER!;
const baseDir = './src/store/api/gen';

const config: ConfigFile = {
schemaFile: './openapi.yaml',
apiFile: './src/store/emptyApi.ts',
apiFile: './src/store/api/empty.api.ts',
apiImport: 'emptySplitApi',
hooks: true,
outputFiles: {
Expand Down
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,33 @@
"prettier": "prettier --write .",
"stylelint:actions": "stylelint --fix \"src/**/*.{css,scss}\"",
"convert-md-to-pdf": "yarn run md-to-pdf ./docs/*.md ./docs/**/*.md",
"rtk:gen": "export GEN_FOLDER=./src/store/gen && rimraf $GEN_FOLDER && mkdir $GEN_FOLDER && rtk-query-codegen-openapi openapi-config.ts && prettier -w $GEN_FOLDER",
"rtk:gen": "rimraf ./src/store/api/gen && mkdir ./src/store/api/gen && rtk-query-codegen-openapi openapi-config.ts && prettier -w ./src/store/api/gen",
"postinstall": "yarn run rtk:gen"
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.6",
"@sentry/react": "^7.114.0",
"@sentry/tracing": "^7.114.0",
"@yudiel/react-qr-scanner": "^2.0.4",
"autoprefixer": "^10.4.19",
"axios": "^1.7.2",
"i18next": "^23.11.4",
"i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^2.5.2",
"i18next-localstorage-backend": "^4.2.0",
"md-to-pdf": "^5.2.4",
"postcss": "^8.4.38",
"react": "^18.3.1",
"react-daisyui": "^5.0.0",
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"react-i18next": "^14.1.1",
"react-qr-code": "^2.0.15",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.0",
"redux": "^5.0.1",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
"redux-toolkit": "^1.1.2",
"tailwindcss": "^3.4.3",
"theme-change": "^2.5.0"
Expand All @@ -41,15 +49,18 @@
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@commitlint/format": "^19.3.0",
"@electron-toolkit/preload": "^3.0.1",
"@eslint/eslintrc": "^3.0.2",
"@rtk-query/codegen-openapi": "^1.2.0",
"@types/i18next-browser-languagedetector": "^3.0.0",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/redux-logger": "^3.0.13",
"@typescript-eslint/eslint-plugin": "^7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"@vitejs/plugin-react": "^4.2.1",
"daisyui": "^4.10.5",
"electron-log": "^5.1.7",
"esbuild-runner": "^2.2.2",
"eslint": "8",
"eslint-config-prettier": "^9.1.0",
Expand Down
6 changes: 6 additions & 0 deletions public/i18n/en/common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"action": {
"scan": "Scan"
},
"welcome": "Welcome to the Lynx-Scanner"
}
7 changes: 7 additions & 0 deletions public/i18n/en/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"page": "Welcome to Lynx!",
"description": "Please scan the QR Code for configuration",
"dark": "Dark Mode",
"light": "Light Mode",
"valantine": "Valantine Mode"
}
8 changes: 4 additions & 4 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { JSX } from 'react';
import { RouterProvider } from 'react-router-dom';
import { router } from './router.tsx';
import { FloatingConfig } from './components/floating-config.tsx';
import { Notification } from '@components/notification.tsx';

/**
* The main application component.
*/
export function App(): JSX.Element {
return (
<div className="mx-auto max-w-screen-lg">
<FloatingConfig />
<>
<RouterProvider router={router} />
</div>
<Notification />
</>
);
}
19 changes: 19 additions & 0 deletions src/components/config-scan.button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useCallback } from 'react';
import { Button } from 'react-daisyui';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';

export default function ConfigScanButton() {
const { t } = useTranslation();
const navigate = useNavigate();
const scanConfigAndPersist = useCallback(
() => navigate('/config/scan'),
[navigate]
);

return (
<Button fullWidth onClick={scanConfigAndPersist} color="primary">
{t('action.scan')}
</Button>
);
}
16 changes: 16 additions & 0 deletions src/components/config.qr-code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useConfigData, useFetchConfigUrl } from '@store';
import { Loading } from 'react-daisyui';
import QRCode from 'react-qr-code';
import { useEffect } from 'react';

export default function ConfigQrCode() {
const getUrl = useFetchConfigUrl();
const url = useConfigData();
useEffect(() => getUrl(), [getUrl]);
return (
<figure>
{!url && <Loading />}
{url && <QRCode value={url} />}
</figure>
);
}
38 changes: 30 additions & 8 deletions src/components/floating-config.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { Button, Divider, Dropdown } from 'react-daisyui';
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Menu } from 'react-feather';
import { BarChart2, Globe, List, Menu, Settings } from 'react-feather';
import { Link } from 'react-router-dom';
import { themeChange } from 'theme-change';

interface ThemeButtonProps {
themeName: 'valantine' | 'light' | 'dark';
}

function ThemeButton({ themeName }: ThemeButtonProps) {
const { t } = useTranslation();
const { t: tC } = useTranslation('config');
return (
<button
data-set-theme={'lynx-' + themeName}
data-act-class={'lynx-' + themeName}
>
{t('config.' + themeName)}
<BarChart2 />
{tC(themeName)}
</button>
);
}

export function FloatingConfig() {
useEffect(() => {
themeChange(false);
}, []);

const { i18n } = useTranslation();

const changeLanguageHandler = useCallback(
Expand All @@ -38,7 +45,7 @@ export function FloatingConfig() {
<Menu />
</Button>
</Dropdown.Toggle>
<Dropdown.Menu className="w-52 bg-base-100">
<Dropdown.Menu className="w-52 bg-base-200">
<Dropdown.Item anchor={false}>
<ThemeButton themeName="light" />
</Dropdown.Item>
Expand All @@ -50,14 +57,29 @@ export function FloatingConfig() {
</Dropdown.Item>
<Divider />
<Dropdown.Item anchor={false}>
<button onClick={() => changeLanguageHandler('en')}>English</button>
<button onClick={() => changeLanguageHandler('en')}>
<Globe />
<span>English</span>
</button>
</Dropdown.Item>
<Dropdown.Item anchor={false}>
<button onClick={() => changeLanguageHandler('de')}>Deutsch</button>
<button onClick={() => changeLanguageHandler('de')}>
<Globe />
<span>Deutsch</span>
</button>
</Dropdown.Item>
<Divider />
<Dropdown.Item anchor={false}>
<a href="/config">Config</a>
<Link to="/config">
<Settings />
<span>Config</span>
</Link>
</Dropdown.Item>
<Dropdown.Item anchor={false}>
<Link to="/scans">
<List />
<span>Scans</span>
</Link>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
Expand Down
50 changes: 50 additions & 0 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Button } from 'react-daisyui';
import { ArrowLeft, Icon } from 'react-feather';
import { useNavigate } from 'react-router-dom';
import { ReactNode } from 'react';

interface HeaderProps {
title: string;
Icon?: Icon;
onIconClick: () => void;
back: false;
}

interface BackHeaderProps {
title: string;
back: string;
}

interface RawTrail {
trailing?: ReactNode;
}

export function Header({
trailing,
title,
back,
...rest
}: RawTrail & (HeaderProps | BackHeaderProps)) {
const nav = useNavigate();
const { Icon, onIconClick } = rest as HeaderProps;
const navBack = () => {
nav('..');
};
return (
<div className={`flex items-center ${back ? '' : 'justify-between'}`}>
{back && (
<Button color="ghost" onClick={navBack} shape="circle">
<ArrowLeft />
</Button>
)}
<h1 className="text-2xl ml-2">{title}</h1>
<div className="mx-auto" />
{!back && Icon && (
<Button color="ghost" onClick={onIconClick} shape="circle">
<Icon />
</Button>
)}
{trailing}
</div>
);
}
37 changes: 37 additions & 0 deletions src/components/notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
selectNotification,
removeNotification,
useAppDispatch,
useAppSelector,
} from '@store';
import { Alert, Button, Toast } from 'react-daisyui';
import { useCallback } from 'react';
import { X } from 'react-feather';

export function Notification() {
const notifications = useAppSelector(selectNotification);
const dispatch = useAppDispatch();
const remove = useCallback(
(id: string) => () => {
dispatch(removeNotification(id));
},
[dispatch]
);
return (
<Toast horizontal="start" vertical="bottom">
{notifications.map((notification) => (
<Alert key={notification.id} status="error">
<Button
onClick={remove(notification.id)}
size="sm"
color="ghost"
shape="circle"
>
<X />
</Button>
<span>{notification.message}</span>
</Alert>
))}
</Toast>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Scan } from '@api/scans.api.gen.ts';
import type { Scan } from '@api';
import { Button, Pagination } from 'react-daisyui';
import { ArrowLeft, ArrowRight } from 'react-feather';

Expand All @@ -9,7 +9,7 @@ export interface ScanListDumpProps {
onPrev: () => void;
}

export function ScanListDump({
export function ScanListSimple({
scans,
onPrev,
onNext,
Expand All @@ -25,11 +25,13 @@ export function ScanListDump({
))}

<Pagination>
<Button onClick={onPrev} className="join-item">
<Button color="primary" onClick={onPrev} className="join-item">
<ArrowLeft />
</Button>
<Button className="join-item">Page {page}</Button>
<Button onClick={onNext} className="join-item">
<Button color="primary" className="join-item">
Page {page}
</Button>
<Button color="primary" onClick={onNext} className="join-item">
<ArrowRight />
</Button>
</Pagination>
Expand Down
10 changes: 0 additions & 10 deletions src/components/theme-wrapper.tsx

This file was deleted.

16 changes: 16 additions & 0 deletions src/components/to-list-scan.button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCallback } from 'react';
import { Button } from 'react-daisyui';
import { ArrowRight } from 'react-feather';
import { useNavigate } from 'react-router-dom';

export default function ToListScanButton() {
const navigate = useNavigate();
const toScans = useCallback(() => navigate('/scans'), [navigate]);

return (
<Button fullWidth color="primary" onClick={toScans}>
<span>To Scans</span>
<ArrowRight />
</Button>
);
}
Loading

0 comments on commit befaccd

Please sign in to comment.