First Commit. Duplicate bunext-sqlite preset

This commit is contained in:
2026-10-18 16:47:11 +01:00
commit 81e4a8aecc
718 changed files with 41695 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
import { useState, useRef, useEffect } from "react";
import adminCrudHandler from "../functions/frontend/admin-crud-handler";
import _ from "lodash";
import { AppData } from "../data/app-data";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import type { ApiReqParams, ClientAdminCrudParams, TableType } from "../types";
import type { ServerQueryParam } from "@moduletrace/bun-sqlite";
import EJSON from "../utils/ejson";
type Params<T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS> = Pick<
ClientAdminCrudParams,
"id" | "count"
> & {
table: TableType;
sql_query?: ServerQueryParam<T>;
debug?: boolean;
page?: number;
limit?: number;
initial_res?: T[];
ready?: boolean;
request_query?: ApiReqParams<T>;
};
export function useAdminCrudGet<T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>({
table,
sql_query,
count: add_count_param,
debug,
page: initial_page,
limit: initial_limit,
initial_res,
ready: initial_ready,
request_query,
...args
}: Params<T>) {
const [res, setRes] = useState<T[] | undefined>(initial_res);
const [count, setCount] = useState<number | undefined>();
const [page, setPage] = useState<number>(initial_page || 1);
const [limit, setLimit] = useState<number>(
initial_limit || AppData["DefaultQueryLimit"],
);
const [query, setQuery] = useState<ServerQueryParam<T> | undefined>(
sql_query,
);
const { ready, setReady } = useStatus();
const fetching_ref = useRef(true);
useEffect(() => {
if (res) return;
if (typeof initial_ready == "boolean" && !initial_ready) return;
fetching_ref.current = true;
const final_query = _.merge<ServerQueryParam<T>, ServerQueryParam<T>>(
query || {},
{ page, limit: query?.limit || limit },
);
if (debug) {
console.log("final_query", final_query);
}
adminCrudHandler<T>({
action: "get",
table,
sql_query: final_query,
count: add_count_param,
request_query,
...args,
})
.then((result) => {
if (debug) {
console.log("Main result", result);
}
setRes(result.payload || []);
setCount(result?.count);
})
.catch((err) => {
console.log(`Error querying ${table} => ${err.message}`);
setRes([]);
})
.finally(() => {
if (!ready) {
setTimeout(() => {
fetching_ref.current = false;
setReady(true);
}, 1000);
} else {
fetching_ref.current = false;
}
});
}, [res, initial_ready]);
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,
};
}
+14
View File
@@ -0,0 +1,14 @@
import { useContext } from "react";
import { AppContext } from "@/src/pages/__root";
export function useAuthStatus() {
const { pageProps } = useContext(AppContext);
const user = pageProps?.user;
const user_types = pageProps?.user_types;
return {
is_logged_in: !!user?.logged_in_status,
user,
user_types,
};
}
+110
View File
@@ -0,0 +1,110 @@
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_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import type { AdminFetchParams, ApiReqParams } from "../types";
import EJSON from "../utils/ejson";
type Params<T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS> = Omit<
AdminFetchParams<T>,
"method" | "body"
> & {
debug?: boolean;
initial_res?: T[];
count?: boolean;
};
export function useFetchGet<T extends {} = BUN_SQLITE_BSQLITE_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,
};
}
+161
View File
@@ -0,0 +1,161 @@
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import {
useCallback,
useContext,
useEffect,
useState,
type Dispatch,
type SetStateAction,
} from "react";
import { AppContext } from "../pages/__root";
import type { ImageInputToBase64FunctionReturn } from "@moduletrace/twui/utils/form/imageInputToBase64";
import EJSON from "../utils/ejson";
import twuiSlugify from "@moduletrace/twui/utils/slugify";
import _ from "lodash";
import type { AppContextObject } from "../types";
type Params<T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS> = {
/**
* Existing record for update. All unneccasary fields
* like join fields should be excluded.
*/
existing?: T;
/**
* Full Existing record with all unneccessary
* Fields like join fields.
*/
existing_full?: T;
default?: T;
/**
* Form title.
*/
title?: string;
/**
* Object keys to exclude from local storage.
* This is useful for image base64 fields
*/
local_storage_exclude_keys?: (keyof T)[];
/**
* Async prep to run before the form is marked ready.
* Useful for seeding derived fields from the existing record.
*/
before_ready_function?: (params: {
form: T;
setForm: Dispatch<SetStateAction<T>>;
app_context: AppContextObject;
}) => Promise<void>;
};
export default function useFormInit<
T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS,
>(params?: Params<T>) {
const appContext = useContext(AppContext);
const [form, setForm] = useState<T>(
params?.existing || params?.default || ({} as T),
);
const [image, setImage] = useState<ImageInputToBase64FunctionReturn>();
const { loading, setLoading, status, setStatus, ready, setReady } =
useStatus();
const form_title = params?.title;
const local_storage_form_title = form_title
? twuiSlugify(
`${form_title.replace(/ ?form$/i, "")}_form_local_storage`,
)
: undefined;
const runBeforeReadyAndSetReady = () => {
if (params?.before_ready_function) {
params
.before_ready_function({
form,
setForm,
app_context: appContext,
})
.then(() => {})
.catch((e2) => {
console.log(`Before ready function error:`, e2.message);
})
.finally(() => {
setReady(true);
});
} else {
setReady(true);
}
};
useEffect(() => {
if (!ready) return;
if (status) {
setStatus(undefined);
}
if (local_storage_form_title) {
try {
let form_to_store = _.cloneDeep(form);
if (params?.local_storage_exclude_keys) {
params.local_storage_exclude_keys.forEach((k) => {
delete form_to_store[k];
});
}
localStorage.setItem(
local_storage_form_title,
EJSON.stringify(form_to_store) || "",
);
} catch (error) {
} finally {
}
}
}, [form, ready]);
useEffect(() => {
if (local_storage_form_title) {
try {
const existing_form_in_local_storage = localStorage.getItem(
local_storage_form_title,
);
const existing_ls_form_object = EJSON.parse(
existing_form_in_local_storage,
) as T | undefined;
if (existing_ls_form_object && !Object.keys(form)?.[0]) {
setForm(existing_ls_form_object);
}
} catch (error) {
} finally {
runBeforeReadyAndSetReady();
}
} else {
runBeforeReadyAndSetReady();
}
}, []);
const clearLocalForm = useCallback(() => {
if (local_storage_form_title) {
localStorage.removeItem(local_storage_form_title);
}
}, []);
return {
form,
setForm,
loading,
setLoading,
status,
setStatus,
existing: params?.existing,
existing_full: params?.existing_full,
app_context: appContext,
image,
setImage,
clearLocalForm,
ready,
setReady,
};
}
+204
View File
@@ -0,0 +1,204 @@
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type useFormInit from "@/src/hooks/use-form-init";
import { AppContext } from "@/src/pages/__root";
import type { ApiReqParams, GoogleWindow } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import { useContext, useEffect, useRef, useState } from "react";
const GoogleScriptSelector =
'script[src="https://accounts.google.com/gsi/client"]';
export type GoogleAuthMode = "login" | "signup";
type Params = Pick<
ReturnType<typeof useFormInit>,
"loading" | "setLoading" | "setStatus"
> & {
mode: GoogleAuthMode;
};
export default function useGoogleAuth({
loading,
setStatus,
setLoading,
mode,
}: Params) {
const { pageProps } = useContext(AppContext);
const googleButtonRef = useRef<HTMLDivElement>(null);
const googleInitializedRef = useRef(false);
const [scriptLoaded, setScriptLoaded] = useState(false);
const [buttonWidth, setButtonWidth] = useState(0);
const [googleReady, setGoogleReady] = useState(false);
const googleClientId = pageProps?.envs?.GOOGLE_CLIENT_ID;
const submitGoogleAuth = async (google_token: string) => {
setLoading(true);
const res = await fetchApi<ApiReqParams, APIResponseObject>(
`/api/auth/google-login`,
{
method: "POST",
body: {
google_token,
google_signup: mode == "signup",
},
},
);
if (res.success) {
window.location.pathname = `/admin`;
} else {
setStatus({
error: true,
msg:
res.msg ||
(mode == "signup"
? "Couldn't sign up with Google."
: "Couldn't log in with Google."),
});
setLoading(false);
}
};
useEffect(() => {
if (!googleClientId) {
return;
}
const clientWindow = window as GoogleWindow;
if (clientWindow.google?.accounts?.id) {
setScriptLoaded(true);
return;
}
const existingScript = document.querySelector(
GoogleScriptSelector,
) as HTMLScriptElement | null;
const handleLoad = () => {
setScriptLoaded(true);
};
const handleError = () => {
setStatus({
error: true,
msg: "Couldn't load Google sign-in.",
});
};
if (existingScript) {
existingScript.addEventListener("load", handleLoad);
existingScript.addEventListener("error", handleError);
return () => {
existingScript.removeEventListener("load", handleLoad);
existingScript.removeEventListener("error", handleError);
};
}
const script = document.createElement("script");
script.src = "https://accounts.google.com/gsi/client";
script.async = true;
script.defer = true;
script.addEventListener("load", handleLoad);
script.addEventListener("error", handleError);
document.head.appendChild(script);
return () => {
script.removeEventListener("load", handleLoad);
script.removeEventListener("error", handleError);
};
}, [googleClientId]);
useEffect(() => {
if (!googleButtonRef.current || typeof ResizeObserver === "undefined") {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
const nextWidth = Math.round(entries[0]?.contentRect.width || 0);
if (!nextWidth) {
return;
}
setButtonWidth((prev) => (prev === nextWidth ? prev : nextWidth));
});
resizeObserver.observe(googleButtonRef.current);
return () => {
resizeObserver.disconnect();
};
}, []);
useEffect(() => {
if (
!googleClientId ||
!scriptLoaded ||
!googleButtonRef.current ||
!buttonWidth
) {
return;
}
const googleId = (window as GoogleWindow).google?.accounts?.id;
if (!googleId) {
return;
}
if (!googleInitializedRef.current) {
googleId.initialize({
client_id: googleClientId,
callback: async ({ credential }) => {
if (!credential) {
setStatus({
error: true,
msg: "Google didn't return a sign-in token.",
});
return;
}
await submitGoogleAuth(credential);
},
});
googleInitializedRef.current = true;
}
googleButtonRef.current.innerHTML = "";
googleId.renderButton(googleButtonRef.current, {
theme: "outline",
size: "large",
text: mode == "signup" ? "signup_with" : "signin_with",
shape: "rectangular",
width: Math.min(buttonWidth, 400),
});
setGoogleReady(true);
}, [googleClientId, buttonWidth, scriptLoaded]);
const handleButtonClick = () => {
if (loading) {
return;
}
if (!googleInitializedRef.current) {
setStatus({
error: true,
msg: "Google sign-in is still loading. Please try again.",
});
}
};
return {
loading,
handleButtonClick,
googleButtonRef,
googleReady,
};
}
+27
View File
@@ -0,0 +1,27 @@
import type { User } from "../types";
import { useEffect, useState } from "react";
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type { BUN_SQLITE_BSQLITE_USER_TYPES } from "@/db/types/db";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export function useLocalUser() {
const [user, setUser] = useState<User>();
const [userTypes, setUserTypes] =
useState<BUN_SQLITE_BSQLITE_USER_TYPES[]>();
useEffect(() => {
fetchApi<any, APIResponseObject>(`/api/admin/user-auth`).then((res) => {
if (res.singleRes) {
setUser(res.singleRes as User);
}
if (res.payload) {
setUserTypes(res.payload as BUN_SQLITE_BSQLITE_USER_TYPES[]);
}
});
}, []);
return {
user,
user_types: userTypes,
};
}
+13
View File
@@ -0,0 +1,13 @@
import { useContext } from "react";
import { AppContext } from "@/src/pages/__root";
import type { LoginMethod } from "@/src/types";
/**
* Site-wide login method from `AUTH_LOGIN_METHOD`, passed down by
* `__root.server.ts`.
*/
export default function useLoginMethod(): LoginMethod {
const { pageProps } = useContext(AppContext);
return pageProps?.envs?.AUTH_LOGIN_METHOD == "password" ? "password" : "code";
}
+13
View File
@@ -0,0 +1,13 @@
import { useEffect, useState } from "react";
import readSSOPendingLogin from "@/src/functions/frontend/read-sso-pending-login";
import type { SSOPendingLogin } from "@/src/types";
export default function useSSOPendingLogin() {
const [pending, setPending] = useState<SSOPendingLogin>();
useEffect(() => {
setPending(readSSOPendingLogin());
}, []);
return pending;
}
+109
View File
@@ -0,0 +1,109 @@
import _ from "lodash";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import adminFetchHandler from "../functions/frontend/admin-fetch-handler";
import type { ApiReqParams } from "../types";
import type { BUN_SQLITE_BSQLITE_USERS_JOIN } from "../types/sql-joins";
import { useEffect, useRef, useState } from "react";
import EJSON from "../utils/ejson";
type Params = {
query?: ApiReqParams;
debug?: boolean;
page?: number;
initial_users?: BUN_SQLITE_BSQLITE_USERS_JOIN[];
};
export function useUsersGet({
query: req_query,
debug,
page: initial_page,
initial_users,
}: Params) {
const [users, setUsers] = useState<
BUN_SQLITE_BSQLITE_USERS_JOIN[] | undefined
>(initial_users);
const [count, setCount] = useState<number | undefined>();
const [page, setPage] = useState<number>(initial_page || 1);
const [usersQuery, setUsersQuery] = useState<ApiReqParams | undefined>(
req_query,
);
const { ready, setReady } = useStatus();
const fetching_ref = useRef(true);
useEffect(() => {
if (users) return;
fetching_ref.current = true;
const final_query = _.merge<ApiReqParams, ApiReqParams>(
usersQuery || {},
{
page,
count: true,
},
);
if (debug) {
console.log("final_query", final_query);
}
adminFetchHandler<BUN_SQLITE_BSQLITE_USERS_JOIN>({
url: `/api/admin/grab-users`,
query: final_query,
})
.then((result) => {
if (debug) {
console.log("Main result", result);
}
setUsers(result.payload || []);
setCount(result?.count);
})
.catch((err) => {
console.log(`Error querying users table => ${err.message}`);
setUsers([]);
})
.finally(() => {
if (!ready) {
setTimeout(() => {
fetching_ref.current = false;
setReady(true);
}, 1000);
} else {
fetching_ref.current = false;
}
});
}, [users]);
useEffect(() => {
if (!ready) return;
if (fetching_ref.current) return;
setUsers(undefined);
}, [page]);
useEffect(() => {
if (!ready) return;
if (fetching_ref.current) return;
if (usersQuery) {
const url = new URL(window.location.href);
url.searchParams.set("q", EJSON.stringify(usersQuery || {}) || "");
history.pushState(null, "", url);
}
setPage(1);
setUsers(undefined);
}, [usersQuery]);
return {
users,
setUsers,
count,
page,
setPage,
usersQuery,
setUsersQuery,
ready,
setReady,
};
}