diff --git a/README.md b/README.md index 145ae3de..f3aee2ed 100755 --- a/README.md +++ b/README.md @@ -107,9 +107,9 @@ yarn run start:frontend # in another terminal ## 📸 Screenshots -| Job Configuration | Job Analytics | Job Overview | -|-------------------|--------------|--------------| -| ![Screenshot showing job configuration in Fredy](doc/screenshot1.png) | ![Screenshot showing job analytics in Fredy](doc/screenshot_2.png) | ![Screenshot showing job overview in Fredy](doc/screenshot_3.png) | +| Fredy Main Overview | Job Configuration | Found Listings | +|--------------------------------------------------|-----------------------------------------------------------------------|-----------------------------------------------------------------------------| +| ![Screenshot showing Fredy](doc/screenshot1.png) | ![Screenshot showing job configuration in Fredy](doc/screenshot3.png) | ![Screenshot showing found listings in Fredy](doc/screenshot2.png) | ------------------------------------------------------------------------ diff --git a/doc/screenshot1.png b/doc/screenshot1.png index 1dd9a977..65859cb0 100644 Binary files a/doc/screenshot1.png and b/doc/screenshot1.png differ diff --git a/doc/screenshot2.png b/doc/screenshot2.png new file mode 100644 index 00000000..21e92b80 Binary files /dev/null and b/doc/screenshot2.png differ diff --git a/doc/screenshot3.png b/doc/screenshot3.png new file mode 100644 index 00000000..939d0d67 Binary files /dev/null and b/doc/screenshot3.png differ diff --git a/doc/screenshot_2.png b/doc/screenshot_2.png deleted file mode 100644 index 2658fa0f..00000000 Binary files a/doc/screenshot_2.png and /dev/null differ diff --git a/doc/screenshot_3.png b/doc/screenshot_3.png deleted file mode 100644 index c407c2d3..00000000 Binary files a/doc/screenshot_3.png and /dev/null differ diff --git a/lib/api/routes/versionRouter.js b/lib/api/routes/versionRouter.js index 4a685e64..9810fbcb 100644 --- a/lib/api/routes/versionRouter.js +++ b/lib/api/routes/versionRouter.js @@ -8,7 +8,14 @@ const versionRouter = service.newRouter(); versionRouter.get('/', async (req, res) => { const versionPayload = await getCurrentVersionFromGithub(); - res.body = versionPayload == null ? { newVersion: false } : versionPayload; + const localFredyVersion = await getPackageVersion(); + res.body = + versionPayload == null + ? { + newVersion: false, + localFredyVersion, + } + : versionPayload; res.send(); }); diff --git a/package.json b/package.json index 02ac9dce..f8e9500e 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fredy", - "version": "12.3.2", + "version": "14.0.0", "description": "[F]ind [R]eal [E]states [d]amn eas[y].", "scripts": { "prepare": "husky", @@ -62,7 +62,7 @@ "@visactor/react-vchart": "^2.0.5", "@visactor/vchart": "^2.0.5", "@visactor/vchart-semi-theme": "^1.12.2", - "@vitejs/plugin-react": "5.0.3", + "@vitejs/plugin-react": "5.0.4", "better-sqlite3": "^12.4.1", "body-parser": "2.2.0", "cheerio": "^1.1.2", @@ -82,8 +82,8 @@ "query-string": "9.3.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-router": "7.9.2", - "react-router-dom": "7.9.2", + "react-router": "7.9.3", + "react-router-dom": "7.9.3", "restana": "5.1.0", "semver": "^7.7.2", "serve-static": "2.2.0", @@ -97,7 +97,7 @@ "@babel/eslint-parser": "7.28.4", "@babel/preset-env": "7.28.3", "@babel/preset-react": "7.27.1", - "chai": "6.0.1", + "chai": "6.2.0", "eslint": "9.36.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-react": "7.37.5", @@ -105,7 +105,7 @@ "history": "5.3.0", "husky": "9.1.7", "less": "4.4.1", - "lint-staged": "16.2.1", + "lint-staged": "16.2.3", "mocha": "11.7.2", "nodemon": "^3.1.10", "prettier": "3.6.2" diff --git a/ui/src/App.jsx b/ui/src/App.jsx index fbfab609..8efd5faa 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -8,18 +8,19 @@ import UserMutator from './views/user/mutation/UserMutator'; import JobInsight from './views/jobs/insights/JobInsight.jsx'; import { useActions, useSelector } from './services/state/store'; import { Routes, Route, Navigate } from 'react-router-dom'; -import Logout from './components/logout/Logout'; -import Logo from './components/logo/Logo'; -import Menu from './components/menu/Menu'; import Login from './views/login/Login'; import Users from './views/user/Users'; import Jobs from './views/jobs/Jobs'; import './App.less'; import TrackingModal from './components/tracking/TrackingModal.jsx'; -import { Banner } from '@douyinfe/semi-ui'; +import { Banner, Divider } from '@douyinfe/semi-ui'; import VersionBanner from './components/version/VersionBanner.jsx'; import Listings from './views/listings/Listings.jsx'; +import Navigation from './components/navigation/Navigation.jsx'; +import { Layout } from '@douyinfe/semi-ui'; +import FredyFooter from './components/footer/FredyFooter.jsx'; +import ProcessingTimes from './views/jobs/ProcessingTimes.jsx'; export default function FredyApp() { const actions = useActions(); @@ -27,6 +28,7 @@ export default function FredyApp() { const currentUser = useSelector((state) => state.user.currentUser); const versionUpdate = useSelector((state) => state.versionUpdate.versionUpdate); const settings = useSelector((state) => state.generalSettings.settings); + const processingTimes = useSelector((state) => state.jobs.processingTimes); useEffect(() => { async function init() { @@ -50,6 +52,7 @@ export default function FredyApp() { }; const isAdmin = () => currentUser != null && currentUser.isAdmin; + const { Footer, Sider, Content } = Layout; return loading ? null : needsLogin() ? ( @@ -57,71 +60,80 @@ export default function FredyApp() { } /> ) : ( -
-
- - - - {versionUpdate?.newVersion && } - {settings.demoMode && ( - <> - -
- - )} - {settings.analyticsEnabled === null && !settings.demoMode && } - - } /> - } /> - } /> - } /> - } /> - } /> + + + + + + + {versionUpdate?.newVersion && } + {settings.demoMode && ( + <> + +
+ + )} + {settings.analyticsEnabled === null && !settings.demoMode && } + {processingTimes != null && } + +
+ + } /> + } /> + } /> + } /> + } /> + } /> - {/* Permission-aware routes */} - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> + {/* Permission-aware routes */} + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> - } /> - -
-
+ } /> + +
+ + + + ); } diff --git a/ui/src/App.less b/ui/src/App.less index c3bc3011..b9e881e8 100644 --- a/ui/src/App.less +++ b/ui/src/App.less @@ -1,12 +1,9 @@ .app { - display: flex; - flex-direction: column; + height: 100%; width: 100%; - &__container { - padding: 1rem 1rem; - color: var(--semi-color-text-0); - background-color: #232429; + &__content { + margin: 1rem; } } diff --git a/ui/src/components/footer/FredyFooter.jsx b/ui/src/components/footer/FredyFooter.jsx new file mode 100644 index 00000000..a5589ed3 --- /dev/null +++ b/ui/src/components/footer/FredyFooter.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import './FredyFooter.less'; +import { useSelector } from '../../services/state/store.js'; +import { Typography } from '@douyinfe/semi-ui'; + +export default function FredyFooter() { + const { Text } = Typography; + const version = useSelector((state) => state.versionUpdate.versionUpdate); + return ( +
+
+ Fredy V{version?.localFredyVersion || 'N/A'} +
+
+ Made with ❤️ +
+
+ ); +} diff --git a/ui/src/components/footer/FredyFooter.less b/ui/src/components/footer/FredyFooter.less new file mode 100644 index 00000000..5909fa8e --- /dev/null +++ b/ui/src/components/footer/FredyFooter.less @@ -0,0 +1,17 @@ +.fredyFooter { + background:rgb(53, 54, 60); + color: white; + display: flex; + justify-content: space-between; + align-items: center; + + &__version { + padding-left: .5rem; + font-size: small; + + } + &__copyRight { + padding-right: 1rem; + + } +} \ No newline at end of file diff --git a/ui/src/components/logout/Logout.jsx b/ui/src/components/logout/Logout.jsx index 24d41850..04908ddf 100644 --- a/ui/src/components/logout/Logout.jsx +++ b/ui/src/components/logout/Logout.jsx @@ -2,19 +2,22 @@ import React from 'react'; import { Button } from '@douyinfe/semi-ui'; import { xhrPost } from '../../services/xhr'; import { IconUser } from '@douyinfe/semi-icons'; -const Logout = function Logout() { + +const Logout = function Logout({ text }) { return ( - +
+ +
); }; diff --git a/ui/src/components/menu/Menu.jsx b/ui/src/components/menu/Menu.jsx deleted file mode 100644 index 45683ae0..00000000 --- a/ui/src/components/menu/Menu.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; -import { Tabs, TabPane } from '@douyinfe/semi-ui'; - -import { useLocation } from 'react-router-dom'; -import { IconUser, IconTerminal, IconSetting, IconArchive } from '@douyinfe/semi-icons'; -import './Menu.less'; - -function parsePathName(name) { - const split = name.split('/').filter((s) => s.length !== 0); - return '/' + split[0]; -} - -const TopMenu = function TopMenu({ isAdmin }) { - const navigate = useNavigate(); - const location = useLocation(); - return ( - navigate(key)}> - - - Jobs - - } - /> - - - Found listings - - } - /> - - {isAdmin && ( - - - User - - } - /> - )} - - {isAdmin && ( - - - Settings - - } - /> - )} - - ); -}; - -export default TopMenu; diff --git a/ui/src/components/menu/Menu.less b/ui/src/components/menu/Menu.less deleted file mode 100644 index 34102907..00000000 --- a/ui/src/components/menu/Menu.less +++ /dev/null @@ -1,3 +0,0 @@ -.menu { - margin-top: 3rem; -} diff --git a/ui/src/components/navigation/Navigate.less b/ui/src/components/navigation/Navigate.less new file mode 100644 index 00000000..394b175a --- /dev/null +++ b/ui/src/components/navigation/Navigate.less @@ -0,0 +1,9 @@ +.navigate { + &__logout_Button { + align-items: center; + justify-content: center; + width: 100%; + display: flex; + + } +} \ No newline at end of file diff --git a/ui/src/components/navigation/Navigation.jsx b/ui/src/components/navigation/Navigation.jsx new file mode 100644 index 00000000..7c553a8f --- /dev/null +++ b/ui/src/components/navigation/Navigation.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Nav } from '@douyinfe/semi-ui'; +import { IconUser, IconStar, IconSetting, IconTerminal } from '@douyinfe/semi-icons'; +import logoWhite from '../../assets/logo_white.png'; +import Logout from '../logout/Logout.jsx'; +import { useLocation, useNavigate } from 'react-router-dom'; + +import './Navigate.less'; +import { useScreenWidth } from '../../hooks/screenWidth.js'; + +export default function Navigation({ isAdmin }) { + const navigate = useNavigate(); + const location = useLocation(); + + const width = useScreenWidth(); + const collapsed = width <= 850; + + const items = [ + { itemKey: '/jobs', text: 'Jobs', icon: }, + { itemKey: '/listings', text: 'Found Listings', icon: }, + ]; + + if (isAdmin) { + items.push({ itemKey: '/users', text: 'User Management', icon: }); + items.push({ itemKey: '/generalSettings', text: 'Settings', icon: }); + } + + function parsePathName(name) { + const split = name.split('/').filter((s) => s.length !== 0); + return '/' + split[0]; + } + + return ( +