mirror of
https://github.com/samkaraca/lazuri-doviguram.git
synced 2026-04-29 17:59:51 +00:00
Refactor AdminLessonApi and AdminActivityApi.
This commit is contained in:
parent
2e6b9b6eb2
commit
01787f93a7
13
src/api/activity/useAdminActivity.ts
Normal file
13
src/api/activity/useAdminActivity.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
|
||||
export const useAdminActivity = ({ themeId, lessonId, activityId }: { themeId: string; lessonId: string; activityId: string }) => {
|
||||
return useQuery({
|
||||
queryKey: ["admin-activity", themeId, lessonId, activityId],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiAdmin.get(`/themes/${themeId}/lessons/${lessonId}/activities/${activityId}`);
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
}
|
||||
13
src/api/activity/useAdminCreateActivity.ts
Normal file
13
src/api/activity/useAdminCreateActivity.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import IActivity from "@/lib/activity/activity";
|
||||
|
||||
export const useAdminCreateActivity = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lessonId, activity }: { themeId: string; lessonId: string; activity: IActivity }) => {
|
||||
const { data } = await apiAdmin.post(`/themes/${themeId}/lessons/${lessonId}/activities`, { activity });
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
12
src/api/activity/useAdminDeleteActivity.ts
Normal file
12
src/api/activity/useAdminDeleteActivity.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
|
||||
export const useAdminDeleteActivity = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lessonId, activityId }: { themeId: string; lessonId: string; activityId: string }) => {
|
||||
const { data } = await apiAdmin.delete(`/themes/${themeId}/lessons/${lessonId}/activities/${activityId}`);
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
13
src/api/activity/useAdminUpdateActivity.ts
Normal file
13
src/api/activity/useAdminUpdateActivity.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
import IActivity from "@/lib/activity/activity";
|
||||
|
||||
export const useAdminUpdateActivity = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lessonId, activity }: { themeId: string; lessonId: string; activity: IActivity }) => {
|
||||
const { data } = await apiAdmin.put(`/themes/${themeId}/lessons/${lessonId}/activities/${activity.id}`, { activity });
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
import IActivity from "../lib/activity/activity";
|
||||
import { BackendActivityService } from "../backend/services/activity_service";
|
||||
import { ApiResponse } from "./api_response";
|
||||
|
||||
export class AdminActivityApi {
|
||||
async fetchActivity(
|
||||
themeId: string,
|
||||
lessonId: string,
|
||||
activityId: string
|
||||
): Promise<IActivity | undefined> {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lessonId}/activities/${activityId}`
|
||||
);
|
||||
const res = await (resObj.json() as ReturnType<
|
||||
BackendActivityService["getActivity"]
|
||||
>);
|
||||
if (res.status === "success" && res.data) {
|
||||
return res.data;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async saveActivity(themeId: string, lessonId: string, activity: IActivity) {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lessonId}/activities/${activity.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
activity,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
return await (resObj.json() as ReturnType<
|
||||
BackendActivityService["saveActivity"]
|
||||
>);
|
||||
}
|
||||
|
||||
deleteActivity = async (
|
||||
themeId: string,
|
||||
lessonId: string,
|
||||
activityId: string
|
||||
) => {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lessonId}/activities/${activityId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
return (await resObj.json()) as ApiResponse;
|
||||
};
|
||||
|
||||
createActivity = async (
|
||||
themeId: string,
|
||||
lessonId: string,
|
||||
activity: IActivity
|
||||
) => {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lessonId}/activities`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
activity,
|
||||
}),
|
||||
}
|
||||
);
|
||||
return (await resObj.json()) as ApiResponse;
|
||||
};
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import ILesson from "@/lib/lesson/lesson";
|
||||
import { ApiResponse } from "./api_response";
|
||||
|
||||
export class AdminLessonApi {
|
||||
deleteLesson = async (themeId: string, lessonId: string) => {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lessonId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
return (await resObj.json()) as ApiResponse;
|
||||
};
|
||||
|
||||
createLesson = async (themeId: string, lesson: ILesson) => {
|
||||
const resObj = await fetch(`/api/admin/themes/${themeId}/lessons`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
lesson,
|
||||
}),
|
||||
});
|
||||
return (await resObj.json()) as ApiResponse;
|
||||
};
|
||||
|
||||
saveLesson = async (themeId: string, lesson: Omit<ILesson, "activities">) => {
|
||||
const resObj = await fetch(
|
||||
`/api/admin/themes/${themeId}/lessons/${lesson.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
lesson,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
return (await resObj.json()) as ApiResponse;
|
||||
};
|
||||
}
|
||||
13
src/api/lesson/useAdminCreateLesson.ts
Normal file
13
src/api/lesson/useAdminCreateLesson.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import ILesson from "@/lib/lesson/lesson";
|
||||
|
||||
export const useAdminCreateLesson = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lesson }: { themeId: string; lesson: ILesson }) => {
|
||||
const { data } = await apiAdmin.post(`/themes/${themeId}/lessons`, { lesson });
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
12
src/api/lesson/useAdminDeleteLesson.ts
Normal file
12
src/api/lesson/useAdminDeleteLesson.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
export const useAdminDeleteLesson = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lessonId }: { themeId: string; lessonId: string }) => {
|
||||
const { data } = await apiAdmin.delete(`/themes/${themeId}/lessons/${lessonId}`);
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
13
src/api/lesson/useAdminUpdateLesson.ts
Normal file
13
src/api/lesson/useAdminUpdateLesson.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { apiAdmin } from "@/lib/axios";
|
||||
import { ApiResponse } from "../api_response";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import ILesson from "@/lib/lesson/lesson";
|
||||
|
||||
export const useAdminUpdateLesson = () => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ themeId, lesson }: { themeId: string; lesson: Omit<ILesson, "activities"> }) => {
|
||||
const { data } = await apiAdmin.put(`/themes/${themeId}/lessons/${lesson.id}`, { lesson });
|
||||
return data as ApiResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1,15 +1,15 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { IViewModel } from "../model/view_model";
|
||||
import { AdminActivityApi } from "@/api/admin_activity_api";
|
||||
import IActivity from "@/lib/activity/activity";
|
||||
import IExercise from "@/lib/exercise/exercise";
|
||||
import { useAdminUpdateActivity } from "@/api/activity/useAdminUpdateActivity";
|
||||
|
||||
export function useViewModel(
|
||||
themeId: string,
|
||||
lessonId: string,
|
||||
activityData: IActivity
|
||||
): IViewModel {
|
||||
const adminService = useRef(new AdminActivityApi());
|
||||
const { mutateAsync: adminUpdateActivity } = useAdminUpdateActivity();
|
||||
const [type, setType] = useState<IViewModel["type"]>(activityData.type);
|
||||
const [title, setTitle] = useState(activityData.title);
|
||||
const [explanation, setExplanation] = useState(activityData.explanation);
|
||||
@ -21,70 +21,74 @@ export function useViewModel(
|
||||
>(
|
||||
activityData.youtubeVideoUrl
|
||||
? {
|
||||
value: activityData.youtubeVideoUrl,
|
||||
status: "success",
|
||||
}
|
||||
value: activityData.youtubeVideoUrl,
|
||||
status: "success",
|
||||
}
|
||||
: {
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
);
|
||||
const [image, setImage] = useState<IViewModel["image"]>(
|
||||
activityData.image
|
||||
? {
|
||||
value: activityData.image,
|
||||
status: "success",
|
||||
}
|
||||
value: activityData.image,
|
||||
status: "success",
|
||||
}
|
||||
: {
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
);
|
||||
const [audio, setAudio] = useState<IViewModel["audio"]>(
|
||||
activityData.audio
|
||||
? {
|
||||
value: activityData.audio,
|
||||
status: "success",
|
||||
}
|
||||
value: activityData.audio,
|
||||
status: "success",
|
||||
}
|
||||
: {
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
value: "",
|
||||
status: "idle",
|
||||
}
|
||||
);
|
||||
const [exercise, setExercise] = useState<IExercise>(activityData.exercise);
|
||||
|
||||
const changeActivityType = (newActivityType: IViewModel["type"]) => {
|
||||
const exerciseType: IExercise["type"] =
|
||||
newActivityType === "drag-into-blanks" ||
|
||||
newActivityType === "type-in-blanks"
|
||||
newActivityType === "type-in-blanks"
|
||||
? "fill-in-blanks-exercise"
|
||||
: newActivityType === "pair-texts-with-images" ||
|
||||
newActivityType === "true-false"
|
||||
? "qa-exercise"
|
||||
: "multiple-choice-exercise";
|
||||
? "qa-exercise"
|
||||
: "multiple-choice-exercise";
|
||||
setType(newActivityType);
|
||||
setExercise({ type: exerciseType, answers: [], template: [] });
|
||||
};
|
||||
|
||||
const saveActivity = async () => {
|
||||
try {
|
||||
await adminService.current.saveActivity(themeId, lessonId, {
|
||||
id: activityData.id,
|
||||
title,
|
||||
explanation,
|
||||
textContent,
|
||||
savedAt: Date.now(),
|
||||
type,
|
||||
audio: audio.status === "success" ? audio.value : activityData.audio,
|
||||
exercise,
|
||||
image: image.status === "success" ? image.value : activityData.image,
|
||||
youtubeVideoUrl:
|
||||
youtubeVideoUrl.status === "success"
|
||||
? youtubeVideoUrl.value
|
||||
: activityData.youtubeVideoUrl,
|
||||
await adminUpdateActivity({
|
||||
themeId,
|
||||
lessonId,
|
||||
activity: {
|
||||
id: activityData.id,
|
||||
title,
|
||||
explanation,
|
||||
textContent,
|
||||
savedAt: Date.now(),
|
||||
type,
|
||||
audio: audio.status === "success" ? audio.value : activityData.audio,
|
||||
exercise,
|
||||
image: image.status === "success" ? image.value : activityData.image,
|
||||
youtubeVideoUrl:
|
||||
youtubeVideoUrl.status === "success"
|
||||
? youtubeVideoUrl.value
|
||||
: activityData.youtubeVideoUrl,
|
||||
},
|
||||
});
|
||||
localStorage.removeItem(activityData.id);
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -7,11 +7,14 @@ import { ApiResponse } from "@/api/api_response";
|
||||
import { defaultActivity } from "@/lib/activity/default_activity";
|
||||
import { slugifyLaz } from "@/utils/slugify_laz";
|
||||
import ILesson from "@/lib/lesson/lesson";
|
||||
import { AdminLessonApi } from "@/api/admin_lesson_api";
|
||||
import { AdminActivityApi } from "@/api/admin_activity_api";
|
||||
import { useAdminDeleteTheme } from "@/api/theme/useAdminDeleteTheme";
|
||||
import { useAdminUpdateTheme } from "@/api/theme/useAdminUpdateTheme";
|
||||
import { useAdminRelocateTheme } from "@/api/theme/useAdminRelocateTheme";
|
||||
import { useAdminCreateLesson } from "@/api/lesson/useAdminCreateLesson";
|
||||
import { useAdminUpdateLesson } from "@/api/lesson/useAdminUpdateLesson";
|
||||
import { useAdminDeleteLesson } from "@/api/lesson/useAdminDeleteLesson";
|
||||
import { useAdminCreateActivity } from "@/api/activity/useAdminCreateActivity";
|
||||
import { useAdminDeleteActivity } from "@/api/activity/useAdminDeleteActivity";
|
||||
|
||||
export function useAdminViewModel(): IAdminViewModel {
|
||||
const {
|
||||
@ -32,8 +35,11 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
const { mutateAsync: adminRelocateTheme } = useAdminRelocateTheme();
|
||||
const { mutateAsync: adminUpdateTheme } = useAdminUpdateTheme();
|
||||
const { mutateAsync: adminDeleteTheme } = useAdminDeleteTheme();
|
||||
const adminLessonApi = useRef(new AdminLessonApi());
|
||||
const adminActivityApi = useRef(new AdminActivityApi());
|
||||
const { mutateAsync: adminDeleteLesson } = useAdminDeleteLesson();
|
||||
const { mutateAsync: adminUpdateLesson } = useAdminUpdateLesson();
|
||||
const { mutateAsync: adminCreateLesson } = useAdminCreateLesson();
|
||||
const { mutateAsync: adminCreateActivity } = useAdminCreateActivity();
|
||||
const { mutateAsync: adminDeleteActivity } = useAdminDeleteActivity();
|
||||
const [stalling, setStalling] = useState(false);
|
||||
const [snackbar, setSnackbar] = useState<{
|
||||
severity: "error" | "success" | "warning" | "info";
|
||||
@ -127,7 +133,7 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
const saveLesson = async (lesson: Omit<ILesson, "activities">) => {
|
||||
if (activeLesson === null) return;
|
||||
await withFeedback(
|
||||
() => adminLessonApi.current.saveLesson(id, lesson),
|
||||
() => adminUpdateLesson({ themeId: id, lesson }),
|
||||
() => {
|
||||
setLessons((prev) => {
|
||||
const newLessons = [...prev];
|
||||
@ -141,7 +147,7 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
const createLesson = async () => {
|
||||
const lesson = defaultLesson();
|
||||
await withFeedback(
|
||||
() => adminLessonApi.current.createLesson(id, lesson),
|
||||
() => adminCreateLesson({ themeId: id, lesson }),
|
||||
(res) => {
|
||||
setLessons((prev) => [...prev, lesson]);
|
||||
if (activeLesson === null) {
|
||||
@ -155,7 +161,7 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
if (activeLesson === null) return;
|
||||
const lessonId = lessons[activeLesson].id;
|
||||
await withFeedback(
|
||||
() => adminLessonApi.current.deleteLesson(id, lessonId),
|
||||
() => adminDeleteLesson({ themeId: id, lessonId }),
|
||||
(res) => {
|
||||
const newLessons = lessons.filter((l) => l.id !== lessonId);
|
||||
let newActiveLesson: number | null = activeLesson;
|
||||
@ -177,7 +183,7 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
const activity = defaultActivity();
|
||||
const lessonId = lessons[activeLesson].id;
|
||||
withFeedback(
|
||||
() => adminActivityApi.current.createActivity(id, lessonId, activity),
|
||||
() => adminCreateActivity({ themeId: id, lessonId, activity }),
|
||||
() => {
|
||||
setLessons((prev) => {
|
||||
return prev.map((l) => {
|
||||
@ -194,7 +200,7 @@ export function useAdminViewModel(): IAdminViewModel {
|
||||
if (activeLesson === null) return;
|
||||
const lessonId = lessons[activeLesson].id;
|
||||
withFeedback(
|
||||
() => adminActivityApi.current.deleteActivity(id, lessonId, activityId),
|
||||
() => adminDeleteActivity({ themeId: id, lessonId, activityId }),
|
||||
() => {
|
||||
setLessons((prev) => {
|
||||
const newLessons = [...prev] as any;
|
||||
|
||||
@ -1,41 +1,21 @@
|
||||
import { ActivityEditor } from "@/features/activity_editor";
|
||||
import IActivity from "@/lib/activity/activity";
|
||||
import { AdminActivityApi } from "@/api/admin_activity_api";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useAdminActivity } from "@/api/activity/useAdminActivity";
|
||||
|
||||
export default function ActivityEditorPage() {
|
||||
const pathname = usePathname();
|
||||
const adminService = useRef(new AdminActivityApi());
|
||||
const [pathnames, setPathnames] = useState<[string, string, string]>();
|
||||
const [activityData, setActivityData] = useState<IActivity>();
|
||||
const { query } = useRouter();
|
||||
const [themeId, lessonId, activityId] = useMemo(() => {
|
||||
return [query.theme as string, query.lesson as string, query.activity as string];
|
||||
}, [query]);
|
||||
const { data: activityData } = useAdminActivity({ themeId, lessonId, activityId });
|
||||
|
||||
const fetchActivity = async (
|
||||
themeId: string,
|
||||
lessonId: string,
|
||||
activityId: string
|
||||
) => {
|
||||
setActivityData(
|
||||
await adminService.current.fetchActivity(themeId, lessonId, activityId)
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathname) return;
|
||||
const splitPathname = pathname.split("/");
|
||||
const themeId = splitPathname[splitPathname.length - 3];
|
||||
const lessonId = splitPathname[splitPathname.length - 2];
|
||||
const activityId = splitPathname[splitPathname.length - 1];
|
||||
setPathnames([themeId, lessonId, activityId]);
|
||||
fetchActivity(themeId, lessonId, activityId);
|
||||
}, [pathname]);
|
||||
|
||||
if (activityData && pathnames) {
|
||||
if (activityData?.data && themeId && lessonId && activityId) {
|
||||
return (
|
||||
<ActivityEditor
|
||||
themeId={pathnames[0]}
|
||||
lessonId={pathnames[1]}
|
||||
activityData={activityData}
|
||||
themeId={themeId}
|
||||
lessonId={lessonId}
|
||||
activityData={activityData.data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import TP from "@/features/theme_page";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useRouter } from "next/router";
|
||||
import dynamic from "next/dynamic";
|
||||
import ITheme from "@/lib/theme/theme";
|
||||
import { useAdminTheme } from "@/api/theme/useAdminTheme";
|
||||
|
||||
const AT = dynamic(() => import("@/features/admin_tools"), {
|
||||
@ -10,17 +8,11 @@ const AT = dynamic(() => import("@/features/admin_tools"), {
|
||||
});
|
||||
|
||||
export default function ThemePage() {
|
||||
const pathname = usePathname();
|
||||
const { data: adminTheme } = useAdminTheme({ themeSlug: pathname ? pathname.split("/").pop() as string : "" });
|
||||
const [themeData, setThemeData] = useState<ITheme>();
|
||||
const { query } = useRouter();
|
||||
const { data: adminTheme } = useAdminTheme({ themeSlug: query.theme as string });
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathname || !adminTheme) return;
|
||||
setThemeData(adminTheme?.data as ITheme);
|
||||
}, [pathname, adminTheme]);
|
||||
|
||||
if (themeData) {
|
||||
return <TP home="/admin" theme={themeData} adminTools={<AT />} />;
|
||||
if (adminTheme?.data) {
|
||||
return <TP home="/admin" theme={adminTheme.data} adminTools={<AT />} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user