mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-01 08:34:53 +02:00
add refresh sites button
This commit is contained in:
parent
073c318f12
commit
809a135721
5 changed files with 61 additions and 11 deletions
|
@ -776,6 +776,8 @@
|
||||||
"orgPoliciesAdd": "Add Organization Policy",
|
"orgPoliciesAdd": "Add Organization Policy",
|
||||||
"orgRequired": "Organization is required",
|
"orgRequired": "Organization is required",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"refreshError": "Failed to refresh data",
|
||||||
|
"refresh": "Refresh",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"orgPolicyAddedDescription": "Policy added successfully",
|
"orgPolicyAddedDescription": "Policy added successfully",
|
||||||
"orgPolicyUpdatedDescription": "Policy updated successfully",
|
"orgPolicyUpdatedDescription": "Policy updated successfully",
|
||||||
|
|
|
@ -8,12 +8,16 @@ interface DataTableProps<TData, TValue> {
|
||||||
columns: ColumnDef<TData, TValue>[];
|
columns: ColumnDef<TData, TValue>[];
|
||||||
data: TData[];
|
data: TData[];
|
||||||
createSite?: () => void;
|
createSite?: () => void;
|
||||||
|
onRefresh?: () => void;
|
||||||
|
isRefreshing?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SitesDataTable<TData, TValue>({
|
export function SitesDataTable<TData, TValue>({
|
||||||
columns,
|
columns,
|
||||||
data,
|
data,
|
||||||
createSite
|
createSite,
|
||||||
|
onRefresh,
|
||||||
|
isRefreshing
|
||||||
}: DataTableProps<TData, TValue>) {
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
@ -27,6 +31,8 @@ export function SitesDataTable<TData, TValue>({
|
||||||
searchColumn="name"
|
searchColumn="name"
|
||||||
onAdd={createSite}
|
onAdd={createSite}
|
||||||
addButtonText={t('siteAdd')}
|
addButtonText={t('siteAdd')}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
isRefreshing={isRefreshing}
|
||||||
defaultSort={{
|
defaultSort={{
|
||||||
id: "name",
|
id: "name",
|
||||||
desc: false
|
desc: false
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import CreateSiteForm from "./CreateSiteForm";
|
import CreateSiteForm from "./CreateSiteForm";
|
||||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
|
@ -53,10 +53,31 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
|
const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
|
||||||
const [rows, setRows] = useState<SiteRow[]>(sites);
|
const [rows, setRows] = useState<SiteRow[]>(sites);
|
||||||
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
|
|
||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
|
// Update local state when props change (e.g., after refresh)
|
||||||
|
useEffect(() => {
|
||||||
|
setRows(sites);
|
||||||
|
}, [sites]);
|
||||||
|
|
||||||
|
const refreshData = async () => {
|
||||||
|
setIsRefreshing(true);
|
||||||
|
try {
|
||||||
|
router.refresh();
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: t("error"),
|
||||||
|
description: t("refreshError"),
|
||||||
|
variant: "destructive"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsRefreshing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const deleteSite = (siteId: number) => {
|
const deleteSite = (siteId: number) => {
|
||||||
api.delete(`/site/${siteId}`)
|
api.delete(`/site/${siteId}`)
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
@ -339,6 +360,8 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
createSite={() =>
|
createSite={() =>
|
||||||
router.push(`/${orgId}/settings/sites/create`)
|
router.push(`/${orgId}/settings/sites/create`)
|
||||||
}
|
}
|
||||||
|
onRefresh={refreshData}
|
||||||
|
isRefreshing={isRefreshing}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -71,7 +71,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
disabled={loading || props.disabled} // Disable button when loading
|
disabled={loading || props.disabled} // Disable button when loading
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
{/* {loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} */}
|
||||||
{props.children}
|
{props.children}
|
||||||
</Comp>
|
</Comp>
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,13 +23,14 @@ import { Button } from "@app/components/ui/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import { DataTablePagination } from "@app/components/DataTablePagination";
|
import { DataTablePagination } from "@app/components/DataTablePagination";
|
||||||
import { Plus, Search } from "lucide-react";
|
import { Plus, Search, RefreshCw } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle
|
CardTitle
|
||||||
} from "@app/components/ui/card";
|
} from "@app/components/ui/card";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
type DataTableProps<TData, TValue> = {
|
type DataTableProps<TData, TValue> = {
|
||||||
columns: ColumnDef<TData, TValue>[];
|
columns: ColumnDef<TData, TValue>[];
|
||||||
|
@ -37,6 +38,8 @@ type DataTableProps<TData, TValue> = {
|
||||||
title?: string;
|
title?: string;
|
||||||
addButtonText?: string;
|
addButtonText?: string;
|
||||||
onAdd?: () => void;
|
onAdd?: () => void;
|
||||||
|
onRefresh?: () => void;
|
||||||
|
isRefreshing?: boolean;
|
||||||
searchPlaceholder?: string;
|
searchPlaceholder?: string;
|
||||||
searchColumn?: string;
|
searchColumn?: string;
|
||||||
defaultSort?: {
|
defaultSort?: {
|
||||||
|
@ -51,6 +54,8 @@ export function DataTable<TData, TValue>({
|
||||||
title,
|
title,
|
||||||
addButtonText,
|
addButtonText,
|
||||||
onAdd,
|
onAdd,
|
||||||
|
onRefresh,
|
||||||
|
isRefreshing,
|
||||||
searchPlaceholder = "Search...",
|
searchPlaceholder = "Search...",
|
||||||
searchColumn = "name",
|
searchColumn = "name",
|
||||||
defaultSort
|
defaultSort
|
||||||
|
@ -60,6 +65,7 @@ export function DataTable<TData, TValue>({
|
||||||
);
|
);
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
const [globalFilter, setGlobalFilter] = useState<any>([]);
|
const [globalFilter, setGlobalFilter] = useState<any>([]);
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
|
@ -99,12 +105,25 @@ export function DataTable<TData, TValue>({
|
||||||
/>
|
/>
|
||||||
<Search className="h-4 w-4 absolute left-2 top-1/2 transform -translate-y-1/2 text-muted-foreground" />
|
<Search className="h-4 w-4 absolute left-2 top-1/2 transform -translate-y-1/2 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
{onAdd && addButtonText && (
|
<div className="flex items-center gap-2">
|
||||||
<Button onClick={onAdd}>
|
{onRefresh && (
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Button
|
||||||
{addButtonText}
|
variant="outline"
|
||||||
</Button>
|
onClick={onRefresh}
|
||||||
)}
|
disabled={isRefreshing}
|
||||||
|
loading={isRefreshing}
|
||||||
|
>
|
||||||
|
<RefreshCw className={`mr-2 h-4 w-4 ${isRefreshing ? 'animate-spin' : ''}`} />
|
||||||
|
{t("refresh")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{onAdd && addButtonText && (
|
||||||
|
<Button onClick={onAdd}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
{addButtonText}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Table>
|
<Table>
|
||||||
|
@ -163,4 +182,4 @@ export function DataTable<TData, TValue>({
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue