temp commit for branch switching

This commit is contained in:
yuneng-jiang 2026-01-10 15:05:30 -08:00
parent 9b4f3308fc
commit 2b93fd775d
3 changed files with 200 additions and 8 deletions

View file

@ -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 };

View file

@ -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: {

View file

@ -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 &ldquo;Organization ID&rdquo; 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>