updated sse supporting react-openapi
This commit is contained in:
@@ -23,7 +23,7 @@ import {
|
||||
useReport,
|
||||
prepareReport,
|
||||
} from "./features/report";
|
||||
import { useResourceByName } from "../react-openapi";
|
||||
import { useReportSnapshotsList } from "./features/report-snapshots";
|
||||
|
||||
function formatSnapshotDate(iso: string) {
|
||||
const d = new Date(iso);
|
||||
@@ -56,13 +56,13 @@ export default function Dashboard() {
|
||||
|
||||
const [selectedSnapshotId, setSelectedSnapshotId] = React.useState<string | null>(null);
|
||||
|
||||
const { data: snapshotsData } = useResourceByName("reports").useList();
|
||||
const { data: snapshotsData } = useReportSnapshotsList();
|
||||
const snapshotOptions = React.useMemo(() => {
|
||||
const options: { label: string; value: string | null }[] = [
|
||||
{ label: "Latest (auto)", value: null },
|
||||
];
|
||||
if (snapshotsData?.data) {
|
||||
for (const snap of snapshotsData.data) {
|
||||
if (snapshotsData?.items) {
|
||||
for (const snap of snapshotsData.items) {
|
||||
options.push({
|
||||
label: `Snapshot from ${formatSnapshotDate(snap.created_at)}`,
|
||||
value: snap.snapshot_id,
|
||||
|
||||
@@ -35,7 +35,8 @@ import type {
|
||||
ProgressMessage,
|
||||
} from "./features/fetch-requests";
|
||||
import { RETRY_MAX, formatApiError } from "./features/fetch-requests";
|
||||
import { useResourceByName, useConfig, defaultFieldComponents } from "../react-openapi";
|
||||
import { useAppContext, useResource } from "../react-openapi";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
|
||||
const statusColors: Record<FetchRequestStatus, "default" | "primary" | "warning" | "info" | "success" | "error"> = {
|
||||
pending: "default",
|
||||
@@ -144,11 +145,18 @@ function isMathValid(candidate: { amount: number; balance: number }, prevBalance
|
||||
export default function FetchRequestDetail() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const config = useConfig();
|
||||
const { resources, config } = useAppContext();
|
||||
const fetchRequestResource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { get, update } = useResource(fetchRequestResource);
|
||||
|
||||
const { useRead, usePatch } = useResourceByName("fetch-requests", { fieldComponents: defaultFieldComponents });
|
||||
const { data: fetchRequest, isLoading, error: fetchError, refetch: refetchRequest } = useRead(id!);
|
||||
const updateMutation = usePatch();
|
||||
const { data: fetchRequest, isLoading, error: fetchError, refetch: refetchRequest } = useQuery({
|
||||
queryKey: ["fetch-requests", "detail", id],
|
||||
queryFn: () => get(id!),
|
||||
enabled: !!id,
|
||||
});
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: ({ id: rid, data }: { id: string; data: any }) => update(rid, data),
|
||||
});
|
||||
const resolveMutation = useResolveAmbiguity();
|
||||
const { data: ambiguities, refetch: refetchAmbiguities } = useFetchRequestAmbiguities(id!);
|
||||
|
||||
@@ -193,8 +201,8 @@ export default function FetchRequestDetail() {
|
||||
}, [fetchRequest, stepStats, liveParsedCount, txnBlockCount]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!id || !config?.baseUrl) return;
|
||||
const url = `${config.baseUrl}/fetch-requests/${id}/events`;
|
||||
if (!id || !config?.baseApiUrl) return;
|
||||
const url = `${config.baseApiUrl}/fetch-requests/${id}/events`;
|
||||
const es = new EventSource(url);
|
||||
sseRef.current = es;
|
||||
|
||||
@@ -243,7 +251,7 @@ export default function FetchRequestDetail() {
|
||||
es.close();
|
||||
sseRef.current = null;
|
||||
};
|
||||
}, [id, config?.baseUrl]);
|
||||
}, [id, config?.baseApiUrl]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (feedRef.current) {
|
||||
|
||||
@@ -47,8 +47,9 @@ import type {
|
||||
} from "./features/fetch-requests";
|
||||
import { RETRY_MAX, formatApiError } from "./features/fetch-requests";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useResourceByName, useConfig, defaultFieldComponents } from "../react-openapi";
|
||||
import type { ResourceField } from "../react-openapi";
|
||||
import { useAppContext, useResource, FormFieldRenderer } from "../react-openapi";
|
||||
import type { FieldConfig } from "../react-openapi";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
|
||||
const statusColors: Record<FetchRequestStatus, "default" | "primary" | "warning" | "info" | "success" | "error"> = {
|
||||
pending: "default",
|
||||
@@ -70,6 +71,16 @@ const statusIcons: Record<FetchRequestStatus, React.ReactNode> = {
|
||||
failed: <ErrorIcon sx={{ fontSize: 16, color: "error.main" }} />,
|
||||
};
|
||||
|
||||
const STATUS_OPTIONS: FetchRequestStatus[] = [
|
||||
"pending",
|
||||
"processing",
|
||||
"paused",
|
||||
"raw_expenses_done",
|
||||
"enriched_done",
|
||||
"completed",
|
||||
"failed",
|
||||
];
|
||||
|
||||
function formatDate(iso: string) {
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleString();
|
||||
@@ -107,33 +118,48 @@ export default function FetchRequests() {
|
||||
const [accountFilter, setAccountFilter] = React.useState("");
|
||||
const [sourceFilter, setSourceFilter] = React.useState<"all" | "file" | "email">("all");
|
||||
|
||||
const { useList, useCreate, usePatch, useDelete, components } = useResourceByName("fetch-requests", { fieldComponents: defaultFieldComponents });
|
||||
const { data: listData, isLoading, isFetching, refetch } = useList({
|
||||
...(statusFilter.length > 0 ? { status: statusFilter.join(",") } : {}),
|
||||
...(accountFilter ? { account_name: accountFilter } : {}),
|
||||
...(sourceFilter !== "all" ? { source_type: sourceFilter } : {}),
|
||||
const { resources } = useAppContext();
|
||||
const fetchRequestsRes = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { list, create, update, remove } = useResource(fetchRequestsRes);
|
||||
|
||||
const { data: listData, isLoading, isFetching, refetch } = useQuery({
|
||||
queryKey: ["fetch-requests", "list", { statusFilter, accountFilter, sourceFilter }],
|
||||
queryFn: () => list({
|
||||
...(statusFilter.length > 0 ? { status: statusFilter.join(",") } : {}),
|
||||
...(accountFilter ? { account_name: accountFilter } : {}),
|
||||
...(sourceFilter !== "all" ? { source_type: sourceFilter } : {}),
|
||||
}),
|
||||
});
|
||||
|
||||
const { useList: useAccountsList } = useResourceByName("accounts");
|
||||
const { data: accountsData } = useAccountsList();
|
||||
const accountsResource = resources.find(r => r.name === "accounts");
|
||||
const { list: listAccounts } = accountsResource ? useResource(accountsResource) : { list: async () => ({ items: [] }) };
|
||||
const { data: accountsData } = useQuery({
|
||||
queryKey: ["accounts", "list"],
|
||||
queryFn: () => listAccounts(),
|
||||
enabled: !!accountsResource,
|
||||
});
|
||||
const accountOptions: string[] = React.useMemo(() => {
|
||||
return (accountsData?.data ?? []).map((a: any) => a.name).filter(Boolean);
|
||||
return (accountsData?.items ?? []).map((a: any) => a.name).filter(Boolean);
|
||||
}, [accountsData]);
|
||||
|
||||
const config = useConfig();
|
||||
const fetchRes = config?.resources.find((r: any) => r.name === "fetch-requests");
|
||||
const formatField: ResourceField | undefined = fetchRes?.fields?.source?.schema?.format;
|
||||
const formatOptions: string[] = formatField?.options ?? [];
|
||||
const startDateField: ResourceField | undefined = fetchRes?.fields?.start_date;
|
||||
const endDateField: ResourceField | undefined = fetchRes?.fields?.end_date;
|
||||
const payorUsernameField: ResourceField | undefined = fetchRes?.fields?.payor_username;
|
||||
const formatField: FieldConfig | undefined = fetchRequestsRes?.orderedFields.find(f => f.name === "format");
|
||||
const formatOptions: string[] = formatField?.enumValues ?? [];
|
||||
const startDateField: FieldConfig | undefined = fetchRequestsRes?.orderedFields.find(f => f.name === "start_date");
|
||||
const endDateField: FieldConfig | undefined = fetchRequestsRes?.orderedFields.find(f => f.name === "end_date");
|
||||
const payorUsernameField: FieldConfig | undefined = fetchRequestsRes?.orderedFields.find(f => f.name === "payor_username");
|
||||
|
||||
const createMutation = useCreate();
|
||||
const updateMutation = usePatch();
|
||||
const deleteMutation = useDelete();
|
||||
const createMutation = useMutation({
|
||||
mutationFn: (data: any) => create(data),
|
||||
});
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: any }) => update(id, data),
|
||||
});
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (id: string) => remove(id),
|
||||
});
|
||||
const uploadMutation = useUploadFile();
|
||||
|
||||
const requests = listData?.data ?? [];
|
||||
const requests = listData?.items ?? [];
|
||||
|
||||
const handleUpload = async () => {
|
||||
if (!file) return;
|
||||
@@ -262,9 +288,8 @@ export default function FetchRequests() {
|
||||
Uploaded as: {uploadedPath}
|
||||
</Alert>
|
||||
)}
|
||||
{formatField && components?.FormField ? (
|
||||
<components.FormField
|
||||
name="format"
|
||||
{formatField ? (
|
||||
<FormFieldRenderer
|
||||
field={formatField}
|
||||
value={format}
|
||||
onChange={setFormat}
|
||||
@@ -282,9 +307,8 @@ export default function FetchRequests() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{formatField && components?.FormField ? (
|
||||
<components.FormField
|
||||
name="format"
|
||||
{formatField ? (
|
||||
<FormFieldRenderer
|
||||
field={formatField}
|
||||
value={format}
|
||||
onChange={setFormat}
|
||||
@@ -314,9 +338,8 @@ export default function FetchRequests() {
|
||||
)}
|
||||
sx={{ "& .MuiOutlinedInput-root": { height: "auto", minHeight: "2.5rem" } }}
|
||||
/>
|
||||
{payorUsernameField && components?.FormField ? (
|
||||
<components.FormField
|
||||
name="payor_username"
|
||||
{payorUsernameField ? (
|
||||
<FormFieldRenderer
|
||||
field={payorUsernameField}
|
||||
value={payorUsername}
|
||||
onChange={setPayorUsername}
|
||||
@@ -326,10 +349,9 @@ export default function FetchRequests() {
|
||||
)}
|
||||
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
{startDateField && components?.date ? (
|
||||
{startDateField ? (
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<components.date
|
||||
name="start_date"
|
||||
<FormFieldRenderer
|
||||
field={startDateField}
|
||||
value={startDate}
|
||||
onChange={setStartDate}
|
||||
@@ -347,10 +369,9 @@ export default function FetchRequests() {
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
)}
|
||||
{endDateField && components?.date ? (
|
||||
{endDateField ? (
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<components.date
|
||||
name="end_date"
|
||||
<FormFieldRenderer
|
||||
field={endDateField}
|
||||
value={endDate}
|
||||
onChange={setEndDate}
|
||||
@@ -391,7 +412,7 @@ export default function FetchRequests() {
|
||||
input={<OutlinedInput label="Status" />}
|
||||
renderValue={(selected) => (selected as string[]).join(", ")}
|
||||
>
|
||||
{(config?.enums?.FetchRequestStatus ?? []).map((s: string) => (
|
||||
{STATUS_OPTIONS.map((s: string) => (
|
||||
<MenuItem key={s} value={s}>{s.replace(/_/g, " ")}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -21,8 +21,9 @@ import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import AddCircleIcon from "@mui/icons-material/AddCircle";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import { useResourceByName, useConfig, defaultFieldComponents } from "../react-openapi";
|
||||
import type { ResourceField } from "../react-openapi";
|
||||
import { useAppContext, useResource, FormFieldRenderer } from "../react-openapi";
|
||||
import type { FieldConfig } from "../react-openapi";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
interface ReportSnapshotQuery {
|
||||
accounts?: string[];
|
||||
@@ -53,21 +54,32 @@ export default function ReportSnapshots() {
|
||||
const [deleteTarget, setDeleteTarget] = React.useState<ReportSnapshot | null>(null);
|
||||
const [createdSnapshotId, setCreatedSnapshotId] = React.useState<string | null>(null);
|
||||
|
||||
const { useList, useCreate, useDelete, components } = useResourceByName("reports", { fieldComponents: defaultFieldComponents });
|
||||
const { resources } = useAppContext();
|
||||
const reportsResource = resources.find(r => r.name === "reports")!;
|
||||
const { list, create, remove } = useResource(reportsResource);
|
||||
|
||||
const { data: listData, isLoading, isFetching, refetch } = useList();
|
||||
const createMutation = useCreate();
|
||||
const deleteMutation = useDelete();
|
||||
const { data: listData, isLoading, isFetching, refetch } = useQuery({
|
||||
queryKey: ["reports", "list"],
|
||||
queryFn: () => list(),
|
||||
});
|
||||
const createMutation = useMutation({
|
||||
mutationFn: (data: any) => create(data),
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (id: string) => remove(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["reports", "list"] });
|
||||
},
|
||||
});
|
||||
|
||||
const config = useConfig();
|
||||
const reportsRes = config?.resources.find((r: any) => r.name === "reports");
|
||||
const ignoreSelfField: ResourceField | undefined = reportsRes?.fields?.ignore_self;
|
||||
const startDateField: ResourceField | undefined = reportsRes?.fields?.start_date;
|
||||
const endDateField: ResourceField | undefined = reportsRes?.fields?.end_date;
|
||||
const minAmountField: ResourceField | undefined = reportsRes?.fields?.min_amount;
|
||||
const maxAmountField: ResourceField | undefined = reportsRes?.fields?.max_amount;
|
||||
const ignoreSelfField: FieldConfig | undefined = reportsResource?.orderedFields.find(f => f.name === "ignore_self");
|
||||
const startDateField: FieldConfig | undefined = reportsResource?.orderedFields.find(f => f.name === "start_date");
|
||||
const endDateField: FieldConfig | undefined = reportsResource?.orderedFields.find(f => f.name === "end_date");
|
||||
const minAmountField: FieldConfig | undefined = reportsResource?.orderedFields.find(f => f.name === "min_amount");
|
||||
const maxAmountField: FieldConfig | undefined = reportsResource?.orderedFields.find(f => f.name === "max_amount");
|
||||
|
||||
const snapshots: ReportSnapshot[] = listData?.data ?? [];
|
||||
const snapshots: ReportSnapshot[] = listData?.items ?? [];
|
||||
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
@@ -123,14 +135,13 @@ export default function ReportSnapshots() {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||
{ignoreSelfField && components?.FormField && (
|
||||
<components.FormField
|
||||
name="ignore_self"
|
||||
{ignoreSelfField ? (
|
||||
<FormFieldRenderer
|
||||
field={ignoreSelfField}
|
||||
value={ignoreSelf}
|
||||
onChange={(val: boolean) => setIgnoreSelf(val)}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
@@ -158,26 +169,24 @@ export default function ReportSnapshots() {
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
{minAmountField && components?.FormField && (
|
||||
{minAmountField ? (
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<components.FormField
|
||||
name="min_amount"
|
||||
<FormFieldRenderer
|
||||
field={minAmountField}
|
||||
value={minAmount}
|
||||
onChange={(val: string) => setMinAmount(val)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{maxAmountField && components?.FormField && (
|
||||
) : null}
|
||||
{maxAmountField ? (
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<components.FormField
|
||||
name="max_amount"
|
||||
<FormFieldRenderer
|
||||
field={maxAmountField}
|
||||
value={maxAmount}
|
||||
onChange={(val: string) => setMaxAmount(val)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
) : null}
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useResourceByName } from "../../../react-openapi";
|
||||
import { api } from "../../../react-openapi/api/client";
|
||||
import { useAppContext, useResource, getApi } from "../../../react-openapi";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { ResolveAmbiguityPayload } from "./fetch-requests.models";
|
||||
|
||||
@@ -8,28 +7,51 @@ export function useFetchRequestsList(params?: {
|
||||
account_name?: string;
|
||||
source_type?: string;
|
||||
}) {
|
||||
const { useList } = useResourceByName("fetch-requests");
|
||||
return useList(params);
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { list } = useResource(resource);
|
||||
return useQuery({
|
||||
queryKey: ["fetch-requests", "list", params],
|
||||
queryFn: () => list(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFetchRequest(id: string) {
|
||||
const { useRead } = useResourceByName("fetch-requests");
|
||||
return useRead(id);
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { get } = useResource(resource);
|
||||
return useQuery({
|
||||
queryKey: ["fetch-requests", "detail", id],
|
||||
queryFn: () => get(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateFetchRequest() {
|
||||
const { useCreate } = useResourceByName("fetch-requests");
|
||||
return useCreate();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { create } = useResource(resource);
|
||||
return useMutation({
|
||||
mutationFn: (data: any) => create(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFetchRequest() {
|
||||
const { usePatch } = useResourceByName("fetch-requests");
|
||||
return usePatch();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { update } = useResource(resource);
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: any }) => update(id, data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFetchRequest() {
|
||||
const { useDelete } = useResourceByName("fetch-requests");
|
||||
return useDelete();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "fetch-requests")!;
|
||||
const { remove } = useResource(resource);
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => remove(id),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUploadFile() {
|
||||
@@ -37,6 +59,7 @@ export function useUploadFile() {
|
||||
mutationFn: async (file: File) => {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const binary = new Uint8Array(arrayBuffer);
|
||||
const api = getApi();
|
||||
const res = await api.post("/uploads", binary, {
|
||||
headers: {
|
||||
"Content-Type": file.type,
|
||||
@@ -52,6 +75,7 @@ export function useFetchRequestAmbiguities(fetchRequestId: string) {
|
||||
return useQuery({
|
||||
queryKey: ["fetch-requests", fetchRequestId, "ambiguities"],
|
||||
queryFn: async () => {
|
||||
const api = getApi();
|
||||
const res = await api.get(
|
||||
`/fetch-requests/${fetchRequestId}/ambiguities`
|
||||
);
|
||||
@@ -72,6 +96,7 @@ export function useResolveAmbiguity() {
|
||||
ambiguityId: string;
|
||||
payload: ResolveAmbiguityPayload;
|
||||
}) => {
|
||||
const api = getApi();
|
||||
const res = await api.post(
|
||||
`/ambiguities/${ambiguityId}/resolve`,
|
||||
payload
|
||||
|
||||
@@ -2,3 +2,8 @@ export type {
|
||||
ReportSnapshot,
|
||||
ReportQuery,
|
||||
} from "./report-snapshots.models";
|
||||
export {
|
||||
useReportSnapshotsList,
|
||||
useCreateSnapshot,
|
||||
useDeleteSnapshot,
|
||||
} from "./useReportSnapshots";
|
||||
@@ -1,16 +1,34 @@
|
||||
import { useResourceByName } from "../../../react-openapi";
|
||||
import { useAppContext, useResource } from "../../../react-openapi";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
export function useReportSnapshotsList() {
|
||||
const { useList } = useResourceByName("reports");
|
||||
return useList();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "reports")!;
|
||||
const { list } = useResource(resource);
|
||||
return useQuery({
|
||||
queryKey: ["reports", "list"],
|
||||
queryFn: () => list(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSnapshot() {
|
||||
const { useCreate } = useResourceByName("reports");
|
||||
return useCreate();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "reports")!;
|
||||
const { create } = useResource(resource);
|
||||
return useMutation({
|
||||
mutationFn: (data: any) => create(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteSnapshot() {
|
||||
const { useDelete } = useResourceByName("reports");
|
||||
return useDelete();
|
||||
const queryClient = useQueryClient();
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "reports")!;
|
||||
const { remove } = useResource(resource);
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => remove(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["reports", "list"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useResourceByName } from "../../../react-openapi";
|
||||
import { useAppContext, useResource } from "../../../react-openapi";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export interface ReportParams {
|
||||
snapshot_id?: string;
|
||||
@@ -9,13 +10,13 @@ export interface ReportParams {
|
||||
}
|
||||
|
||||
export function useReport(params: ReportParams) {
|
||||
const { useRead } = useResourceByName("reports");
|
||||
const { resources } = useAppContext();
|
||||
const resource = resources.find(r => r.name === "reports")!;
|
||||
const { get } = useResource(resource);
|
||||
|
||||
return useRead(
|
||||
params.snapshot_id ? params.snapshot_id : "latest",
|
||||
{
|
||||
...params,
|
||||
periods: params.periods,
|
||||
}
|
||||
);
|
||||
return useQuery({
|
||||
queryKey: ["reports", "read", params],
|
||||
queryFn: () =>
|
||||
get(params.snapshot_id ? params.snapshot_id : "latest"),
|
||||
});
|
||||
}
|
||||
|
||||
15
src/main.jsx
15
src/main.jsx
@@ -15,8 +15,7 @@ import Dashboard from './Dashboard';
|
||||
import FetchRequests from './FetchRequests';
|
||||
import FetchRequestDetail from './FetchRequestDetail';
|
||||
import ReportSnapshots from './ReportSnapshots';
|
||||
import { Admin, AppProvider, defaultFieldComponents } from '../react-openapi';
|
||||
import { configuration, profileConfiguration } from './openapi-config';
|
||||
import { AppProvider, Admin } from '../react-openapi';
|
||||
import { Buffer } from 'buffer';
|
||||
import process from 'process';
|
||||
import { AuthProvider } from "../react-auth";
|
||||
@@ -31,6 +30,14 @@ const rootElement = document.getElementById('root');
|
||||
const root = createRoot(rootElement);
|
||||
|
||||
const AUTH_BASE = import.meta.env.VITE_AUTH_BASE_URL;
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL;
|
||||
|
||||
const specConfig = {
|
||||
specUrl: `${API_BASE}/openapi.yaml`,
|
||||
baseApiUrl: API_BASE,
|
||||
title: 'Khata Admin',
|
||||
getToken: () => localStorage.getItem('token'),
|
||||
};
|
||||
|
||||
const routerMapping = [
|
||||
{ path: "/", component: Home, headerTitle: "Home" },
|
||||
@@ -43,7 +50,7 @@ const routerMapping = [
|
||||
];
|
||||
|
||||
root.render(
|
||||
<AppProvider resourceOverrides={configuration} profileConfig={profileConfiguration}>
|
||||
<AppProvider specConfiguration={specConfig}>
|
||||
<BrowserRouter>
|
||||
<AuthProvider authBaseUrl={AUTH_BASE}>
|
||||
<AppTheme>
|
||||
@@ -60,7 +67,7 @@ root.render(
|
||||
path={path}
|
||||
element={
|
||||
path.startsWith("/admin") ? (
|
||||
<Component basePath="/admin" fieldComponents={{ ...defaultFieldComponents }} />
|
||||
<Component basePath="/admin" />
|
||||
) : (
|
||||
<Component />
|
||||
)
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { ResourceOverride } from "../react-openapi";
|
||||
|
||||
export const configuration: Record<string, ResourceOverride> = {
|
||||
expenses: {
|
||||
filterOptions: {
|
||||
mode: "client",
|
||||
fields: ["account", "payee", "tags", "occurred_at", "amount"],
|
||||
},
|
||||
fields: {
|
||||
payee: {
|
||||
displayFormat: "{name}",
|
||||
filterType: "autocomplete",
|
||||
},
|
||||
payor: {
|
||||
display: false,
|
||||
displayFormat: "{username}",
|
||||
},
|
||||
account: {
|
||||
displayFormat: "{name}",
|
||||
filterType: "multiselect",
|
||||
refers: "accounts"
|
||||
},
|
||||
tags: {
|
||||
displayFormat: "{icon} {name}",
|
||||
filterType: "autocomplete",
|
||||
refers: "tags"
|
||||
},
|
||||
occurred_at: {
|
||||
filterType: "date-range",
|
||||
formatter: (val: string) => {
|
||||
const date = new Date(val);
|
||||
const day = date.getDate();
|
||||
const month = date.toLocaleString('default', { month: 'long' });
|
||||
const year = date.getFullYear();
|
||||
const suffix = (day: number) => {
|
||||
if (day > 3 && day < 21) return 'th';
|
||||
switch (day % 10) {
|
||||
case 1: return "st";
|
||||
case 2: return "nd";
|
||||
case 3: return "rd";
|
||||
default: return "th";
|
||||
}
|
||||
};
|
||||
return `${day}${suffix(day)} ${month} ${year}`;
|
||||
}
|
||||
},
|
||||
amount: {
|
||||
filterType: "number-range",
|
||||
},
|
||||
created_at: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
},
|
||||
'fetch-requests': {
|
||||
fields: {
|
||||
format: {
|
||||
path: 'source.format',
|
||||
},
|
||||
start_date: {
|
||||
type: 'date',
|
||||
label: 'Start Date',
|
||||
},
|
||||
end_date: {
|
||||
type: 'date',
|
||||
label: 'End Date',
|
||||
},
|
||||
// account: {
|
||||
// refers: 'accounts',
|
||||
// },
|
||||
// tags: {
|
||||
// refers: 'tags',
|
||||
// },
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
referenceOptions: {
|
||||
enumOption: {
|
||||
key: 'id',
|
||||
value: '{name} - XX{number}',
|
||||
},
|
||||
autoComplete: true,
|
||||
prefetch: true,
|
||||
}
|
||||
},
|
||||
tags: {
|
||||
referenceOptions: {
|
||||
enumOption: {
|
||||
key: 'id',
|
||||
value: '{icon} {name}',
|
||||
},
|
||||
autoComplete: true,
|
||||
prefetch: true,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const profileConfiguration = {
|
||||
"extraFields": ['name'],
|
||||
"resource": "payors",
|
||||
// not in use
|
||||
"hidden": true,
|
||||
};
|
||||
Reference in New Issue
Block a user