landing and theme page for admin made client side rendered

This commit is contained in:
Hikmet 2023-07-21 14:38:39 +03:00
parent 56d49d10aa
commit d5ed9201c5
14 changed files with 99 additions and 45 deletions

View File

@ -12,7 +12,6 @@ export function YouTubeVideoUrlTester({
media: OptionalStringValueProperty;
setMedia: Dispatch<SetStateAction<OptionalStringValueProperty>>;
}) {
console.log(media);
return (
<div>
{media.status !== "success" && (

View File

@ -3,9 +3,11 @@ import { LessonSideBar } from "./lesson_side_bar";
import { ThemeSideBar } from "./theme_side_bar";
import { InteractiveFeedbacks } from "@/core/components/interactive_feedbacks";
import { useAdminViewModelContext } from "../theme_page/view_model/context_providers/admin_view_model";
import { useBaseViewModelContext } from "../theme_page/view_model/context_providers/base_view_model";
export function AdminTools() {
const { stalling, snackbar, setSnackbar } = useAdminViewModelContext()!;
const { activeLesson } = useBaseViewModelContext()!;
const [isThemeSideBarOpen, setIsThemeSideBarOpen] = useState(false);
const [isLessonSideBarOpen, setIsLessonSideBarOpen] = useState(false);
@ -16,11 +18,14 @@ export function AdminTools() {
isOpen={isThemeSideBarOpen}
setIsOpen={setIsThemeSideBarOpen}
/>
<LessonSideBar
hide={isThemeSideBarOpen}
isOpen={isLessonSideBarOpen}
setIsOpen={setIsLessonSideBarOpen}
/>
{activeLesson !== null && (
<LessonSideBar
hide={isThemeSideBarOpen}
isOpen={isLessonSideBarOpen}
setIsOpen={setIsLessonSideBarOpen}
activeLesson={activeLesson}
/>
)}
<InteractiveFeedbacks
stalling={stalling}
snackbar={snackbar}

View File

@ -15,17 +15,19 @@ export function LessonSideBar({
isOpen,
setIsOpen,
hide,
activeLesson,
}: {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
hide: boolean;
activeLesson: number;
}) {
const pathname = usePathname();
const { push } = useRouter();
const [modified, setModified] = useState(false);
const [adminLessonTitle, setAdminLessonTitle] = useState("");
const [adminLessonExplanation, setAdminLessonExplanation] = useState("");
const { activeLesson, lessons } = useBaseViewModelContext()!;
const { lessons } = useBaseViewModelContext()!;
const {
saveLesson,
createNewLesson,

View File

@ -10,7 +10,7 @@ export function ThemeCard({ themeMeta }: { themeMeta: ThemeMetaDTO }) {
return (
<li>
<a
href={`${pathname}/temalar/${id}`}
href={`/${pathname}/temalar/${id}`}
className={`${styles["card"]} card-button`}
>
<img

View File

@ -18,6 +18,18 @@ export function ThemesSection({
{themeMetas.map((themeMeta) => {
return <ThemeCard key={themeMeta.id} themeMeta={themeMeta} />;
})}
{/* <button
style={{
backgroundColor: "#eee",
border: "1px solid #ccc",
display: "flex",
justifyContent: "center",
alignItems: "center",
fontSize: "1.5rem",
}}
>
Yeni Tema +
</button> */}
</ol>
</section>
</div>

View File

@ -18,7 +18,7 @@ export interface BaseViewModel {
setThemeYoutubeVideoUrl: Dispatch<
SetStateAction<BaseViewModel["themeYoutubeVideoUrl"]>
>;
activeLesson: number;
activeLesson: number | null;
setActiveLesson: Dispatch<SetStateAction<BaseViewModel["activeLesson"]>>;
isActivityDialogOpen: boolean;
openActivity: (activity: Activity<any>) => void;

View File

@ -3,9 +3,11 @@ import { useBaseViewModelContext } from "../../view_model/context_providers/base
import { BaseViewModel } from "../../model/base_view_model";
export function TabBar() {
const { lessons, activeLesson, setActiveLesson, isAdmin } =
const { lessons, activeLesson, setActiveLesson } =
useBaseViewModelContext() as BaseViewModel;
if (!activeLesson) return null;
return (
<Tabs
value={activeLesson}

View File

@ -1,10 +1,8 @@
import { useBaseViewModelContext } from "./context_providers/base_view_model";
import { AdminViewModel } from "../model/admin_view_model";
import { LessonRepository } from "@/core/models/repositories/lesson_repository";
import { useState } from "react";
import { StatusResponse } from "@/core/models/repositories/status_response";
import { Activity, LessonMap } from "@/core/models/entities/learning_unit";
import { usePathname, useRouter } from "next/navigation";
export function useAdminViewModel(): AdminViewModel {
const {
@ -93,6 +91,7 @@ export function useAdminViewModel(): AdminViewModel {
title: string;
explanation: string;
}) => {
if (!activeLesson) return;
setStalling(true);
const lessonId = lessons.meta[activeLesson].id;
const resObj = await fetch(`/api/admin/temalar/${themeId}/${lessonId}`, {
@ -169,6 +168,7 @@ export function useAdminViewModel(): AdminViewModel {
};
const deleteLesson = async () => {
if (!activeLesson) return;
setStalling(true);
const lessonId = lessons.meta[activeLesson].id;
const resObj = await fetch(`/api/admin/temalar/${themeId}/${lessonId}`, {
@ -205,10 +205,11 @@ export function useAdminViewModel(): AdminViewModel {
);
return newLessons;
});
setActiveLesson((prev) => (prev === 0 ? prev : prev - 1));
setActiveLesson((prev) => (prev ? (prev === 0 ? null : prev - 1) : null));
};
const createNewActivity = async () => {
if (!activeLesson) return;
setStalling(true);
const lessonId = lessons.meta[activeLesson].id;
const resObj = await fetch(`/api/admin/temalar/${themeId}/${lessonId}`, {
@ -260,6 +261,7 @@ export function useAdminViewModel(): AdminViewModel {
activityIndex: number;
activityId: string;
}) => {
if (!activeLesson) return;
setStalling(true);
const lessonId = lessons.meta[activeLesson].id;
const resObj = await fetch(

View File

@ -13,7 +13,9 @@ export function useBaseViewModel(
theme.youtubeVideoUrl
);
const [lessons, setLessons] = useState(theme.lessons);
const [activeLesson, setActiveLesson] = useState(0);
const [activeLesson, setActiveLesson] = useState<number | null>(
theme.lessons.meta.length > 0 ? 0 : null
);
// ACTIVITY DIALOG
const [activeActivity, setActiveActivity] = useState<Activity<any> | null>(

View File

@ -1,13 +1,21 @@
import { ThemeMetaDTO } from "@/core/models/dtos/theme_meta_dto";
import { ThemeReposityImplementation } from "@/core/models/repositories/theme_repository_implementation";
import { LandingPageView } from "@/features/landing_page_view";
import Head from "next/head";
import { useEffect, useState } from "react";
export default function AdminPage() {
const [themeMetas, setThemeMetas] = useState<ThemeMetaDTO[]>([]);
const fetchThemeMetas = async () => {
const resObj = await fetch(`/api/admin/temalar`);
const res = (await resObj.json()) as ThemeMetaDTO[];
setThemeMetas(res);
};
useEffect(() => {
fetchThemeMetas();
}, []);
export default function AdminPage({
themeMetas,
}: {
themeMetas: ThemeMetaDTO[];
}) {
return (
<>
<Head>
@ -18,14 +26,3 @@ export default function AdminPage({
</>
);
}
export async function getServerSideProps() {
const themeRepository = new ThemeReposityImplementation();
const themeMetas = await themeRepository.getThemeMetas();
return {
props: {
themeMetas,
},
};
}

View File

@ -1,20 +1,33 @@
import { GetServerSidePropsContext } from "next";
import { ThemeReposityImplementation } from "@/core/models/repositories/theme_repository_implementation";
import { Theme } from "@/core/models/entities/learning_unit";
import { ThemePage as ThemePageElement } from "@/features/theme_page";
import { useEffect, useState } from "react";
export default function ThemePage({ themeData }: { themeData: Theme }) {
return <ThemePageElement theme={Theme.from(themeData)} isAdmin={true} />;
export default function ThemePage({ themeId }: { themeId: string }) {
const [themeData, setThemeData] = useState<Theme>();
const fetchTheme = async (themeId: string) => {
const resObj = await fetch(`/api/admin/temalar/${themeId}`);
const res = (await resObj.json()) as Theme;
setThemeData(Theme.from(res));
};
useEffect(() => {
fetchTheme(themeId);
}, [themeId]);
if (themeData) return <ThemePageElement theme={themeData} isAdmin={true} />;
return (
<div className="admin-waiting-room">
<h1>Merhaba Admin!</h1>
<p>Tema Yükleniyor...</p>
</div>
);
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
const path = context.params as unknown as { theme: string };
const themeRepository = new ThemeReposityImplementation();
const themeData = await themeRepository.getThemeData(path.theme);
const { theme } = context.params as unknown as { theme: string };
return {
props: {
themeData,
},
};
return { props: { themeId: theme } };
}

View File

@ -27,6 +27,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
return res.status(501).json({ error: "Unsopported action" });
} else if (req.method === "GET") {
const theme = req.query.theme as string;
const themeData = await themeRepo.getThemeData(theme);
return res.status(200).json(themeData);
}
return res.status(501).json({ error: "Unsopported request method" });

View File

@ -1,7 +1,10 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { ThemeReposityImplementation } from "@/core/models/repositories/theme_repository_implementation";
async function handler(req: NextApiRequest, res: NextApiResponse) {
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const themeRepo = new ThemeReposityImplementation();
if (req.method === "PUT") {
@ -18,9 +21,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
return res.status(501).json({ error: "Unsopported action" });
} else if (req.method === "GET") {
const themeMetas = await themeRepo.getThemeMetas();
return res.status(200).json(themeMetas);
}
return res.status(501).json({ error: "Unsopported request method" });
}
export default handler;

View File

@ -80,6 +80,18 @@ div.simple-card {
}
}
div.admin-waiting-room {
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
row-gap: 3rem;
justify-content: center;
align-items: center;
font-size: 120%;
text-align: center;
}
@media (max-width: 400px) {
html {
font-size: 75%; /* this is the base font size for screens smaller than 400px */