Feat(Fix): Multi Org, Suspension, User Detail

Multi Org switch, members suspend/unsuspend status, delete account, next serial, show email in user member search
This commit is contained in:
2026-03-02 23:55:47 +05:45
parent 6cab506603
commit b97937f080
16 changed files with 2011 additions and 298 deletions
+76
View File
@@ -0,0 +1,76 @@
import {
createContext,
useContext,
useState,
useEffect,
useCallback,
ReactNode,
} from "react";
import { useQueryClient } from "@tanstack/react-query";
import { Organization } from "@/lib/api";
import { useOrganizations } from "@/hooks/useOrganizations";
import { useAuth } from "@/contexts/AuthContext";
interface OrgContextType {
/** The currently selected organisation (null while loading or no memberships). */
selectedOrg: Organization | null;
/** Programmatically switch the active organisation and invalidate all org-scoped queries. */
selectOrg: (org: Organization) => void;
/** Convenience accessor for the selected org's ID. */
selectedOrgId: string | null;
}
const OrgContext = createContext<OrgContextType | null>(null);
export function OrgProvider({ children }: { children: ReactNode }) {
const { isAuthenticated } = useAuth();
const { data: organizations = [] } = useOrganizations();
const queryClient = useQueryClient();
const [selectedOrg, setSelectedOrg] = useState<Organization | null>(null);
// Auto-select the first org once the list arrives (or when the list changes
// and the previously selected org is no longer present, e.g. after deletion).
useEffect(() => {
if (!isAuthenticated) {
setSelectedOrg(null);
return;
}
if (organizations.length === 0) return;
setSelectedOrg((prev) => {
// Keep the current selection if it still exists in the updated list.
if (prev && organizations.some((o) => o.id === prev.id)) {
// Refresh the object in case name/role changed.
return organizations.find((o) => o.id === prev.id) ?? prev;
}
return organizations[0];
});
}, [organizations, isAuthenticated]);
const selectOrg = useCallback(
(org: Organization) => {
setSelectedOrg(org);
// Invalidate all organisation-scoped React Query caches so every page
// immediately re-fetches data for the newly selected org.
queryClient.invalidateQueries({ queryKey: ["organizations"] });
// Invalidate any queries keyed by the previous org id. The broadest
// approach is to remove all non-organisations queries so pages reload.
queryClient.invalidateQueries();
},
[queryClient],
);
return (
<OrgContext.Provider
value={{ selectedOrg, selectOrg, selectedOrgId: selectedOrg?.id ?? null }}
>
{children}
</OrgContext.Provider>
);
}
export function useOrg(): OrgContextType {
const ctx = useContext(OrgContext);
if (!ctx) throw new Error("useOrg must be used inside <OrgProvider>");
return ctx;
}