From eeb4bea9eae8e071e284c2839aafd8628c3a926f Mon Sep 17 00:00:00 2001 From: orangecoding Date: Wed, 28 Jan 2026 10:48:50 +0100 Subject: [PATCH 1/7] improving footer --- ui/src/components/footer/FredyFooter.jsx | 22 +++++++++++++--------- ui/src/components/footer/FredyFooter.less | 21 +++++---------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/ui/src/components/footer/FredyFooter.jsx b/ui/src/components/footer/FredyFooter.jsx index 43701eba..ea8b9a19 100644 --- a/ui/src/components/footer/FredyFooter.jsx +++ b/ui/src/components/footer/FredyFooter.jsx @@ -6,19 +6,23 @@ import React from 'react'; import './FredyFooter.less'; import { useSelector } from '../../services/state/store.js'; -import { Typography } from '@douyinfe/semi-ui-19'; +import { Typography, Layout, Space, Divider } from '@douyinfe/semi-ui-19'; export default function FredyFooter() { const { Text } = Typography; + const { Footer } = Layout; 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 index c0558b28..3efb04cd 100644 --- a/ui/src/components/footer/FredyFooter.less +++ b/ui/src/components/footer/FredyFooter.less @@ -1,20 +1,9 @@ .fredyFooter { - background:rgb(53, 54, 60); - color: white; + background-color: var(--semi-color-bg-1); display: flex; - justify-content: space-between; + justify-content: flex-end; align-items: center; - height: 1.7rem; - border-radius: .3rem; - border-top: 1px solid #45464b; - - &__version { - padding-left: .5rem; - font-size: small; - - } - &__copyRight { - padding-right: 1rem; - - } + padding: 0 1rem; + height: 32px; + border-top: 1px solid var(--semi-color-border); } \ No newline at end of file From c9ea35fda72b4bc98e299c18f16933a7078d7b31 Mon Sep 17 00:00:00 2001 From: orangecoding Date: Wed, 28 Jan 2026 11:09:51 +0100 Subject: [PATCH 2/7] improve ui --- ui/src/App.jsx | 125 +++++++++--------- ui/src/App.less | 44 +----- ui/src/components/cards/DashboardCard.less | 115 ++++++---------- ui/src/components/cards/KpiCard.jsx | 41 +++--- ui/src/components/grid/jobs/JobGrid.jsx | 33 ++--- ui/src/components/grid/jobs/JobGrid.less | 15 ++- .../grid/listings/ListingsGrid.less | 29 +++- ui/src/components/navigation/Navigate.less | 1 + ui/src/components/navigation/Navigation.jsx | 8 +- ui/src/views/dashboard/Dashboard.jsx | 6 +- ui/src/views/dashboard/Dashboard.less | 7 +- 11 files changed, 187 insertions(+), 237 deletions(-) diff --git a/ui/src/App.jsx b/ui/src/App.jsx index a1cbc907..02c26853 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -19,7 +19,7 @@ import Jobs from './views/jobs/Jobs'; import './App.less'; import TrackingModal from './components/tracking/TrackingModal.jsx'; -import { Banner, Divider } from '@douyinfe/semi-ui-19'; +import { Banner } from '@douyinfe/semi-ui-19'; import VersionBanner from './components/version/VersionBanner.jsx'; import Listings from './views/listings/Listings.jsx'; import MapView from './views/listings/Map.jsx'; @@ -59,7 +59,7 @@ export default function FredyApp() { }; const isAdmin = () => currentUser != null && currentUser.isAdmin; - const { Footer, Sider, Content } = Layout; + const { Sider, Content } = Layout; return loading ? null : needsLogin() ? ( @@ -68,11 +68,11 @@ export default function FredyApp() { ) : ( - - - - - + + + + + {versionUpdate?.newVersion && } {settings.demoMode && ( <> @@ -87,68 +87,63 @@ export default function FredyApp() { )} {settings.analyticsEnabled === null && !settings.demoMode && } - -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - {/* Permission-aware routes */} - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> + {/* Permission-aware routes */} + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> - } /> - -
+ } /> +
-
-
-
+
); } diff --git a/ui/src/App.less b/ui/src/App.less index 3168d09b..92f394b3 100644 --- a/ui/src/App.less +++ b/ui/src/App.less @@ -1,47 +1,15 @@ .app { - height: 100%; - width: 100%; + height: 100vh; + width: 100vw; &__content { - margin: 1rem; + padding: 24px; + background-color: var(--semi-color-bg-0); + min-height: calc(100vh - 32px); // 32px is footer height + box-sizing: border-box; } } -.ui.inverted.segment { - background: #31303078 !important; -} - -.ui.black.label, -.ui.black.labels .label { - background-color: #31303078 !important; -} - -a:link { - color: #54a9ff; - background-color: transparent; - text-decoration: none; -} - -a:visited { - color: #54a9ff; - background-color: transparent; - text-decoration: none; -} - -a:hover { - color: #54a9ff; - background-color: transparent; - text-decoration: underline; -} - -a:active { - color: #54a9ff; - background-color: transparent; - text-decoration: underline; -} - -a {outline : none;} - .semi-icon:not(.semi-tabs-bar .semi-tabs-tab .semi-icon) { vertical-align: middle; } diff --git a/ui/src/components/cards/DashboardCard.less b/ui/src/components/cards/DashboardCard.less index f3179f7b..a9182325 100644 --- a/ui/src/components/cards/DashboardCard.less +++ b/ui/src/components/cards/DashboardCard.less @@ -1,92 +1,63 @@ -@import "DashboardCardColors.less"; - -.color-variant(@bg, @border, @text) { - background-color: @bg; - border: 1px solid @border; - color: @text; -} - .dashboard-card { - box-sizing: border-box; - padding: .8rem; - border-radius: .5rem; - border-width: 1px; - font-weight: 600; - box-shadow: 0 6px 20px rgba(0,0,0,0.08); - /* Make all KPI boxes the same size regardless of content/font */ width: 100%; - max-width: none; - height: 10rem; - display: flex; - flex-direction: column; - - &.blue { - .color-variant(@color-blue-bg, @color-blue-border, @color-blue-text); - } - - &.orange { - .color-variant(@color-orange-bg, @color-orange-border, @color-orange-text); - } - - &.green { - .color-variant(@color-green-bg, @color-green-border, @color-green-text); + height: 140px; + margin-bottom: 16px; + transition: transform 0.2s, box-shadow 0.2s; + background-color: rgba(36, 36, 36, 0.9); + backdrop-filter: blur(8px); + border: 1px solid var(--semi-color-border); + + &:hover { + transform: translateY(-4px); + background-color: rgba(36, 36, 36, 1); + + &.blue { + box-shadow: 0 8px 24px -5px var(--semi-color-primary); + } + &.orange { + box-shadow: 0 8px 24px -5px var(--semi-color-warning); + } + &.green { + box-shadow: 0 8px 24px -5px var(--semi-color-success); + } + &.purple { + box-shadow: 0 8px 24px -5px var(--semi-color-info); + } } - &.purple { - .color-variant(@color-purple-bg, @color-purple-border, @color-purple-text); + &__icon { + font-size: 20px; + display: flex; + align-items: center; + justify-content: center; } - &.gray { - .color-variant(@color-gray-bg, @color-gray-border, @color-gray-text); + &__content { + width: 100%; } - &__header { - display: flex; - align-items: center; - gap: .6rem; - /* Keep header from growing content height */ - min-height: 2rem; - overflow: hidden; + &__value { + font-weight: 700; + margin-bottom: 4px; + color: var(--semi-color-text-0); } - &__icon { - border-radius: .6rem; - display: grid; - place-items: center; + &.blue { + box-shadow: 0 4px 20px -5px var(--semi-color-primary); } - &__title { - font-weight: 600; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + &.orange { + box-shadow: 0 4px 20px -5px var(--semi-color-warning); } - &__content { - margin-top: .4rem; - font-size: .7rem; - flex: 1 1 auto; - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; + &.green { + box-shadow: 0 4px 20px -5px var(--semi-color-success); } - &__value { - margin: 0; - font-size: 1.5rem; - line-height: 1.1; - color: #fff; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + &.purple { + box-shadow: 0 4px 20px -5px var(--semi-color-info); } - &__desc { - opacity: .8; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + &.gray { } - } \ No newline at end of file diff --git a/ui/src/components/cards/KpiCard.jsx b/ui/src/components/cards/KpiCard.jsx index a0ab4bad..f96435ec 100644 --- a/ui/src/components/cards/KpiCard.jsx +++ b/ui/src/components/cards/KpiCard.jsx @@ -3,12 +3,8 @@ * Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause */ -/* - * Copyright (c) 2025 by Christian Kellner. - * Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause - */ import React from 'react'; - +import { Card, Typography, Space } from '@douyinfe/semi-ui-19'; import './DashboardCard.less'; export default function KpiCard({ @@ -20,21 +16,28 @@ export default function KpiCard({ color = 'gray', children, }) { + const { Text } = Typography; return ( -
-
-
{icon}
-
- {title} + + + +
{icon}
+ + {title} + +
+
+
+ {value} + {children} +
+ {description && ( + + {description} + + )}
-
-
-

- {value} - {children} -

- {description && {description}} -
-
+ + ); } diff --git a/ui/src/components/grid/jobs/JobGrid.jsx b/ui/src/components/grid/jobs/JobGrid.jsx index 99ec091a..c78a6887 100644 --- a/ui/src/components/grid/jobs/JobGrid.jsx +++ b/ui/src/components/grid/jobs/JobGrid.jsx @@ -185,31 +185,21 @@ const JobGrid = () => { return (
-
- - -
+
} showClear placeholder="Search" onChange={handleFilterChange} /> - -
-
-
+
-
+ {showFilterBar && (
@@ -277,7 +267,6 @@ const JobGrid = () => { diff --git a/ui/src/components/grid/jobs/JobGrid.less b/ui/src/components/grid/jobs/JobGrid.less index 6fb2e6dc..5ce81019 100644 --- a/ui/src/components/grid/jobs/JobGrid.less +++ b/ui/src/components/grid/jobs/JobGrid.less @@ -1,11 +1,16 @@ .jobGrid { &__card { height: 100%; - transition: transform 0.2s; + transition: transform 0.2s, box-shadow 0.2s; + background-color: rgba(36, 36, 36, 0.9); + backdrop-filter: blur(8px); + border: 1px solid var(--semi-color-border); + box-shadow: 0 0 15px -3px rgb(78 78 78 / 50%); &:hover { transform: translateY(-4px); - box-shadow: var(--semi-shadow-elevated); + box-shadow: 0 0 15px -3px rgb(78 78 78 / 70%); + background-color: rgba(36, 36, 36, 1); } } @@ -19,12 +24,14 @@ &__toolbar { &__card { - border-radius: 5px; + border-radius: var(--semi-border-radius-medium); display: flex; flex-direction: column; gap: .3rem; - background: #232429; + background: rgba(36, 36, 36, 0.9); + backdrop-filter: blur(8px); padding: 0.5rem; + border: 1px solid var(--semi-color-border); } } diff --git a/ui/src/components/grid/listings/ListingsGrid.less b/ui/src/components/grid/listings/ListingsGrid.less index 6fd7c1cb..d9a2473a 100644 --- a/ui/src/components/grid/listings/ListingsGrid.less +++ b/ui/src/components/grid/listings/ListingsGrid.less @@ -33,11 +33,15 @@ &__card { height: 100%; - transition: transform 0.2s; + transition: transform 0.2s, box-shadow 0.2s; + background-color: rgba(36, 36, 36, 0.9); + backdrop-filter: blur(8px); + border: 1px solid var(--semi-color-border); &:hover { transform: translateY(-4px); box-shadow: var(--semi-shadow-elevated); + background-color: rgba(36, 36, 36, 1); } &--inactive { @@ -90,13 +94,15 @@ } &__toolbar { - &__card { - border-radius: 5px; + border-radius: var(--semi-border-radius-medium); display: flex; flex-direction: column; gap: .3rem; - background: #232429; + background: rgba(36, 36, 36, 0.9); + backdrop-filter: blur(8px); + padding: 0.5rem; + border: 1px solid var(--semi-color-border); } } @@ -105,7 +111,7 @@ } &__linkButton { - background: var(--semi-color-fill-0); + background: var(--semi-color-primary); font-size: 14px; line-height: 20px; font-weight: 600; @@ -115,5 +121,18 @@ align-items: center; justify-content: center; border-radius: 3px; + + a { + color: white; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + } + + &:hover { + background: var(--semi-color-primary-hover); + } } } diff --git a/ui/src/components/navigation/Navigate.less b/ui/src/components/navigation/Navigate.less index d2936e4b..cd98f273 100644 --- a/ui/src/components/navigation/Navigate.less +++ b/ui/src/components/navigation/Navigate.less @@ -6,5 +6,6 @@ gap: 0.5rem; width: 100%; display: flex; + padding-bottom: 12px; } } \ No newline at end of file diff --git a/ui/src/components/navigation/Navigation.jsx b/ui/src/components/navigation/Navigation.jsx index 5b215aab..5823fa0b 100644 --- a/ui/src/components/navigation/Navigation.jsx +++ b/ui/src/components/navigation/Navigation.jsx @@ -70,20 +70,18 @@ export default function Navigation({ isAdmin }) { return ( <Nav - style={{ height: '100%' }} + style={{ height: '100%', maxWidth: collapsed ? '60px' : '240px' }} items={items} isCollapsed={collapsed} selectedKeys={[parsePathName(location.pathname)]} onSelect={(key) => { navigate(key.itemKey); }} - header={<img src={collapsed ? heart : logoWhite} width={collapsed ? '80' : '160'} alt="Fredy Logo" />} + header={<img src={collapsed ? heart : logoWhite} width={collapsed ? '30' : '120'} alt="Fredy Logo" />} footer={ <Nav.Footer className="navigate__footer"> <Logout text={!collapsed} /> - <Button icon={<IconSidebar />} onClick={() => setCollapsed(!collapsed)}> - {!collapsed && 'Collapse'} - </Button> + <Button icon={<IconSidebar />} onClick={() => setCollapsed(!collapsed)} /> </Nav.Footer> } /> diff --git a/ui/src/views/dashboard/Dashboard.jsx b/ui/src/views/dashboard/Dashboard.jsx index 25388889..40d732eb 100644 --- a/ui/src/views/dashboard/Dashboard.jsx +++ b/ui/src/views/dashboard/Dashboard.jsx @@ -41,10 +41,10 @@ export default function Dashboard() { <div className="dashboard"> <Headline text="Dashboard" size={3} /> - <Row gutter={16} className="dashboard__row"> + <Row gutter={[16, 16]} className="dashboard__row"> <Col span={12} xs={24} sm={24} md={24} lg={24} xl={12}> <SegmentPart name="General" Icon={IconTerminal}> - <Row gutter={16} className="dashboard__row"> + <Row gutter={[16, 16]} className="dashboard__row"> <Col span={12} xs={24} sm={12} md={12} lg={12} xl={12}> <KpiCard title="Search Interval" @@ -104,7 +104,7 @@ export default function Dashboard() { </Col> <Col span={12} xs={24} sm={24} md={24} lg={24} xl={12}> <SegmentPart name="Overview" Icon={IconStar}> - <Row gutter={16} className="dashboard__row"> + <Row gutter={[16, 16]} className="dashboard__row"> <Col span={12} xs={24} sm={12} md={12} lg={12} xl={12}> <KpiCard title="Jobs" diff --git a/ui/src/views/dashboard/Dashboard.less b/ui/src/views/dashboard/Dashboard.less index e3b3c887..a97f9a17 100644 --- a/ui/src/views/dashboard/Dashboard.less +++ b/ui/src/views/dashboard/Dashboard.less @@ -1,11 +1,10 @@ .dashboard { &__row { - margin-bottom: 1rem; - /* Ensure grid items wrap to next line on narrow screens */ + margin-bottom: 24px; flex-wrap: wrap; - /* Vertical gap of 1rem between wrapped grid items (no px) */ + .semi-col { - margin-bottom: 1rem; + margin-bottom: 0; // Handled by Row gutter } } } From 68ea931eadb11c82af8bea38087abdc8276d7fe0 Mon Sep 17 00:00:00 2001 From: orangecoding <christian.kellner.privat@gmail.com> Date: Wed, 28 Jan 2026 11:10:52 +0100 Subject: [PATCH 3/7] upgrading dependencies --- package.json | 8 ++--- yarn.lock | 88 ++++++++++++++++++++++++---------------------------- 2 files changed, 44 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 48354ed9..246fca5b 100755 --- a/package.json +++ b/package.json @@ -72,20 +72,20 @@ "cookie-session": "2.1.1", "handlebars": "4.7.8", "lodash": "4.17.23", - "maplibre-gl": "^5.16.0", + "maplibre-gl": "^5.17.0", "nanoid": "5.1.6", "node-cron": "^4.2.1", "node-fetch": "3.3.2", "node-mailjet": "6.0.11", "p-throttle": "^8.1.0", "package-up": "^5.0.0", - "puppeteer": "^24.36.0", + "puppeteer": "^24.36.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "query-string": "9.3.1", - "react": "19.2.3", + "react": "19.2.4", "react-chartjs-2": "^5.3.1", - "react-dom": "19.2.3", + "react-dom": "19.2.4", "react-range-slider-input": "^3.3.2", "react-router": "7.13.0", "react-router-dom": "7.13.0", diff --git a/yarn.lock b/yarn.lock index 72b6ec52..9e748bee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1384,6 +1384,11 @@ resolved "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz" integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== +"@maplibre/geojson-vt@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz#c5f301a5d227cecf0bf4d1ab9239b8b0b13e78fe" + integrity sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ== + "@maplibre/maplibre-gl-style-spec@^24.4.1": version "24.4.1" resolved "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.4.1.tgz" @@ -1404,16 +1409,16 @@ dependencies: "@mapbox/point-geometry" "^1.1.0" -"@maplibre/vt-pbf@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.2.0.tgz" - integrity sha512-bxrk/kQUwWXZgmqYgwOCnZCMONCRi3MJMqJdza4T3E4AeR5i+VyMnaJ8iDWtWxdfEAJRtrzIOeJtxZSy5mFrFA== +"@maplibre/vt-pbf@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@maplibre/vt-pbf/-/vt-pbf-4.2.1.tgz#395d97bd5de68b5efabf0d56c535163bb88f75c7" + integrity sha512-IxZBGq/+9cqf2qdWlFuQ+ZfoMhWpxDUGQZ/poPHOJBvwMUT1GuxLo6HgYTou+xxtsOsjfbcjI8PZaPCtmt97rA== dependencies: "@mapbox/point-geometry" "^1.1.0" "@mapbox/vector-tile" "^2.0.4" - "@types/geojson-vt" "3.2.5" + "@maplibre/geojson-vt" "^5.0.4" + "@types/geojson" "^7946.0.16" "@types/supercluster" "^7.1.3" - geojson-vt "^4.0.2" pbf "^4.0.1" supercluster "^8.0.1" @@ -1460,10 +1465,10 @@ resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@puppeteer/browsers@2.11.1": - version "2.11.1" - resolved "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.1.tgz" - integrity sha512-YmhAxs7XPuxN0j7LJloHpfD1ylhDuFmmwMvfy/+6nBSrETT2ycL53LrhgPtR+f+GcPSybQVuQ5inWWu5MrWCpA== +"@puppeteer/browsers@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.11.2.tgz#54ac339579c23014535011e6dc04bf3157705d73" + integrity sha512-GBY0+2lI9fDrjgb5dFL9+enKXqyOPok9PXg/69NVkjW3bikbK9RQrNrI3qccQXmDNN7ln4j/yL89Qgvj/tfqrw== dependencies: debug "^4.4.3" extract-zip "^2.0.1" @@ -1746,13 +1751,6 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/geojson-vt@3.2.5": - version "3.2.5" - resolved "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz" - integrity sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g== - dependencies: - "@types/geojson" "*" - "@types/geojson@*", "@types/geojson@^7946.0.16": version "7946.0.16" resolved "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz" @@ -3591,11 +3589,6 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -geojson-vt@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz" - integrity sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A== - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" @@ -4578,10 +4571,10 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -maplibre-gl@^5.16.0: - version "5.16.0" - resolved "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.16.0.tgz" - integrity sha512-/VDY89nr4jgLJyzmhy325cG6VUI02WkZ/UfVuDbG/piXzo6ODnM+omDFIwWY8tsEsBG26DNDmNMn3Y2ikHsBiA== +maplibre-gl@^5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-5.17.0.tgz#b7de18caf2c70d0ba98715803eea7f1e39581c36" + integrity sha512-gwS6NpXBfWD406dtT5YfEpl2hmpMm+wcPqf04UAez/TxY1OBjiMdK2ZoMGcNIlGHelKc4+Uet6zhDdDEnlJVHA== dependencies: "@mapbox/geojson-rewind" "^0.5.2" "@mapbox/jsonlint-lines-primitives" "^2.0.2" @@ -4590,14 +4583,13 @@ maplibre-gl@^5.16.0: "@mapbox/unitbezier" "^0.0.1" "@mapbox/vector-tile" "^2.0.4" "@mapbox/whoots-js" "^3.1.0" + "@maplibre/geojson-vt" "^5.0.4" "@maplibre/maplibre-gl-style-spec" "^24.4.1" "@maplibre/mlt" "^1.1.2" - "@maplibre/vt-pbf" "^4.2.0" + "@maplibre/vt-pbf" "^4.2.1" "@types/geojson" "^7946.0.16" - "@types/geojson-vt" "3.2.5" "@types/supercluster" "^7.1.3" earcut "^3.0.2" - geojson-vt "^4.0.2" gl-matrix "^3.4.4" kdbush "^4.0.2" murmurhash-js "^1.0.0" @@ -6019,12 +6011,12 @@ punycode@^2.1.0: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@24.36.0: - version "24.36.0" - resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.36.0.tgz" - integrity sha512-P3Ou0MAFDCQ0dK1d9F9+8jTrg6JvXjUacgG0YkJQP4kbEnUOGokSDEMmMId5ZhXD5HwsHM202E9VwEpEjWfwxg== +puppeteer-core@24.36.1: + version "24.36.1" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-24.36.1.tgz#8d60c80a27b86b3d1d948155ecab2deb404cc00f" + integrity sha512-L7ykMWc3lQf3HS7ME3PSjp7wMIjJeW6+bKfH/RSTz5l6VUDGubnrC2BKj3UvM28Y5PMDFW0xniJOZHBZPpW1dQ== dependencies: - "@puppeteer/browsers" "2.11.1" + "@puppeteer/browsers" "2.11.2" chromium-bidi "13.0.1" debug "^4.4.3" devtools-protocol "0.0.1551306" @@ -6079,16 +6071,16 @@ puppeteer-extra@^3.3.6: debug "^4.1.1" deepmerge "^4.2.2" -puppeteer@^24.36.0: - version "24.36.0" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-24.36.0.tgz" - integrity sha512-BD/VCyV/Uezvd6o7Fd1DmEJSxTzofAKplzDy6T9d4WbLTQ5A+06zY7VwO91ZlNU22vYE8sidVEsTpTrKc+EEnQ== +puppeteer@^24.36.1: + version "24.36.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-24.36.1.tgz#55b328215090b9617eb71ca541232c14a60c14cb" + integrity sha512-uPiDUyf7gd7Il1KnqfNUtHqntL0w1LapEw5Zsuh8oCK8GsqdxySX1PzdIHKB2Dw273gWY4MW0zC5gy3Re9XlqQ== dependencies: - "@puppeteer/browsers" "2.11.1" + "@puppeteer/browsers" "2.11.2" chromium-bidi "13.0.1" cosmiconfig "^9.0.0" devtools-protocol "0.0.1551306" - puppeteer-core "24.36.0" + puppeteer-core "24.36.1" typed-query-selector "^2.12.0" qs@^6.14.1: @@ -6149,10 +6141,10 @@ react-chartjs-2@^5.3.1: resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.1.tgz" integrity sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A== -react-dom@19.2.3: - version "19.2.3" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz" - integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== +react-dom@19.2.4: + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.4.tgz#6fac6bd96f7db477d966c7ec17c1a2b1ad8e6591" + integrity sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ== dependencies: scheduler "^0.27.0" @@ -6213,10 +6205,10 @@ react-window@^1.8.2: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@19.2.3: - version "19.2.3" - resolved "https://registry.npmjs.org/react/-/react-19.2.3.tgz" - integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== +react@19.2.4: + version "19.2.4" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.4.tgz#438e57baa19b77cb23aab516cf635cd0579ee09a" + integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.2" From 9dc03bf83a63a40a9fae64d57799a37e07198770 Mon Sep 17 00:00:00 2001 From: orangecoding <christian.kellner.privat@gmail.com> Date: Wed, 28 Jan 2026 11:12:40 +0100 Subject: [PATCH 4/7] adding glow to all boxes on dashboard --- ui/src/components/cards/DashboardCard.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/components/cards/DashboardCard.less b/ui/src/components/cards/DashboardCard.less index a9182325..4e3b0edc 100644 --- a/ui/src/components/cards/DashboardCard.less +++ b/ui/src/components/cards/DashboardCard.less @@ -23,6 +23,9 @@ &.purple { box-shadow: 0 8px 24px -5px var(--semi-color-info); } + &.gray { + box-shadow: 0 8px 24px -5px rgba(255, 255, 255, 0.4); + } } &__icon { @@ -59,5 +62,6 @@ } &.gray { + box-shadow: 0 4px 20px -5px rgba(255, 255, 255, 0.2); } } \ No newline at end of file From 8b80bbf00f1bcd159e4e2e82160c3c92bf2e5229 Mon Sep 17 00:00:00 2001 From: orangecoding <christian.kellner.privat@gmail.com> Date: Wed, 28 Jan 2026 13:17:50 +0100 Subject: [PATCH 5/7] introducing single listing view --- index.html | 2 +- lib/api/routes/listingsRouter.js | 12 + lib/services/storage/listingsStorage.js | 26 ++ ui/src/App.jsx | 4 +- ui/src/App.less | 16 +- ui/src/components/footer/FredyFooter.less | 3 + ui/src/components/grid/jobs/JobGrid.jsx | 10 +- .../components/grid/listings/ListingsGrid.jsx | 18 +- ui/src/components/table/ProviderTable.jsx | 8 +- ui/src/services/state/store.js | 13 + ui/src/views/dashboard/Dashboard.jsx | 2 +- ui/src/views/listings/ListingDetail.jsx | 383 ++++++++++++++++++ ui/src/views/listings/ListingDetail.less | 110 +++++ ui/src/views/listings/Map.jsx | 29 +- ui/src/views/listings/Map.less | 28 +- 15 files changed, 643 insertions(+), 21 deletions(-) create mode 100644 ui/src/views/listings/ListingDetail.jsx create mode 100644 ui/src/views/listings/ListingDetail.less diff --git a/index.html b/index.html index 9804b342..0e4f9fd7 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="google" content="notranslate" /> - <meta name="apple-mobile-web-app-capable" content="yes" /> + <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <title>Fredy || Real Estate Finder diff --git a/lib/api/routes/listingsRouter.js b/lib/api/routes/listingsRouter.js index ef0af828..4de113d4 100644 --- a/lib/api/routes/listingsRouter.js +++ b/lib/api/routes/listingsRouter.js @@ -74,6 +74,18 @@ listingsRouter.get('/map', async (req, res) => { res.send(); }); +listingsRouter.get('/:listingId', async (req, res) => { + const { listingId } = req.params; + const listing = listingStorage.getListingById(listingId, req.session.currentUser, isAdminFn(req)); + if (!listing) { + res.statusCode = 404; + res.body = { message: 'Listing not found' }; + return res.send(); + } + res.body = listing; + res.send(); +}); + // Toggle watch state for the current user on a listing listingsRouter.post('/watch', async (req, res) => { try { diff --git a/lib/services/storage/listingsStorage.js b/lib/services/storage/listingsStorage.js index e3e6b1bb..07b50d89 100755 --- a/lib/services/storage/listingsStorage.js +++ b/lib/services/storage/listingsStorage.js @@ -566,3 +566,29 @@ export const updateListingDistance = (id, distance) => { { id, distance }, ); }; + +/** + * Return a single listing by id. + * + * @param {string} id + * @param {string} userId + * @param {boolean} isAdmin + * @returns {Object|null} + */ +export const getListingById = (id, userId = null, isAdmin = false) => { + const params = { id, userId: userId || '__NO_USER__' }; + let whereScoping = ''; + if (!isAdmin) { + whereScoping = `AND (j.user_id = @userId OR EXISTS (SELECT 1 FROM json_each(j.shared_with_user) AS sw WHERE sw.value = @userId))`; + } + return ( + SqliteConnection.query( + `SELECT l.*, j.name AS job_name, CASE WHEN wl.id IS NOT NULL THEN 1 ELSE 0 END AS isWatched + FROM listings l + LEFT JOIN jobs j ON j.id = l.job_id + LEFT JOIN watch_list wl ON wl.listing_id = l.id AND wl.user_id = @userId + WHERE l.id = @id AND l.manually_deleted = 0 ${whereScoping}`, + params, + )[0] || null + ); +}; diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 02c26853..1e65c2bb 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -28,6 +28,7 @@ import { Layout } from '@douyinfe/semi-ui-19'; import FredyFooter from './components/footer/FredyFooter.jsx'; import WatchlistManagement from './views/listings/management/WatchlistManagement.jsx'; import Dashboard from './views/dashboard/Dashboard.jsx'; +import ListingDetail from './views/listings/ListingDetail.jsx'; export default function FredyApp() { const actions = useActions(); @@ -71,7 +72,7 @@ export default function FredyApp() { - + {versionUpdate?.newVersion && } {settings.demoMode && ( @@ -94,6 +95,7 @@ export default function FredyApp() { } /> } /> } /> + } /> } /> } /> diff --git a/ui/src/App.less b/ui/src/App.less index 92f394b3..dbdfeec3 100644 --- a/ui/src/App.less +++ b/ui/src/App.less @@ -2,11 +2,25 @@ height: 100vh; width: 100vw; + &__main { + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; + } + &__content { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + position: relative; padding: 24px; background-color: var(--semi-color-bg-0); - min-height: calc(100vh - 32px); // 32px is footer height box-sizing: border-box; + + @media (max-width: 768px) { + padding: 12px; + } } } diff --git a/ui/src/components/footer/FredyFooter.less b/ui/src/components/footer/FredyFooter.less index 3efb04cd..037ea344 100644 --- a/ui/src/components/footer/FredyFooter.less +++ b/ui/src/components/footer/FredyFooter.less @@ -6,4 +6,7 @@ padding: 0 1rem; height: 32px; border-top: 1px solid var(--semi-color-border); + z-index: 1000; + position: relative; + flex-shrink: 0; } \ No newline at end of file diff --git a/ui/src/components/grid/jobs/JobGrid.jsx b/ui/src/components/grid/jobs/JobGrid.jsx index c78a6887..f2d4e5f2 100644 --- a/ui/src/components/grid/jobs/JobGrid.jsx +++ b/ui/src/components/grid/jobs/JobGrid.jsx @@ -340,6 +340,8 @@ const JobGrid = () => {