feat(dimentorin): halaman Materi + AI Agent RAG
- /mentoring/materi: list materi (kategori chips, cards) + detail konten per slug - /mentoring/ai-agent: chatbox RAG — tanya materi, jawaban dari Qdrant context + sumber relevansi % - service: getMaterialsList/getMaterialBySlug/getMaterialById/getMaterialCategories + postChat + hooks - header: menu Materi & AI Agent - e2e verified: list 3 materi, detail tampil, chat 'ownership' jawab dari materi Rust + 3 sources (84/64/61%)
This commit is contained in:
@@ -3,6 +3,11 @@ import {
|
||||
TArticleDetail,
|
||||
TArticleListItem,
|
||||
TArticleListParams,
|
||||
TChatRequest,
|
||||
TChatResponse,
|
||||
TMaterialDetail,
|
||||
TMaterialItem,
|
||||
TMaterialListResponse,
|
||||
TBookSessionRequest,
|
||||
TCreatePaymentRequest,
|
||||
TMentorAvailability,
|
||||
@@ -214,3 +219,47 @@ export const postRefreshPayment = async (
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
|
||||
// ---- Materials ----
|
||||
export const getMaterialsList = async (): Promise<TMaterialListResponse> => {
|
||||
const { data } = await api({ method: 'GET', url: '/dimentorin/materials' });
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const getMaterialBySlug = async (
|
||||
slug: string
|
||||
): Promise<TMaterialDetail> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: `/dimentorin/materials/slug/${slug}`,
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const getMaterialById = async (
|
||||
id: string
|
||||
): Promise<TMaterialDetail> => {
|
||||
const { data } = await api({ method: 'GET', url: `/dimentorin/materials/${id}` });
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const getMaterialCategories = async (): Promise<string[]> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/dimentorin/materials/categories',
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
// ---- AI Agent (RAG) ----
|
||||
export const postChat = async (
|
||||
request: TChatRequest
|
||||
): Promise<TChatResponse> => {
|
||||
const { data } = await api({
|
||||
method: 'POST',
|
||||
url: '/dimentorin/ai/chat',
|
||||
data: request,
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,12 @@ import {
|
||||
getMentorStats,
|
||||
getMentors,
|
||||
getMyPayments,
|
||||
getMaterialById,
|
||||
getMaterialBySlug,
|
||||
getMaterialCategories,
|
||||
getMaterialsList,
|
||||
getMySessions,
|
||||
postChat,
|
||||
getPaymentById,
|
||||
getSessionPayments,
|
||||
postBookSession,
|
||||
@@ -26,7 +31,12 @@ import {
|
||||
TArticleDetail,
|
||||
TArticleListItem,
|
||||
TArticleListParams,
|
||||
TBookSessionRequest,
|
||||
TBookSessionRequest,
|
||||
TChatRequest,
|
||||
TChatResponse,
|
||||
TMaterialDetail,
|
||||
TMaterialItem,
|
||||
TMaterialListResponse,
|
||||
TCreatePaymentRequest,
|
||||
TMentorAvailability,
|
||||
TMentorDetail,
|
||||
@@ -245,4 +255,55 @@ export const useGetArticleById = (
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ---- Materials ----
|
||||
export const useGetMaterialsList = (): UseQueryResult<
|
||||
TMaterialListResponse,
|
||||
TResponseError
|
||||
> => {
|
||||
return useQuery({
|
||||
queryKey: ['get-materials-list'],
|
||||
queryFn: async () => await getMaterialsList(),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetMaterialBySlug = (
|
||||
slug: string
|
||||
): UseQueryResult<TMaterialDetail, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: ['get-material-by-slug', slug],
|
||||
queryFn: async () => await getMaterialBySlug(slug),
|
||||
enabled: !!slug,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetMaterialById = (
|
||||
id: string
|
||||
): UseQueryResult<TMaterialDetail, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: ['get-material-by-id', id],
|
||||
queryFn: async () => await getMaterialById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetMaterialCategories = (): UseQueryResult<
|
||||
string[],
|
||||
TResponseError
|
||||
> => {
|
||||
return useQuery({
|
||||
queryKey: ['get-material-categories'],
|
||||
queryFn: async () => await getMaterialCategories(),
|
||||
});
|
||||
};
|
||||
|
||||
// ---- AI Agent ----
|
||||
export const usePostChat = () => {
|
||||
return useMutation({
|
||||
mutationKey: ['post-chat'],
|
||||
mutationFn: async (request: TChatRequest) => await postChat(request),
|
||||
});
|
||||
};
|
||||
|
||||
export type { TResponseMessage };
|
||||
|
||||
@@ -182,3 +182,50 @@ export type TPayment = {
|
||||
export type TCreatePaymentRequest = {
|
||||
method: string; // 'va' | 'qris' | 'manual'
|
||||
};
|
||||
|
||||
|
||||
export type TMaterialItem = {
|
||||
id: string;
|
||||
mentorId: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
category: string;
|
||||
description: string;
|
||||
coverUrl?: string | null;
|
||||
isPublished: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type TMaterialDetail = TMaterialItem & {
|
||||
content: string;
|
||||
};
|
||||
|
||||
export type TMaterialListResponse = {
|
||||
data: TMaterialItem[];
|
||||
meta: {
|
||||
page: number;
|
||||
per_page: number;
|
||||
total?: number;
|
||||
total_pages?: number;
|
||||
has_next: boolean;
|
||||
has_prev: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type TChatRequest = {
|
||||
question: string;
|
||||
materialId?: string;
|
||||
};
|
||||
|
||||
export type TChatSource = {
|
||||
materialId: string;
|
||||
title: string;
|
||||
score: number;
|
||||
snippet: string;
|
||||
};
|
||||
|
||||
export type TChatResponse = {
|
||||
answer: string;
|
||||
sources: TChatSource[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user