Skip to content

Instantly share code, notes, and snippets.

@tbxark
Last active February 15, 2025 04:39
Show Gist options
  • Select an option

  • Save tbxark/adbe65da6d49605f81d4a33f57f163f5 to your computer and use it in GitHub Desktop.

Select an option

Save tbxark/adbe65da6d49605f81d4a33f57f163f5 to your computer and use it in GitHub Desktop.
Sphere Dash Adapter
import { Api } from "@/api/dash/Api";
import { http } from "@/utils/http";
import type { AxiosInstance, AxiosResponse } from "axios";
import type { HttpClient } from "@/api/dash/http-client";
interface PureHTTP {
axiosInstance: AxiosInstance;
}
type UnwrapResponse<T> =
T extends Promise<AxiosResponse<infer R>> ? Promise<R> : T;
type TransformFunction<T> = T extends (...args: infer Args) => infer Return
? (...args: Args) => UnwrapResponse<Return>
: never;
type UnwrappedApiClient = {
[K in keyof Omit<Api, keyof HttpClient>]: TransformFunction<Api[K]>;
};
type AdapterAPI = UnwrappedApiClient & {
request: TransformFunction<Api["request"]>;
};
function createNewAPI(): AdapterAPI {
const api = new Api();
api.instance = (http.constructor as unknown as PureHTTP).axiosInstance;
return api as AdapterAPI;
}
export const API = createNewAPI();
type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}`
? `${T}${Capitalize<SnakeToCamelCase<U>>}`
: S;
type SnakeToCamelCaseObject<T> = {
[K in keyof T as K extends string
? SnakeToCamelCase<K>
: K]: T[K] extends object ? SnakeToCamelCaseObject<T[K]> : T[K];
};
export function convertToCamelCase<T extends object>(
obj: T,
): SnakeToCamelCaseObject<T> {
const toCamelCase = (str: string): string => {
return str
.split('_')
.map((part, index) =>
index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),
)
.join('');
};
const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(obj)) {
const camelKey = toCamelCase(key);
result[camelKey] =
value instanceof Object && !Array.isArray(value)
? convertToCamelCase(value)
: value;
}
return result as SnakeToCamelCaseObject<T>;
}
import { computed, ref, watch } from "vue";
interface ListQueryParam {
page?: number;
limit?: number;
}
export interface ListManager<QUERY, DATA> {
query: ReturnType<typeof ref<QUERY>>;
data: ReturnType<typeof ref<DATA>>;
totalPage: ReturnType<typeof ref<number>>;
displayPage: ReturnType<typeof ref<number>>;
execute: (query: QUERY) => Promise<void>;
onResetQuery: () => void;
onSearch: () => Promise<void>;
onPageChange: (val: number) => Promise<void>;
}
type PaginatedListData<ITEM, KEY extends string> = {
[P in KEY]?: ITEM[];
} & {
total?: number;
total_page?: number;
};
type PaginatedListItem<REQUEST, KEY extends string> = REQUEST extends (
...args: any
) => Promise<{ data?: PaginatedListData<infer ITEM, KEY> }>
? ITEM
: never;
export function createListManager<
KEY extends string,
REQUEST extends (...args: any) => Promise<any>,
ITEM = PaginatedListItem<REQUEST, KEY>,
QUERY extends ListQueryParam = Parameters<REQUEST>[0],
DATA extends PaginatedListData<ITEM, KEY> = PaginatedListData<ITEM, KEY>
>(options: { query: QUERY; request: REQUEST }): ListManager<QUERY, DATA> {
const query = ref<QUERY>(options.query);
const data = ref<DATA | null>(null);
const totalPage = ref(1);
const displayPage = computed(() => (query.value.page || 0) + 1);
watch(data, newData => {
if (newData.total_page) {
totalPage.value = newData.total_page;
}
});
const execute = async (query: QUERY) => {
try {
data.value = (await options.request(query)).data;
} catch (e) {
console.error("[ListManager] execute", e);
}
};
const onResetQuery = () => {
query.value = {};
};
const onSearch = async () => {
query.value.page = 0;
await execute(query.value);
};
const onPageChange = async (val: number) => {
query.value.page = val - 1;
await execute(query.value);
};
return {
query,
data,
totalPage,
displayPage,
execute,
onResetQuery,
onSearch,
onPageChange
} as ListManager<QUERY, DATA>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment