Finish admin page

This commit is contained in:
Nông Đức Huy
2026-08-13 23:20:23 +07:00
parent cda5d21d0d
commit 8fd23d162f
107 changed files with 6321 additions and 563 deletions
+6
View File
@@ -27,6 +27,10 @@ import {
type ReviewsAdminResource,
type ReviewsResource,
} from './resources/reviews';
import {
createTaxonomyAdminResource,
type TaxonomyAdminResource,
} from './resources/taxonomy-admin';
/**
* Resource modules are added here as the backend grows — one file per bounded
@@ -47,6 +51,7 @@ export interface ApiClient {
readonly reviews: ReviewsResource;
readonly reviewsAdmin: ReviewsAdminResource;
readonly account: AccountResource;
readonly taxonomyAdmin: TaxonomyAdminResource;
readonly content: ContentResource;
readonly contentAdmin: ContentAdminResource;
}
@@ -67,6 +72,7 @@ export function createApiClient(options: HttpClientOptions): ApiClient {
reviews: createReviewsResource(http),
reviewsAdmin: createReviewsAdminResource(http),
account: createAccountResource(http),
taxonomyAdmin: createTaxonomyAdminResource(http),
content: createContentResource(http),
contentAdmin: createContentAdminResource(http),
};
+8
View File
@@ -59,4 +59,12 @@ export type {
PostListParams,
} from './resources/content';
export type { AccountResource, AddressPayload, ProfilePayload } from './resources/account';
export type {
BrandPayload,
CategoryPayload,
CollectionPayload,
TaxonomyAdminResource,
TaxonomyListParams,
TaxonomyResource,
} from './resources/taxonomy-admin';
export type { ApiClient } from './create-client';
@@ -0,0 +1,87 @@
import type { AdminBrand, AdminCategory, AdminCollection, OffsetPaginated } from '@sport/types';
import type { HttpClient } from '../http-client';
export interface TaxonomyListParams {
page?: number;
perPage?: number;
q?: string;
activeOnly?: boolean;
}
export interface TaxonomyTranslationPayload {
name: string;
slug: string;
description?: string | null;
metaTitle?: string | null;
metaDescription?: string | null;
}
interface TaxonomyBasePayload {
name: string;
slug: string;
description?: string | null;
isActive?: boolean;
metaTitle?: string | null;
metaDescription?: string | null;
translations?: Record<string, TaxonomyTranslationPayload>;
}
export interface BrandPayload extends TaxonomyBasePayload {
logoId?: string | null;
}
export interface CategoryPayload extends TaxonomyBasePayload {
parentId?: string | null;
position?: number;
imageId?: string | null;
}
export interface CollectionPayload extends TaxonomyBasePayload {
type?: 'MANUAL' | 'AUTOMATED';
bannerId?: string | null;
startsAt?: string | null;
endsAt?: string | null;
position?: number;
}
/** CRUD for one taxonomy entity. The three share a shape, so they share a type. */
export interface TaxonomyResource<TEntity, TPayload> {
list(params?: TaxonomyListParams): Promise<OffsetPaginated<TEntity>>;
getById(id: string): Promise<TEntity>;
create(payload: TPayload): Promise<TEntity>;
update(id: string, payload: TPayload): Promise<TEntity>;
remove(id: string): Promise<void>;
}
export interface TaxonomyAdminResource {
brands: TaxonomyResource<AdminBrand, BrandPayload>;
categories: TaxonomyResource<AdminCategory, CategoryPayload>;
collections: TaxonomyResource<AdminCollection, CollectionPayload>;
}
function resource<TEntity, TPayload>(
http: HttpClient,
path: string,
): TaxonomyResource<TEntity, TPayload> {
// Never cached: an operator who just deactivated a brand must see it off.
const uncached = { cache: 'no-store' } as const;
return {
list: (params = {}) =>
http.get<OffsetPaginated<TEntity>>(`/admin/${path}`, { ...uncached, query: { ...params } }),
getById: (id) => http.get<TEntity>(`/admin/${path}/${encodeURIComponent(id)}`, uncached),
create: (payload) => http.post<TEntity>(`/admin/${path}`, payload, uncached),
update: (id, payload) =>
http.patch<TEntity>(`/admin/${path}/${encodeURIComponent(id)}`, payload, uncached),
remove: (id) => http.delete<void>(`/admin/${path}/${encodeURIComponent(id)}`, uncached),
};
}
export function createTaxonomyAdminResource(http: HttpClient): TaxonomyAdminResource {
return {
brands: resource<AdminBrand, BrandPayload>(http, 'brands'),
categories: resource<AdminCategory, CategoryPayload>(http, 'categories'),
collections: resource<AdminCollection, CollectionPayload>(http, 'collections'),
};
}