TS issues #2

Merged
Brook-Tewabe-Yaltopia merged 1 commits from el-ui into prod 2026-06-11 10:50:01 +03:00
14 changed files with 50 additions and 32 deletions

View File

@ -1,5 +1,3 @@
import { Navigate, useLocation } from "react-router-dom";
interface ProtectedRouteProps {
children: React.ReactNode;
}

View File

@ -16,5 +16,7 @@ export function useAdminRole() {
roleLabel: roleLabel(role),
hasPanelAccess: hasPanelAccess(role),
...permissions,
canAccessSystemMembers: permissions.canManageSystem,
canSendBroadcast: permissions.canSendNotifications,
};
}

View File

@ -1,4 +1,4 @@
import React, { useState, type ComponentType, useEffect } from "react";
import { useState, type ComponentType, useEffect } from "react";
import { Outlet, Link, useLocation, useNavigate } from "react-router-dom";
import {
LayoutDashboard,

View File

@ -150,7 +150,7 @@ export default function AnalyticsStoragePage() {
fontWeight: "700",
textTransform: "uppercase",
}}
formatter={(value: number) => formatBytes(value)}
formatter={(value) => formatBytes(Number(value ?? 0))}
/>
</PieChart>
</ResponsiveContainer>

View File

@ -150,7 +150,7 @@ export default function AnalyticsStoragePage() {
fontWeight: "700",
textTransform: "uppercase",
}}
formatter={(value: number) => formatBytes(value)}
formatter={(value) => formatBytes(Number(value ?? 0))}
/>
</PieChart>
</ResponsiveContainer>

View File

@ -38,19 +38,19 @@ export default function AnalyticsUsersPage() {
{[
{
label: "Total Population",
value: lastMetrics?.total || 0,
value: lastMetrics?.users || 0,
icon: Users,
color: "text-blue-600 bg-blue-50 border-blue-100",
},
{
label: "Privileged Access",
value: lastMetrics?.admins || 0,
label: "Active Users",
value: lastMetrics?.activeUsers || 0,
icon: ShieldCheck,
color: "text-emerald-600 bg-emerald-50 border-emerald-100",
},
{
label: "Standard Users",
value: lastMetrics?.regular || 0,
label: "Inactive Users",
value: (lastMetrics?.users ?? 0) - (lastMetrics?.activeUsers ?? 0),
icon: UserCheck,
color: "text-purple-600 bg-purple-50 border-purple-100",
},
@ -126,21 +126,21 @@ export default function AnalyticsUsersPage() {
/>
<Line
type="monotone"
dataKey="total"
dataKey="users"
stroke="#111827"
strokeWidth={2}
dot={false}
activeDot={{ r: 4, fill: "#111827", strokeWidth: 0 }}
name="Aggregate Population"
name="Total Users"
/>
<Line
type="monotone"
dataKey="admins"
dataKey="activeUsers"
stroke="#10B981"
strokeWidth={1.5}
strokeDasharray="4 4"
dot={false}
name="Privileged"
name="Active Users"
/>
</LineChart>
</ResponsiveContainer>

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
Dialog,
@ -10,7 +10,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Megaphone, Plus, Edit, Trash2, Filter } from "lucide-react";
import { Plus, Edit, Trash2, Filter } from "lucide-react";
import {
announcementService,
type Announcement,

View File

@ -5,7 +5,6 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Search,
Eye,
ChevronLeft,
ChevronRight,
Filter,

View File

@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Shield, Ban, Mail, Globe, AlertTriangle } from "lucide-react";
import { Ban, Mail, Globe, AlertTriangle } from "lucide-react";
import { securityService } from "@/services";
import type { SuspiciousIP, SuspiciousEmail } from "@/types/security.types";

View File

@ -1,7 +1,6 @@
import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

View File

@ -1,7 +1,7 @@
import { useState } from "react";
import { NavLink } from "react-router-dom";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tabs, TabsContent } from "@/components/ui/tabs";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@ -31,7 +31,6 @@ import {
Globe,
ArrowRight,
Library,
Settings2,
ChevronRight,
} from "lucide-react";
import { faqService } from "@/services";

View File

@ -23,16 +23,22 @@ import {
import { Search, UserPlus } from "lucide-react"
import { systemMemberService } from "@/services"
import { useAdminRole } from "@/hooks/use-admin-role"
import { AdminRole } from "@/lib/admin-roles"
import { AdminRole, type AdminRoleValue } from "@/lib/admin-roles"
import { toast } from "sonner"
export default function SystemMembersPage() {
const { canAccessSystemMembers, canEdit } = useAdminRole()
const { canAccessSystemMembers, canManageSystem } = useAdminRole()
const queryClient = useQueryClient()
const [page, setPage] = useState(1)
const [search, setSearch] = useState("")
const [open, setOpen] = useState(false)
const [form, setForm] = useState({
const [form, setForm] = useState<{
email: string
firstName: string
lastName: string
password: string
role: AdminRoleValue
}>({
email: "",
firstName: "",
lastName: "",
@ -85,7 +91,7 @@ export default function SystemMembersPage() {
areas; cannot manage this list).
</p>
</div>
{canEdit && (
{canManageSystem && (
<Button
className="rounded-none gap-2"
onClick={() => setOpen(true)}
@ -243,7 +249,7 @@ export default function SystemMembersPage() {
<Select
value={form.role}
onValueChange={(role) =>
setForm((f) => ({ ...f, role }))
setForm((f) => ({ ...f, role: role as AdminRoleValue }))
}
>
<SelectTrigger className="rounded-none">

View File

@ -1,6 +1,6 @@
import { useState, useMemo } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
@ -12,21 +12,18 @@ import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Search,
CheckCheck,
Send,
Plus,
BellRing,
Mail,
MessageSquare,
History,
Target,
ArrowRight,
ChevronRight,
Loader2,
Calendar,
} from "lucide-react";
@ -36,7 +33,6 @@ import type {
SendSmsNotificationRequest,
SendEmailNotificationRequest,
} from "@/services/notification.service";
import { useAdminRole } from "@/hooks/use-admin-role";
import { toast } from "sonner";
import { format } from "date-fns";
import { cn } from "@/lib/utils";
@ -44,7 +40,6 @@ import { cn } from "@/lib/utils";
type Channel = "PUSH" | "SMS" | "EMAIL";
export default function NotificationsPage() {
const { canSendNotifications } = useAdminRole();
const queryClient = useQueryClient();
const [searchQuery, setSearchQuery] = useState("");
const [activeChannel, setActiveChannel] = useState<Channel>("PUSH");

View File

@ -46,6 +46,13 @@ export interface SendEmailNotificationRequest {
scheduledFor?: string;
}
export interface SendBroadcastRequest {
title: string;
message: string;
audience: "all_end_users" | "system_users_only" | "everyone_with_access";
channels: ("push" | "sms" | "email")[];
}
class NotificationService {
/**
* Get all notifications for current user (Paginated)
@ -87,6 +94,19 @@ class NotificationService {
await apiClient.post("/notifications/read-all");
}
/**
* Send multi-channel broadcast (ADMIN only)
*/
async sendBroadcast(
data: SendBroadcastRequest,
): Promise<{ success: boolean }> {
const response = await apiClient.post<{ success: boolean }>(
"/admin/notifications/broadcast",
data,
);
return response.data;
}
/**
* Send push notification (ADMIN only)
*/