- Remove the preset document suite (letters, emails, invoices, quotes, PDF generation, line items) with its routes, API handlers and tables - Rebrand to Cobble: site data, Lato, handoff design tokens (light and dark) in main.css, brand assets in public/brand, ember stone logo and regenerated favicons - Add bots, bot_messages, bot_tasks and bot_task_steps tables, regenerate DB types (BUN_SQLITE_COBBLE_*) and add a sample crew seed (seed:bots) - Add the chat shell at /admin/bots: rail with pinned coordinator and user popover, bottom-anchored conversation with composer, working-on panel, and a send-message API scoped to the session user - Add BotStone and Wordmark components, crew status and relative time utils with tests, and update AGENTS.md Co-Authored-By: Claude Opus 5.5 <[email protected]>
111 lines
3.0 KiB
TypeScript
111 lines
3.0 KiB
TypeScript
import { useState, useRef, useEffect } from "react";
|
|
import _ from "lodash";
|
|
import { AppData } from "../data/app-data";
|
|
import useStatus from "@moduletrace/twui/hooks/useStatus";
|
|
import adminFetchHandler from "../functions/frontend/admin-fetch-handler";
|
|
import type { BUN_SQLITE_COBBLE_ALL_TYPEDEFS } from "@/db/types/db";
|
|
import type { AdminFetchParams, ApiReqParams } from "../types";
|
|
import EJSON from "../utils/ejson";
|
|
|
|
type Params<T extends {} = BUN_SQLITE_COBBLE_ALL_TYPEDEFS> = Omit<
|
|
AdminFetchParams<T>,
|
|
"method" | "body"
|
|
> & {
|
|
debug?: boolean;
|
|
initial_res?: T[];
|
|
count?: boolean;
|
|
};
|
|
|
|
export function useFetchGet<T extends {} = BUN_SQLITE_COBBLE_ALL_TYPEDEFS>({
|
|
url,
|
|
debug,
|
|
initial_res,
|
|
query: api_query_params,
|
|
}: Params<T>) {
|
|
const [res, setRes] = useState<T[] | undefined>(initial_res);
|
|
const [count, setCount] = useState<number | undefined>();
|
|
const [page, setPage] = useState<number>(api_query_params?.page || 1);
|
|
const [limit, setLimit] = useState<number>(AppData["DefaultQueryLimit"]);
|
|
const [query, setQuery] = useState<ApiReqParams<T> | undefined>(
|
|
api_query_params,
|
|
);
|
|
|
|
const { ready, setReady } = useStatus();
|
|
|
|
const fetching_ref = useRef(true);
|
|
|
|
useEffect(() => {
|
|
if (res) return;
|
|
|
|
fetching_ref.current = true;
|
|
|
|
const final_query = _.merge<ApiReqParams<T>, ApiReqParams<T>>(
|
|
query || {},
|
|
{ page },
|
|
);
|
|
|
|
if (debug) {
|
|
console.log("final_query", final_query);
|
|
}
|
|
|
|
adminFetchHandler<T>({
|
|
url,
|
|
query: final_query,
|
|
})
|
|
.then((result) => {
|
|
if (debug) {
|
|
console.log("Main result", result);
|
|
}
|
|
|
|
setRes(result.payload || []);
|
|
setCount(result?.count);
|
|
})
|
|
.catch((err) => {
|
|
console.log(`Error => ${err.message}`);
|
|
setRes([]);
|
|
})
|
|
.finally(() => {
|
|
if (!ready) {
|
|
setTimeout(() => {
|
|
fetching_ref.current = false;
|
|
setReady(true);
|
|
}, 1000);
|
|
} else {
|
|
fetching_ref.current = false;
|
|
}
|
|
});
|
|
}, [res]);
|
|
|
|
useEffect(() => {
|
|
if (!ready) return;
|
|
if (fetching_ref.current) return;
|
|
setRes(undefined);
|
|
}, [page]);
|
|
|
|
useEffect(() => {
|
|
if (!ready) return;
|
|
if (fetching_ref.current) return;
|
|
if (query) {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set("q", EJSON.stringify(query || {}) || "");
|
|
history.pushState(null, "", url);
|
|
}
|
|
setPage(1);
|
|
setRes(undefined);
|
|
}, [query]);
|
|
|
|
return {
|
|
res,
|
|
setRes,
|
|
count,
|
|
page,
|
|
setPage,
|
|
query,
|
|
setQuery,
|
|
limit,
|
|
setLimit,
|
|
ready,
|
|
setReady,
|
|
};
|
|
}
|