mirror of
https://github.com/khairul169/garage-webui.git
synced 2026-09-15 00:43:21 +07:00
feat: project init
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
type FetchOptions = Omit<RequestInit, "headers" | "body"> & {
|
||||
params?: Record<string, any>;
|
||||
headers?: Record<string, string>;
|
||||
body?: any;
|
||||
};
|
||||
|
||||
const ADMIN_KEY = "E1tDBf4mhc/XMHq1YJkDE6N1j3AZG9dRWR+vDDTyASk=";
|
||||
|
||||
const api = {
|
||||
async fetch<T = any>(url: string, options?: Partial<FetchOptions>) {
|
||||
const headers: Record<string, string> = {};
|
||||
const _url = new URL("/api" + url, window.location.origin);
|
||||
|
||||
if (options?.params) {
|
||||
Object.entries(options.params).forEach(([key, value]) => {
|
||||
_url.searchParams.set(key, String(value));
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof options?.body === "object" &&
|
||||
!(options.body instanceof FormData)
|
||||
) {
|
||||
options.body = JSON.stringify(options.body);
|
||||
headers["Content-Type"] = "application/json";
|
||||
}
|
||||
|
||||
if (ADMIN_KEY) {
|
||||
headers["Authorization"] = `Bearer ${ADMIN_KEY}`;
|
||||
}
|
||||
|
||||
const res = await fetch(_url, {
|
||||
...options,
|
||||
headers: { ...headers, ...(options?.headers || {}) },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
|
||||
const isJson = res.headers
|
||||
.get("Content-Type")
|
||||
?.includes("application/json");
|
||||
|
||||
if (isJson) {
|
||||
const json = (await res.json()) as T;
|
||||
return json;
|
||||
}
|
||||
|
||||
const text = await res.text();
|
||||
return text as unknown as T;
|
||||
},
|
||||
|
||||
async get<T = any>(url: string, options?: Partial<FetchOptions>) {
|
||||
return this.fetch<T>(url, {
|
||||
...options,
|
||||
method: "GET",
|
||||
});
|
||||
},
|
||||
|
||||
async post<T = any>(url: string, options?: Partial<FetchOptions>) {
|
||||
return this.fetch<T>(url, {
|
||||
...options,
|
||||
method: "POST",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default api;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createStore, useStore } from "zustand";
|
||||
|
||||
export const createDisclosure = <T = any>() => {
|
||||
const store = createStore(() => ({
|
||||
data: undefined as T | null,
|
||||
isOpen: false,
|
||||
}));
|
||||
|
||||
return {
|
||||
store,
|
||||
use: () => useStore(store),
|
||||
open: (data?: T | null) => store.setState({ isOpen: true, data }),
|
||||
close: () => store.setState({ isOpen: false }),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import clsx from "clsx";
|
||||
import { toast } from "sonner";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export const cn = (...args: any[]) => {
|
||||
return twMerge(clsx(...args));
|
||||
};
|
||||
|
||||
export const ucfirst = (text?: string | null) => {
|
||||
return text ? text.charAt(0).toUpperCase() + text.slice(1) : null;
|
||||
};
|
||||
|
||||
export const readableBytes = (bytes?: number | null, divider = 1024) => {
|
||||
if (bytes == null || Number.isNaN(bytes)) return "n/a";
|
||||
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||
if (bytes === 0) return "n/a";
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(divider));
|
||||
|
||||
return `${(bytes / Math.pow(divider, i)).toFixed(1)} ${sizes[i]}`;
|
||||
};
|
||||
|
||||
export const handleError = (err: unknown) => {
|
||||
toast.error((err as Error)?.message || "Unknown error");
|
||||
};
|
||||
Reference in New Issue
Block a user