fix admin user related issues
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 9s
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 9s
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getDevices } from "@/queries/devices";
|
||||
import BlockDeviceDialog from "../block-device-dialog";
|
||||
@@ -22,7 +22,7 @@ import Pagination from "../pagination";
|
||||
export function AdminDevicesTable() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const user = useAtomValue(userAtom);
|
||||
const isAdmin = user?.is_admin;
|
||||
const isAdmin = isAdminUser(user);
|
||||
|
||||
const page = Number.parseInt(searchParams.get("page") as string) || 1;
|
||||
const limit = 10;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
import { getDevices } from "@/queries/devices";
|
||||
import DevicesTableSkeleton from "./device-table-skeleton";
|
||||
import ClickableRow from "./clickable-row";
|
||||
@@ -27,7 +27,7 @@ export function DevicesTable({
|
||||
}) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const user = useAtomValue(userAtom);
|
||||
const isAdmin = user?.is_admin;
|
||||
const isAdmin = isAdminUser(user);
|
||||
|
||||
const page = Number.parseInt(searchParams.get("page") as string) || 1;
|
||||
const limit = 10;
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
SidebarMenuItem,
|
||||
SidebarRail,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
|
||||
type Permission = {
|
||||
id: number;
|
||||
@@ -141,7 +141,7 @@ export function AppSidebar({
|
||||
];
|
||||
|
||||
let CATEGORIES: Categories;
|
||||
if (user?.is_admin) {
|
||||
if (isAdminUser(user)) {
|
||||
CATEGORIES = categories;
|
||||
} else {
|
||||
// Filter out ADMIN CONTROL category for non-admin users
|
||||
|
||||
@@ -42,6 +42,16 @@ export function getStoredUser(): AuthUser | null {
|
||||
return store.get(userAtom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single source of truth for "is this user an administrator?" on the client.
|
||||
* Mirrors the backend `user_is_admin` helper: any of the app-specific
|
||||
* `is_admin` flag, Django `is_staff`, or `is_superuser` grants admin access.
|
||||
* Use this for every admin route/UI gate instead of checking `is_admin` alone.
|
||||
*/
|
||||
export function isAdminUser(user: AuthUser | null | undefined): boolean {
|
||||
return Boolean(user && (user.is_admin || user.is_staff || user.is_superuser));
|
||||
}
|
||||
|
||||
/** Persist token + user on successful login. */
|
||||
export function setAuth(token: string, user: AuthUser): void {
|
||||
store.set(tokenAtom, token);
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface AuthUser {
|
||||
last_name: string;
|
||||
email: string;
|
||||
is_admin: boolean;
|
||||
is_staff: boolean;
|
||||
is_superuser: boolean;
|
||||
last_login: string;
|
||||
date_joined: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Navigate, useParams } from "react-router-dom";
|
||||
import ClientErrorMessage from "@/components/client-error-message";
|
||||
import FullPageLoader from "@/components/full-page-loader";
|
||||
import UserAgreementForm from "@/components/user/user-agreement-form";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
import { getProfileById } from "@/queries/users";
|
||||
|
||||
export default function UserAgreement() {
|
||||
@@ -17,7 +17,7 @@ export default function UserAgreement() {
|
||||
enabled: !!userId,
|
||||
});
|
||||
|
||||
if (!authUser?.is_admin) return <Navigate to="/devices" replace />;
|
||||
if (!isAdminUser(authUser)) return <Navigate to="/devices" replace />;
|
||||
if (isLoading) return <FullPageLoader />;
|
||||
if (error) return <ClientErrorMessage message={error.message} />;
|
||||
if (!user) return null;
|
||||
|
||||
@@ -80,7 +80,7 @@ export default function UserDetails() {
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-start justify-start">
|
||||
<div id="database-information">
|
||||
<h4 className="p-2 rounded font-semibold">Database Information</h4>
|
||||
<h4 className="p-2 rounded font-semibold">Customer Input Information</h4>
|
||||
<div className="shadow-md p-2 bg-sarLinkOrange/10 border border-dashed border-sarLinkOrange rounded-lg space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<InputReadOnly
|
||||
showCheck
|
||||
@@ -191,15 +191,17 @@ export default function UserDetails() {
|
||||
label="Phone Number"
|
||||
value={nationalData?.primary_contact ?? ""}
|
||||
/>
|
||||
<div className="flex flex-col col-span-2 items-center justify-center">
|
||||
<img
|
||||
src={nationalData?.image_url || "https://i.pravatar.cc/300"}
|
||||
height={100}
|
||||
width={100}
|
||||
className="object-fit aspect-square rounded-full"
|
||||
alt="id photo"
|
||||
/>
|
||||
</div>
|
||||
{nationalData?.image_url && (
|
||||
<div className="flex flex-col col-span-2 items-center justify-center">
|
||||
<img
|
||||
src={nationalData.image_url}
|
||||
height={100}
|
||||
width={100}
|
||||
className="object-fit aspect-square rounded-full"
|
||||
alt="id photo"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useAtomValue } from "jotai";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { AdminDevicesTable } from "@/components/admin/admin-devices-table";
|
||||
import DynamicFilter from "@/components/generic-filter";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
|
||||
export default function UserDevices() {
|
||||
const user = useAtomValue(userAtom);
|
||||
if (!user?.is_admin) return <Navigate to="/devices" replace />;
|
||||
if (!isAdminUser(user)) return <Navigate to="/devices" replace />;
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useAtomValue } from "jotai";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { UsersPaymentsTable } from "@/components/admin/user-payments-table";
|
||||
import DynamicFilter from "@/components/generic-filter";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
|
||||
export default function UserPayments() {
|
||||
const user = useAtomValue(userAtom);
|
||||
if (!user?.is_admin) return <Navigate to="/payments" replace />;
|
||||
if (!isAdminUser(user)) return <Navigate to="/payments" replace />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useAtomValue } from "jotai";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { AdminTopupsTable } from "@/components/admin/admin-topup-table";
|
||||
import DynamicFilter from "@/components/generic-filter";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
|
||||
export default function UserTopups() {
|
||||
const user = useAtomValue(userAtom);
|
||||
if (!user?.is_admin) return <Navigate to="/top-ups" replace />;
|
||||
if (!isAdminUser(user)) return <Navigate to="/top-ups" replace />;
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Navigate, useParams } from "react-router-dom";
|
||||
import ClientErrorMessage from "@/components/client-error-message";
|
||||
import FullPageLoader from "@/components/full-page-loader";
|
||||
import UserUpdateForm from "@/components/user/user-update-form";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
import { getProfileById } from "@/queries/users";
|
||||
|
||||
export default function UserUpdate() {
|
||||
@@ -17,7 +17,7 @@ export default function UserUpdate() {
|
||||
enabled: !!userId,
|
||||
});
|
||||
|
||||
if (!authUser?.is_admin) return <Navigate to="/devices" replace />;
|
||||
if (!isAdminUser(authUser)) return <Navigate to="/devices" replace />;
|
||||
if (isLoading) return <FullPageLoader />;
|
||||
if (error) return <ClientErrorMessage message={error.message} />;
|
||||
if (!user) return null;
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useAtomValue } from "jotai";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import DynamicFilter from "@/components/generic-filter";
|
||||
import { UsersTable } from "@/components/user-table";
|
||||
import { userAtom } from "@/lib/auth-store";
|
||||
import { isAdminUser, userAtom } from "@/lib/auth-store";
|
||||
|
||||
export default function Users() {
|
||||
const user = useAtomValue(userAtom);
|
||||
if (!user?.is_admin) return <Navigate to="/devices" replace />;
|
||||
if (!isAdminUser(user)) return <Navigate to="/devices" replace />;
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center border rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
|
||||
|
||||
Reference in New Issue
Block a user