mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 20:14:50 +00:00
fix(ui): scope setup skip state by tenant (#372)
Co-authored-by: Minh Nguyen <gbut.277@gmail.com>
This commit is contained in:
1 parent
4868f6c4d8
commit
eaa0aecccf
5 files changed
+66
-2
No files matched your search
@@ -49,6 +49,7 @@ export const LOCAL_STORAGE_KEYS = {
|
||||
SENDER_ID: "goclaw:senderID",
|
||||
TENANT_ID: "goclaw:tenant_id",
|
||||
TENANT_HINT: "goclaw:tenant_hint",
|
||||
SETUP_SKIPPED: "goclaw:setup_skipped",
|
||||
THEME: "goclaw:theme",
|
||||
SIDEBAR_COLLAPSED: "goclaw:sidebarCollapsed",
|
||||
LANGUAGE: "goclaw:language",
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { LOCAL_STORAGE_KEYS } from "@/lib/constants";
|
||||
|
||||
const SETUP_SKIPPED_PREFIX = `${LOCAL_STORAGE_KEYS.SETUP_SKIPPED}:`;
|
||||
|
||||
interface SetupSkipScope {
|
||||
tenantId?: string;
|
||||
tenantSlug?: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
function getScopePart(value: string | null | undefined, fallback: string) {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed && trimmed.length > 0 ? trimmed : fallback;
|
||||
}
|
||||
|
||||
function getScopedSetupSkipKey(scope: SetupSkipScope) {
|
||||
const tenantScope = getScopePart(
|
||||
scope.tenantId ?? scope.tenantSlug ?? localStorage.getItem(LOCAL_STORAGE_KEYS.TENANT_ID),
|
||||
"default",
|
||||
);
|
||||
const userScope = getScopePart(
|
||||
scope.userId ?? localStorage.getItem(LOCAL_STORAGE_KEYS.USER_ID),
|
||||
"anonymous",
|
||||
);
|
||||
|
||||
return `${SETUP_SKIPPED_PREFIX}${encodeURIComponent(tenantScope)}:${encodeURIComponent(userScope)}`;
|
||||
}
|
||||
|
||||
export function isSetupSkipped(scope: SetupSkipScope) {
|
||||
if (localStorage.getItem(LOCAL_STORAGE_KEYS.SETUP_SKIPPED) !== null) {
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEYS.SETUP_SKIPPED);
|
||||
}
|
||||
return localStorage.getItem(getScopedSetupSkipKey(scope)) === "1";
|
||||
}
|
||||
|
||||
export function markSetupSkipped(scope: SetupSkipScope) {
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEYS.SETUP_SKIPPED);
|
||||
localStorage.setItem(getScopedSetupSkipKey(scope), "1");
|
||||
}
|
||||
|
||||
export function clearSetupSkippedState() {
|
||||
const keysToRemove: string[] = [];
|
||||
for (let i = 0; i < localStorage.length; i += 1) {
|
||||
const key = localStorage.key(i);
|
||||
if (!key) continue;
|
||||
if (key === LOCAL_STORAGE_KEYS.SETUP_SKIPPED || key.startsWith(SETUP_SKIPPED_PREFIX)) {
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
||||
}
|
||||
@@ -2,11 +2,15 @@ import { useMemo } from "react";
|
||||
import { useProviders } from "@/pages/providers/hooks/use-providers";
|
||||
import { useAgents } from "@/pages/agents/hooks/use-agents";
|
||||
import { useAuthStore } from "@/stores/use-auth-store";
|
||||
import { isSetupSkipped } from "@/lib/setup-skip";
|
||||
|
||||
export type SetupStep = 1 | 2 | 3 | 4 | "complete";
|
||||
|
||||
export function useBootstrapStatus() {
|
||||
const connected = useAuthStore((s) => s.connected);
|
||||
const userId = useAuthStore((s) => s.userId);
|
||||
const tenantId = useAuthStore((s) => s.tenantId);
|
||||
const tenantSlug = useAuthStore((s) => s.tenantSlug);
|
||||
const { providers, loading: providersLoading } = useProviders();
|
||||
const { agents, loading: agentsLoading } = useAgents();
|
||||
|
||||
@@ -23,13 +27,13 @@ export function useBootstrapStatus() {
|
||||
const hasAgent = agents.length > 0;
|
||||
|
||||
// Allow skipping setup entirely via localStorage
|
||||
const skipped = localStorage.getItem("setup_skipped") === "1";
|
||||
const skipped = isSetupSkipped({ userId, tenantId, tenantSlug });
|
||||
if (skipped) return { needsSetup: false, currentStep: "complete" as SetupStep };
|
||||
|
||||
if (!hasProvider) return { needsSetup: true, currentStep: 1 as SetupStep };
|
||||
if (!hasAgent) return { needsSetup: true, currentStep: 2 as SetupStep };
|
||||
return { needsSetup: false, currentStep: "complete" as SetupStep };
|
||||
}, [loading, providers, agents]);
|
||||
}, [loading, providers, agents, userId, tenantId, tenantSlug]);
|
||||
|
||||
return { needsSetup, currentStep, loading, providers, agents };
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import { StepChannel } from "./step-channel";
|
||||
import { SetupCompleteModal } from "./setup-complete-modal";
|
||||
import { Building2 } from "lucide-react";
|
||||
import { ROUTES, SUPPORTED_LANGUAGES, LANGUAGE_LABELS, LOCAL_STORAGE_KEYS } from "@/lib/constants";
|
||||
import { markSetupSkipped } from "@/lib/setup-skip";
|
||||
import { useAuthStore } from "@/stores/use-auth-store";
|
||||
import { useUiStore } from "@/stores/use-ui-store";
|
||||
import { useTenants } from "@/hooks/use-tenants";
|
||||
import type { ProviderData } from "@/types/provider";
|
||||
@@ -78,6 +80,8 @@ function TenantSwitcher() {
|
||||
export function SetupPage() {
|
||||
const { t } = useTranslation("setup");
|
||||
const navigate = useNavigate();
|
||||
const userId = useAuthStore((s) => s.userId);
|
||||
const { currentTenantId, currentTenantSlug } = useTenants();
|
||||
const { currentStep, loading, providers, agents } = useBootstrapStatus();
|
||||
const [step, setStep] = useState<1 | 2 | 3 | 4>(1);
|
||||
const [createdProvider, setCreatedProvider] = useState<ProviderData | null>(null);
|
||||
@@ -170,6 +174,7 @@ export function SetupPage() {
|
||||
className="text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground transition-colors"
|
||||
onClick={() => {
|
||||
if (window.confirm(t("skipSetupConfirm"))) {
|
||||
markSetupSkipped({ userId, tenantId: currentTenantId, tenantSlug: currentTenantSlug });
|
||||
navigate(ROUTES.OVERVIEW, { replace: true });
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { LOCAL_STORAGE_KEYS } from "@/lib/constants";
|
||||
import { clearSetupSkippedState } from "@/lib/setup-skip";
|
||||
import type { TenantMembership } from "@/types/tenant";
|
||||
|
||||
type UserRole = "admin" | "operator" | "viewer" | "";
|
||||
@@ -80,6 +81,7 @@ export const useAuthStore = create<AuthState>((set) => ({
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEYS.SENDER_ID);
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEYS.TENANT_ID);
|
||||
localStorage.removeItem(LOCAL_STORAGE_KEYS.TENANT_HINT);
|
||||
clearSetupSkippedState();
|
||||
set({
|
||||
token: "", userId: "", senderID: "", connected: false, role: "", serverInfo: null,
|
||||
tenantId: "", tenantName: "", tenantSlug: "", isCrossTenant: false, availableTenants: [],
|
||||
|
||||
Reference in new issue
Block a user