mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-08 22:21:35 +00:00
temp commit for branch switching
This commit is contained in:
parent
9b4f3308fc
commit
2b93fd775d
3 changed files with 200 additions and 8 deletions
|
|
@ -0,0 +1,120 @@
|
|||
import React from "react";
|
||||
|
||||
interface OrganizationFiltersProps {
|
||||
filters: FilterState;
|
||||
showFilters: boolean;
|
||||
onToggleFilters: (toggle: boolean) => void;
|
||||
onChange: <K extends keyof FilterState>(key: K, value: FilterState[K]) => void;
|
||||
onReset: () => void;
|
||||
}
|
||||
|
||||
type FilterState = {
|
||||
org_id: string;
|
||||
org_alias: string;
|
||||
sort_by: string;
|
||||
sort_order: "asc" | "desc";
|
||||
};
|
||||
|
||||
const OrganizationFilters = ({
|
||||
filters,
|
||||
showFilters,
|
||||
onToggleFilters,
|
||||
onChange,
|
||||
onReset,
|
||||
}: OrganizationFiltersProps) => {
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
{/* Search and Filter Controls */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{/* Organization Alias Search */}
|
||||
<div className="relative w-64">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search by Organization Name..."
|
||||
className="w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value={filters.org_alias}
|
||||
onChange={(e) => onChange("org_alias", e.target.value)}
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Filter Button */}
|
||||
<button
|
||||
className={`px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2 ${showFilters ? "bg-gray-100" : ""}`}
|
||||
onClick={() => onToggleFilters(!showFilters)}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
|
||||
/>
|
||||
</svg>
|
||||
Filters
|
||||
{(filters.org_id || filters.org_alias) && <span className="w-2 h-2 rounded-full bg-blue-500"></span>}
|
||||
</button>
|
||||
|
||||
{/* Reset Filters Button */}
|
||||
<button
|
||||
className="px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2"
|
||||
onClick={onReset}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
</svg>
|
||||
Reset Filters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Additional Filters */}
|
||||
{showFilters && (
|
||||
<div className="flex flex-wrap items-center gap-3 mt-3">
|
||||
{/* Organization ID Search */}
|
||||
<div className="relative w-64">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter Organization ID"
|
||||
className="w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value={filters.org_id}
|
||||
onChange={(e) => onChange("org_id", e.target.value)}
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationFilters;
|
||||
export type { FilterState };
|
||||
|
|
@ -1411,12 +1411,31 @@ export const availableTeamListCall = async (accessToken: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const organizationListCall = async (accessToken: string) => {
|
||||
export const organizationListCall = async (
|
||||
accessToken: string,
|
||||
org_id: string | null = null,
|
||||
org_alias: string | null = null,
|
||||
) => {
|
||||
/**
|
||||
* Get all organizations on proxy
|
||||
*/
|
||||
try {
|
||||
let url = proxyBaseUrl ? `${proxyBaseUrl}/organization/list` : `/organization/list`;
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (org_id) {
|
||||
queryParams.append("org_id", org_id.toString());
|
||||
}
|
||||
|
||||
if (org_alias) {
|
||||
queryParams.append("org_alias", org_alias.toString());
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
if (queryString) {
|
||||
url += `?${queryString}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import { formatNumberWithCommas } from "../utils/dataUtils";
|
|||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
|
||||
import OrganizationFilters, { FilterState } from "@/app/(dashboard)/organizations/OrganizationFilters";
|
||||
|
||||
interface OrganizationsTableProps {
|
||||
organizations: Organization[];
|
||||
|
|
@ -51,8 +52,10 @@ interface OrganizationsTableProps {
|
|||
export const fetchOrganizations = async (
|
||||
accessToken: string,
|
||||
setOrganizations: (organizations: Organization[]) => void,
|
||||
org_id: string | null = null,
|
||||
org_alias: string | null = null,
|
||||
) => {
|
||||
const organizations = await organizationListCall(accessToken);
|
||||
const organizations = await organizationListCall(accessToken, org_id, org_alias);
|
||||
setOrganizations(organizations);
|
||||
};
|
||||
|
||||
|
|
@ -76,12 +79,51 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
|||
const [isOrgModalVisible, setIsOrgModalVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [expandedAccordions, setExpandedAccordions] = useState<Record<string, boolean>>({});
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
const [filters, setFilters] = useState<FilterState>({
|
||||
org_id: "",
|
||||
org_alias: "",
|
||||
sort_by: "created_at",
|
||||
sort_order: "desc",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleFilterChange = (key: keyof FilterState, value: string) => {
|
||||
const newFilters = { ...filters, [key]: value };
|
||||
setFilters(newFilters);
|
||||
// Call organizationListCall with the new filters
|
||||
if (accessToken) {
|
||||
fetchOrganizations(accessToken, setOrganizations);
|
||||
organizationListCall(accessToken, newFilters.org_id || null, newFilters.org_alias || null)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setOrganizations(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error fetching organizations:", error);
|
||||
});
|
||||
}
|
||||
}, [accessToken]);
|
||||
};
|
||||
|
||||
const handleFilterReset = () => {
|
||||
setFilters({
|
||||
org_id: "",
|
||||
org_alias: "",
|
||||
sort_by: "created_at",
|
||||
sort_order: "desc",
|
||||
});
|
||||
// Reset organizations list
|
||||
if (accessToken) {
|
||||
organizationListCall(accessToken, null, null)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
setOrganizations(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error fetching organizations:", error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (orgId: string | null) => {
|
||||
if (!orgId) return;
|
||||
|
|
@ -101,7 +143,7 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
|||
setIsDeleteModalOpen(false);
|
||||
setOrgToDelete(null);
|
||||
// Refresh organizations list
|
||||
await fetchOrganizations(accessToken, setOrganizations);
|
||||
await fetchOrganizations(accessToken, setOrganizations, filters.org_id || null, filters.org_alias || null);
|
||||
} catch (error) {
|
||||
console.error("Error deleting organization:", error);
|
||||
} finally {
|
||||
|
|
@ -148,7 +190,7 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
|||
setIsOrgModalVisible(false);
|
||||
form.resetFields();
|
||||
// Refresh organizations list
|
||||
fetchOrganizations(accessToken, setOrganizations);
|
||||
fetchOrganizations(accessToken, setOrganizations, filters.org_id || null, filters.org_alias || null);
|
||||
} catch (error) {
|
||||
console.error("Error creating organization:", error);
|
||||
}
|
||||
|
|
@ -217,7 +259,18 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
|||
<Text>Click on “Organization ID” to view organization details.</Text>
|
||||
<Grid numItems={1} className="gap-2 pt-2 pb-2 h-[75vh] w-full mt-2">
|
||||
<Col numColSpan={1}>
|
||||
<Card className="w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]">
|
||||
<Card className="w-full mx-auto flex-auto overflow-hidden overflow-y-auto max-h-[50vh]">
|
||||
<div className="border-b px-6 py-4">
|
||||
<div className="flex flex-col space-y-4">
|
||||
<OrganizationFilters
|
||||
filters={filters}
|
||||
showFilters={showFilters}
|
||||
onToggleFilters={setShowFilters}
|
||||
onChange={handleFilterChange}
|
||||
onReset={handleFilterReset}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue