From 0890ed0bb841aecfbe5a6bfaa997422aaf91be5d Mon Sep 17 00:00:00 2001
From: Arunavo Ray
Date: Sat, 24 May 2025 10:24:25 +0530
Subject: [PATCH] feat: add live refresh functionality and configuration status
hooks; enhance UI components with new switch and refresh features
---
bun.lock | 7 +
package.json | 1 +
src/components/activity/ActivityList.tsx | 7 +-
src/components/activity/ActivityLog.tsx | 29 +++-
src/components/dashboard/Dashboard.tsx | 128 +++++++++++++-----
src/components/layout/Header.tsx | 42 +++++-
src/components/layout/MainLayout.tsx | 2 +-
src/components/layout/Providers.tsx | 9 +-
src/components/organizations/Organization.tsx | 36 ++++-
src/components/repositories/Repository.tsx | 55 ++++----
src/components/ui/switch.tsx | 29 ++++
src/hooks/useConfigStatus.ts | 84 ++++++++++++
src/hooks/useLiveRefresh.ts | 102 ++++++++++++++
src/hooks/usePageVisibility.ts | 28 ++++
14 files changed, 485 insertions(+), 74 deletions(-)
create mode 100644 src/components/ui/switch.tsx
create mode 100644 src/hooks/useConfigStatus.ts
create mode 100644 src/hooks/useLiveRefresh.ts
create mode 100644 src/hooks/usePageVisibility.ts
diff --git a/bun.lock b/bun.lock
index d165a9f..c4c412a 100644
--- a/bun.lock
+++ b/bun.lock
@@ -17,6 +17,7 @@
"@radix-ui/react-radio-group": "^1.3.6",
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-slot": "^1.2.2",
+ "@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tabs": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.6",
"@tailwindcss/vite": "^4.1.7",
@@ -334,6 +335,8 @@
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="],
+ "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ=="],
+
"@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-roving-focus": "1.1.9", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4FiKSVoXqPP/KfzlB7lwwqoFV6EPwkrrqGp9cUYXjwDYHhvpnqq79P+EPHKcdoTE7Rl8w/+6s9rTlsfXHES9GA=="],
"@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.9", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.6", "@radix-ui/react-portal": "1.1.8", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-slot": "1.2.2", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zYb+9dc9tkoN2JjBDIIPLQtk3gGyz8FMKoqYTb8EMVQ5a5hBcdHPECrsZVI4NpPAUOixhkoqg7Hj5ry5USowfA=="],
@@ -1672,6 +1675,8 @@
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
+ "@radix-ui/react-switch/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
@@ -1768,6 +1773,8 @@
"@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
+ "@radix-ui/react-switch/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
diff --git a/package.json b/package.json
index e4ddc06..bf30b1e 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,7 @@
"@radix-ui/react-radio-group": "^1.3.6",
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-slot": "^1.2.2",
+ "@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tabs": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.6",
"@tailwindcss/vite": "^4.1.7",
diff --git a/src/components/activity/ActivityList.tsx b/src/components/activity/ActivityList.tsx
index cfe7c37..788ea35 100644
--- a/src/components/activity/ActivityList.tsx
+++ b/src/components/activity/ActivityList.tsx
@@ -105,7 +105,7 @@ export default function ActivityList({
? 'Try adjusting your search or filter criteria.'
: 'No mirroring activities have been recorded yet.'}
- {hasFilter ? (
+ {hasFilter && (
- ) : (
-
)}
);
diff --git a/src/components/activity/ActivityLog.tsx b/src/components/activity/ActivityLog.tsx
index aa717f4..380ae4b 100644
--- a/src/components/activity/ActivityLog.tsx
+++ b/src/components/activity/ActivityLog.tsx
@@ -24,6 +24,8 @@ import { ActivityNameCombobox } from './ActivityNameCombobox';
import { useSSE } from '@/hooks/useSEE';
import { useFilterParams } from '@/hooks/useFilterParams';
import { toast } from 'sonner';
+import { useLiveRefresh } from '@/hooks/useLiveRefresh';
+import { useConfigStatus } from '@/hooks/useConfigStatus';
type MirrorJobWithKey = MirrorJob & { _rowKey: string };
@@ -37,6 +39,8 @@ function genKey(job: MirrorJob): string {
export function ActivityLog() {
const { user } = useAuth();
+ const { registerRefreshCallback } = useLiveRefresh();
+ const { isFullyConfigured } = useConfigStatus();
const [activities, setActivities] = useState([]);
const [isLoading, setIsLoading] = useState(false);
@@ -103,6 +107,21 @@ export function ActivityLog() {
fetchActivities();
}, [fetchActivities]);
+ // Register with global live refresh system
+ useEffect(() => {
+ // Only register for live refresh if configuration is complete
+ // Activity logs can exist from previous runs, but new activities won't be generated without config
+ if (!isFullyConfigured) {
+ return;
+ }
+
+ const unregister = registerRefreshCallback(() => {
+ fetchActivities();
+ });
+
+ return unregister;
+ }, [registerRefreshCallback, fetchActivities, isFullyConfigured]);
+
/* ---------------------- filtering + exporting ---------------------- */
const applyLightFilter = (list: MirrorJobWithKey[]) => {
@@ -277,9 +296,13 @@ export function ActivityLog() {
{/* refresh */}
-