diff --git a/app/(full-page)/auth/login/page.tsx b/app/(full-page)/auth/login/page.tsx
index d3dd85ca..1e8d8728 100644
--- a/app/(full-page)/auth/login/page.tsx
+++ b/app/(full-page)/auth/login/page.tsx
@@ -8,56 +8,110 @@ import { Password } from 'primereact/password';
import { LayoutContext } from '../../../../layout/context/layoutcontext';
import { InputText } from 'primereact/inputtext';
import { classNames } from 'primereact/utils';
+import InfoBanner from '@/app/components/InfoBanner';
+
+import { useForm } from 'react-hook-form';
+import { schema } from '@/schemas/authSchema';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { Controller } from 'react-hook-form';
+import { getUser, login } from '@/services/auth';
+import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn';
+import { getToken } from '@/utils/auth';
+import { logout } from '@/utils/logout';
+import { LoginType } from '@/types/login';
const LoginPage = () => {
- const [password, setPassword] = useState('');
- const [checked, setChecked] = useState(false);
- const { layoutConfig } = useContext(LayoutContext);
+ const { layoutConfig, setUser, setMessage, setGlobalLoading } = useContext(LayoutContext);
const router = useRouter();
- const containerClassName = classNames('surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden', { 'p-input-filled': layoutConfig.inputStyle === 'filled' });
+ // const containerClassName = classNames('surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden', { 'p-input-filled': layoutConfig.inputStyle === 'filled' });
- return (
-
-
-

-
-
-
-

-
Welcome, Isabel!
-
Sign in to continue
-
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ control
+ } = useForm({
+ resolver: yupResolver(schema),
+ mode: 'onChange'
+ });
-
-
-
+ const onSubmit = async (value: LoginType) => {
+ console.log('Данные пользователя: ', value);
-
-
setPassword(e.target.value)} placeholder="Password" toggleMask className="w-full mb-5" inputClassName="w-full p-3 md:w-30rem">
+ const user = await login(value);
+ console.log(user);
+ if (user && user.success) {
+ document.cookie = `access_token=${user.token.access_token}; path=/; Secure; SameSite=Strict; expires=${user.token.expires_at}`;
+
+ const token = user.token.access_token;
+ if (token) {
+ const res = await getUser(token);
+ try {
+ if (res?.success) {
+ console.log(res);
+ if (res?.user.is_working) {
+ window.location.href = '/course';
+ }
+ } else {
+ logout({ setUser, setGlobalLoading });
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' }
+ }); // messege - Ошибка при авторизации
+ console.log('Ошибка при получении пользователя');
+ }
+ } catch (error) {
+ logout({ setUser, setGlobalLoading });
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' }
+ }); // messege - Ошибка при авторизации
+ console.log('Ошибка при получении пользователя');
+ }
+ }
+ } else {
+ console.log('Ошибка при авторизации');
+ }
+ };
+
+ const onError = (errors:any) => {
+ console.log('Ошибки формы:', errors);
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Катаа', detail: 'ПОвтор' }
+ }); // messege - Ошибка при авторизации
+ };
-
-
+ return (
+
+
+
+
+

+
+
+
+
+
+
diff --git a/app/(full-page)/pages/notfound/page.tsx b/app/(full-page)/pages/notfound/page.tsx
index 52c0b4ce..c9c4dc0e 100644
--- a/app/(full-page)/pages/notfound/page.tsx
+++ b/app/(full-page)/pages/notfound/page.tsx
@@ -49,6 +49,6 @@ const NotFoundPage = () => {
);
-};
+};
export default NotFoundPage;
diff --git a/app/(main)/course/[courseTheme]/[lessons]/page.tsx b/app/(main)/course/[courseTheme]/[lessons]/page.tsx
new file mode 100644
index 00000000..2afff9fc
--- /dev/null
+++ b/app/(main)/course/[courseTheme]/[lessons]/page.tsx
@@ -0,0 +1,279 @@
+'use client';
+
+import { useContext, useEffect, useState } from 'react';
+import { TabView, TabPanel } from 'primereact/tabview';
+import CKEditorWrapper from '@/app/components/CKEditorWrapper.tsx';
+import { Button } from 'primereact/button';
+import { useForm } from 'react-hook-form';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { lessonSchema } from '@/schemas/lessonSchema';
+import Redacting from '@/app/components/popUp/Redacting';
+import { addLesson, deleteLesson, fetchLesson, updateLesson } from '@/services/courses';
+import { getToken } from '@/utils/auth';
+import { useParams } from 'next/navigation';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import useErrorMessage from '@/hooks/useErrorMessage';
+import { getRedactor } from '@/utils/getRedactor';
+import { getConfirmOptions } from '@/utils/getConfirmOptions';
+import LessonTyping from '@/app/components/lessons/LessonTyping';
+import { TabViewChange } from '@/types/tabViewChange';
+
+export default function Lesson() {
+ const [activeIndex, setActiveIndex] = useState
(0);
+ const [contentShow, setContentShow] = useState(true);
+ const [textShow, setTextShow] = useState(false);
+ const [editMode, setEditMode] = useState(false);
+ const [editingLesson, setEditingLesson] = useState(null);
+ const [textValue, setTextValue] = useState<{ id: number | null }>({ id: null });
+ const { setMessage } = useContext(LayoutContext);
+
+ const showError = useErrorMessage();
+ const [sentValues, setSentValues] = useState('');
+
+ const params = useParams();
+ const courseId = params.courseTheme;
+ const lessonId = params.lessons;
+
+ const handleText = (e: string | null) => {
+ setSentValues(e);
+ };
+
+ useEffect(() => {
+ console.log(sentValues);
+ }, [sentValues]);
+
+ const {
+ register,
+ setValue,
+ formState: { errors }
+ } = useForm({
+ resolver: yupResolver(lessonSchema),
+ mode: 'onChange'
+ });
+
+ const handleFetchLesson = async () => {
+ // skeleton = false
+
+ const token = getToken('access_token');
+ const data = await fetchLesson('text', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null);
+ console.log(data);
+
+ if (data.success) {
+ if (data?.content?.content) {
+ setTextValue({ id: data?.content.id });
+ setSentValues(data.content.content);
+ setTextShow(true);
+ } else {
+ setTextShow(false);
+ }
+ } else {
+ setTextShow(false);
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ // skeleton = false
+ }
+ };
+
+ const handleAddLesson = async () => {
+ const token = getToken('access_token');
+
+ const data = await addLesson('text', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, String(sentValues), 'the title');
+ if (data.success) {
+ handleFetchLesson();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү Кошулдуу!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleDeleteLesson = async () => {
+ const token = getToken('access_token');
+
+ const data = await deleteLesson('text', token, Number(courseId), Number(lessonId), textValue.id);
+ if (data.success) {
+ handleFetchLesson();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const edit = async () => {
+ setTextShow(false);
+ setEditMode(true);
+
+ const token = getToken('access_token');
+ const data = await fetchLesson('text', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null);
+ if (data.success) {
+ setEditingLesson(data.content.content);
+ }
+ };
+
+ const cencalEdit = () => {
+ handleFetchLesson();
+ setEditingLesson('');
+ setTextShow(true);
+ setEditMode(false);
+ };
+
+ const handleUpdateLesson = async () => {
+ const token = getToken('access_token');
+ const data = await updateLesson('text', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, textValue.id, sentValues);
+ console.log(data);
+
+ if (data.success) {
+ setEditMode(false);
+ setTextShow(true);
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при изменении темы' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleTabChange = (e: TabViewChange) => {
+ console.log(e);
+
+ if (e.index === 0) handleFetchLesson();
+ setActiveIndex(e.index);
+ };
+
+ return (
+
+ {/* header section */}
+
handleTabChange(e)}
+ activeIndex={activeIndex}
+ className="main-bg"
+ pt={{
+ nav: { className: 'flex flex-wrap justify-around' },
+ panelContainer: { className: 'flex-1 pl-4' }
+ }}
+ >
+ {/* CKEDITOR */}
+
+ {contentShow && (
+
+ {textShow ? (
+
+
+
+
+
+ Текст
+
+
+
+ {/*
*/}
+
+
+
+ xx-xx-xx
+
+
+
+
+ {/* {sentValues.text} */}
+
+
+ ) : (
+
+ {editMode ? (
+ <>
+
handleText(e)} />
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+ )}
+
+ )}
+
+
+ {/* DOC */}
+
+ {contentShow && }
+
+
+ {/* USEFUL LINKS */}
+
+ {contentShow && }
+
+
+ {/* VIDEO */}
+
+ {contentShow && (
+
+ )}
+
+
+
+ );
+}
diff --git a/app/(main)/course/[courseTheme]/page.tsx b/app/(main)/course/[courseTheme]/page.tsx
new file mode 100644
index 00000000..ff21b8f1
--- /dev/null
+++ b/app/(main)/course/[courseTheme]/page.tsx
@@ -0,0 +1,277 @@
+'use client';
+
+import ConfirmModal from '@/app/components/popUp/ConfirmModal';
+import FormModal from '@/app/components/popUp/FormModal';
+import GroupSkeleton from '@/app/components/skeleton/GroupSkeleton';
+import Link from 'next/link';
+import { Button } from 'primereact/button';
+import { Column } from 'primereact/column';
+import { DataTable } from 'primereact/datatable';
+import { InputText } from 'primereact/inputtext';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import React, { useContext, useEffect, useState } from 'react';
+import { getToken } from '@/utils/auth';
+import { useParams } from 'next/navigation';
+import { addThemes, deleteTheme, fetchCourseInfo, fetchThemes, updateTheme } from '@/services/courses';
+import useErrorMessage from '@/hooks/useErrorMessage';
+import { CourseCreateType } from '@/types/courseCreateType';
+import { NotFound } from '@/app/components/NotFound';
+
+export default function CourseTheme() {
+ const [hasThemes, setHasThemes] = useState(false);
+ const [themes, setThemes] = useState([]);
+ const [themeValue, setThemeValue] = useState({ title: '', description: '', video_url: ''});
+ const [themeInfo, setThemeInfo] = useState();
+ const [courseTitle, setCourseTitle] = useState('');
+ const [selectedCourse, setSelectedCourse] = useState<{id: number | null}>({id: null});
+ const [formVisible, setFormVisible] = useState(false);
+ const [editMode, setEditMode] = useState(false);
+ const [forStart, setForStart] = useState(false);
+ const [skeleton, setSkeleton] = useState(false);
+ const { setMessage } = useContext(LayoutContext);
+
+ const { courseTheme } = useParams() as { courseTheme: string };
+ const showError = useErrorMessage();
+
+ const handleFetchThemes = async () => {
+ const token = getToken('access_token');
+ const data = await fetchThemes(token, Number(courseTheme));
+
+ if (data.lessons) {
+ setThemes(data.lessons.data);
+ setHasThemes(false);
+ } else {
+ setHasThemes(true);
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Проблема с соединением. Повторите заново' }
+ }); // messege - Ошибка загрузки курсов
+ if(data.response.status){
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleFetchInfo = async () => {
+ const token = getToken('access_token');
+ const data = await fetchCourseInfo(token, Number(courseTheme));
+
+ if(data.success) {
+ setThemeInfo(data.course);
+ }
+ };
+
+ const handleAddTheme = async () => {
+ if (themeValue.title.length < 1) {
+ alert('hi');
+ return null;
+ }
+
+ const token = getToken('access_token');
+ const data = await addThemes(token, Number(courseTheme), themeValue.title);
+ console.log(data);
+
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchThemes();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү кошулду!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' }
+ }); // messege - Ошибка при добавлении
+ if(data.response.status){
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleDeleteCourse = async (id: number) => {
+ const token = getToken('access_token');
+
+ const data = await deleteTheme(token, id);
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchThemes();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' }
+ }); // messege - Ошибка при добавлении
+ if(data.response.status){
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleUpdateTheme = async () => {
+ const token = getToken('access_token');
+
+ const data = await updateTheme(token, Number(courseTheme), selectedCourse.id, themeValue);
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchThemes();
+ clearValues();
+ setEditMode(false);
+ setSelectedCourse({id: null});
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка при при изменении темы', detail: 'Заполняйте поля правильно' }
+ }); // messege - Ошибка при изменении курса
+ if(data.response.status){
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const clearValues = () => {
+ setThemeValue({ title: '', description: '', video_url: ''});
+ setCourseTitle('');
+ setEditMode(false);
+ setSelectedCourse({id: null});
+ };
+
+ const getConfirmOptions = (id: number) => ({
+ message: 'Сиз чын эле өчүрүүнү каалайсызбы??',
+ header: 'Өчүрүү',
+ icon: 'pi pi-info-circle',
+ defaultFocus: 'reject',
+ acceptClassName: 'p-button-danger',
+ acceptLabel: 'Кийинки кадам', // кастомная надпись для "Yes"
+ rejectLabel: 'Артка',
+ accept: () => handleDeleteCourse(id),
+ reject: () => console.log('Удаление отменено')
+ });
+
+ const toggleSkeleton = () => {
+ setSkeleton(true);
+ setTimeout(() => {
+ setSkeleton(false);
+ }, 1000);
+ };
+
+ useEffect(() => {
+ handleFetchInfo();
+ handleFetchThemes();
+ }, []);
+
+ useEffect(() => {
+ const title = courseTitle.trim();
+ title.length > 0 ? setForStart(false) : setForStart(true);
+ }, [courseTitle]);
+
+ useEffect(() => {
+ themes.length < 1 ? setHasThemes(true)
+ : setHasThemes(false);
+ }, [themes]);
+
+ const titleInfoClass = `${!themeInfo?.image ? 'items-center' : 'w-full'} ${themeInfo?.image ? 'w-1/2' : 'w-full'}`;
+ const titleImageClass = `${themeInfo?.image ? 'md:w-1/3' : ''}`;
+
+ return (
+
+ {/* title section */}
+
+
+
+ {themeInfo?.title}
+
+
{themeInfo?.description}
+
+
+ {themeInfo?.created_at}
+ Home/theme
+
+
+
+
+
}/)
+
+
+
+ {/* add button*/}
+
+ );
+}
diff --git a/app/(main)/course/page.tsx b/app/(main)/course/page.tsx
new file mode 100644
index 00000000..feaba0ee
--- /dev/null
+++ b/app/(main)/course/page.tsx
@@ -0,0 +1,369 @@
+'use client';
+
+import FormModal from '@/app/components/popUp/FormModal';
+import { addCourse, deleteCourse, fetchCourseInfo, fetchCourses, updateCourse } from '@/services/courses';
+import { getToken } from '@/utils/auth';
+import { Button } from 'primereact/button';
+import { FileUpload, FileUploadSelectEvent } from 'primereact/fileupload';
+import { InputText } from 'primereact/inputtext';
+import { useContext, useEffect, useState } from 'react';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import { InputTextarea } from 'primereact/inputtextarea';
+import { DataTable } from 'primereact/datatable';
+import { Column } from 'primereact/column';
+import GroupSkeleton from '@/app/components/skeleton/GroupSkeleton';
+import Link from 'next/link';
+import { CourseCreateType } from '@/types/courseCreateType';
+import { CourseType } from '@/types/courseType';
+import { Paginator } from 'primereact/paginator';
+// import NotFoundPage from '@/app/(full-page)/pages/notfound/page';
+import { NotFound } from '@/app/components/NotFound';
+import Redacting from '@/app/components/popUp/Redacting';
+import { getRedactor } from '@/utils/getRedactor';
+import { getConfirmOptions } from '@/utils/getConfirmOptions';
+import useErrorMessage from '@/hooks/useErrorMessage';
+
+export default function Course() {
+ const [courses, setCourses] = useState([]);
+ const [hasCourses, setHasCourses] = useState(false);
+ const [courseValue, setCourseValue] = useState({ title: '', description: '', video_url: '', image: '' });
+ const [courseTitle, setCourseTitle] = useState('');
+ const [video_url, setVideo_url] = useState('');
+ const [description, setDesciption] = useState('');
+ const [editMode, setEditMode] = useState(false);
+ const [selectedCourse, setSelectedCourse] = useState(null);
+ const [formVisible, setFormVisible] = useState(false);
+ const [image, setImage] = useState('');
+ const [forStart, setForStart] = useState(false);
+ const [skeleton, setSkeleton] = useState(false);
+ const [pagination, setPagination] = useState({
+ currentPage: 1,
+ total: 0,
+ perPage: 0
+ });
+
+ const { setMessage } = useContext(LayoutContext);
+ const showError = useErrorMessage();
+
+ const handleFetchCourse = async (page = 1) => {
+ const token = getToken('access_token');
+ const data = await fetchCourses(token, page, 3);
+ console.log(data);
+
+ if (data.courses) {
+ setHasCourses(false);
+ setCourses(data.courses.data);
+ setPagination({
+ currentPage: data.courses.current_page,
+ total: data.courses.total,
+ perPage: data.courses.per_page
+ });
+ } else {
+ setHasCourses(true);
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleAddCourse = async () => {
+ if (courseValue.title.length < 1) {
+ alert('hi');
+ return null;
+ }
+ const token = getToken('access_token');
+ const data = await addCourse(token, courseValue);
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchCourse();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү кошулду!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' }
+ }); // messege - Ошибка при добавлении
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleDeleteCourse = async (id: number) => {
+ const token = getToken('access_token');
+
+ const data = await deleteCourse(token, id);
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchCourse();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' }
+ }); // messege - Ошибка при добавлении
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const handleUpdateCourse = async () => {
+ const token = getToken('access_token');
+
+ const data = await updateCourse(token, selectedCourse, courseValue);
+ if (data.success) {
+ toggleSkeleton();
+ handleFetchCourse();
+ clearValues();
+ setEditMode(false);
+ setSelectedCourse(null);
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' }
+ }); // messege - Успех!
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка при при изменении курса', detail: 'Заполняйте поля правильно' }
+ }); // messege - Ошибка при изменении курса
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ const clearValues = () => {
+ setCourseValue({ title: '', description: '', video_url: '', image: '' });
+ setCourseTitle('');
+ setVideo_url('');
+ setDesciption('');
+ setImage('');
+ setEditMode(false);
+ setSelectedCourse(null);
+ };
+
+ const onSelect = (e: FileUploadSelectEvent) => {
+ setImage(e.files[0]); // сохраняешь файл
+ console.log(e.files[0]);
+
+ setCourseValue((prev) => ({
+ ...prev,
+ image: e.files[0]
+ }));
+ };
+
+ const imageBodyTemplate = (product: CourseType) => {
+ const image = product.image;
+
+ if (typeof image === 'string') {
+ return (
+
+

+
+ );
+ }
+
+ return (
+
+

+
+ );
+ };
+
+ const toggleSkeleton = () => {
+ setSkeleton(true);
+ setTimeout(() => {
+ setSkeleton(false);
+ }, 1000);
+ };
+
+ // Ручное управление пагинацией
+ const handlePageChange = (page: number) => {
+ // fetchData(page);
+ handleFetchCourse(page);
+ };
+
+ useEffect(() => {
+ handleFetchCourse();
+ }, []);
+
+ useEffect(() => {
+ const title = courseTitle.trim();
+ if (title.length > 0) {
+ setForStart(false);
+ } else {
+ setForStart(true);
+ }
+ }, [courseTitle]);
+
+ useEffect(() => {
+ console.log('Курсы ', courses);
+ courses.length < 1 ? setHasCourses(true) : setHasCourses(false);
+ }, [courses]);
+
+ useEffect(() => {
+ console.log(image);
+ }, [image]);
+
+ useEffect(() => {
+ const handleShow = async () => {
+ const token = getToken('access_token');
+ const data = await fetchCourseInfo(token, selectedCourse);
+ console.log(data);
+ setCourseTitle(data.course.title || '');
+ setVideo_url(data.course.video_url || '');
+ setDesciption(data.course.description || '');
+ setImage(data.course.image);
+ };
+
+ if (editMode) {
+ handleShow();
+ }
+ }, [editMode]);
+
+ const edit = (rowData: number | null) => {
+ setEditMode(true);
+ setSelectedCourse(rowData);
+ setFormVisible(true);
+ };
+
+ return (
+
+ {/* modal window */}
+
+
+
+
+
+ {
+ setCourseTitle(e.target.value);
+ setCourseValue((prev) => ({
+ ...prev,
+ title: e.target.value
+ }));
+ }}
+ />
+
+
+
+ {
+ setVideo_url(e.target.value);
+ setCourseValue((prev) => ({
+ ...prev,
+ video_url: e.target.value
+ }));
+ }}
+ />
+
+
+
+
+
+
+ {
+ setDesciption(e.target.value);
+ setCourseValue((prev) => ({
+ ...prev,
+ description: e.target.value
+ }));
+ }}
+ />
+
+
+
+
+
+ {image ? (
+
+ {typeof image === 'string' && (
+ <>Сүрөт: {image}>
+ )}
+
+ ) : (
+
jpeg, png, jpg
+ )}
+
+
+
+
+
+ {/* info section */}
+
+
Курстар
+
+ {
+ setEditMode(false);
+ clearValues();
+ setFormVisible(true);
+ }}
+ />
+
+
+ {/* table section */}
+ {hasCourses ? (
+
+ ) : (
+
+ {skeleton ? (
+
+ ) : (
+ <>
+
+ rowIndex + 1} header="Номер" style={{ width: '20px' }}>
+ (
+
+ {rowData.title}
+
+ )}
+ >
+
+ (
+
+
+
+ )}
+ />
+
+
handlePageChange(e.page + 1)}
+ template="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink"
+ />
+ >
+ )}
+
+ )}
+
+ );
+}
diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx
index 5cb39fdb..122640dd 100644
--- a/app/(main)/layout.tsx
+++ b/app/(main)/layout.tsx
@@ -19,7 +19,7 @@ export const metadata: Metadata = {
ttl: 604800
},
icons: {
- icon: '/favicon.ico'
+ // icon: ''
}
};
diff --git a/app/(main)/page.tsx b/app/(main)/teacher/page.tsx
similarity index 99%
rename from app/(main)/page.tsx
rename to app/(main)/teacher/page.tsx
index afe44527..a06710d5 100644
--- a/app/(main)/page.tsx
+++ b/app/(main)/teacher/page.tsx
@@ -6,8 +6,8 @@ import { Column } from 'primereact/column';
import { DataTable } from 'primereact/datatable';
import { Menu } from 'primereact/menu';
import React, { useContext, useEffect, useRef, useState } from 'react';
-import { ProductService } from '../../demo/service/ProductService';
-import { LayoutContext } from '../../layout/context/layoutcontext';
+import { ProductService } from '../../../demo/service/ProductService';
+import { LayoutContext } from '../../../layout/context/layoutcontext';
import Link from 'next/link';
import { Demo } from '@/types';
import { ChartData, ChartOptions } from 'chart.js';
diff --git a/app/(student)/layout.tsx b/app/(student)/layout.tsx
new file mode 100644
index 00000000..0eec769f
--- /dev/null
+++ b/app/(student)/layout.tsx
@@ -0,0 +1,15 @@
+import Layout from "../../layout/layout";
+
+export default function LayoutStudent({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+
+ return (
+
+ {/* {children} */}
+ {children};
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/BaseLayout.tsx b/app/components/BaseLayout.tsx
new file mode 100644
index 00000000..101fd3fb
--- /dev/null
+++ b/app/components/BaseLayout.tsx
@@ -0,0 +1,19 @@
+"use client";
+
+import AppTopbar from "@/layout/AppTopbar";
+import HomeClient from "./HomeClient";
+import AppFooter from "@/layout/AppFooter";
+
+export default function BaseLayout() {
+ return (
+ <>
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/app/components/CKEditorWrapper.tsx.tsx b/app/components/CKEditorWrapper.tsx.tsx
new file mode 100644
index 00000000..2f2156d1
--- /dev/null
+++ b/app/components/CKEditorWrapper.tsx.tsx
@@ -0,0 +1,28 @@
+'use client';
+import useTypingEffect from '@/hooks/useTypingEffect';
+import { Editor } from 'primereact/editor';
+import { useEffect, useState } from 'react';
+import { EditorTextChangeEvent } from 'primereact/editor';
+
+export default function CKEditorWrapper({textValue, insideValue}: {textValue: (e: string | null)=> void, insideValue: string | null}) {
+ const [text, setText] = useState('');
+ const [toggleTyping, setToggleTyping] = useState(true);
+
+ // const typedText = useTypingEffect(
+ // 'Текстти ушул жерге жазыныз',
+ // toggleTyping
+ // );
+
+ useEffect(()=> {
+ textValue(text);
+ },[text]);
+
+ return (
+
+ {insideValue ?
+ setText(e.htmlValue)} className='w-[800px] h-[300px]'/>
+ : setText(e.htmlValue)} className='w-[800px] h-[300px]'/>
+ }
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/CounterBanner.tsx b/app/components/CounterBanner.tsx
new file mode 100644
index 00000000..70317ba5
--- /dev/null
+++ b/app/components/CounterBanner.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { faCircle, faChalkboard, faUserGraduate, faBookOpen, faShieldHeart } from '@fortawesome/free-solid-svg-icons';
+import MyFontAwesome from './MyFontAwesome';
+import CountUp from 'react-countup';
+
+export default function CounterBanner() {
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
Курстар & видеосабактар
+
+
+
+
+
+
+
+
+
+
+
+
Катталган студенттер
+
+
+
+
+
+
+
+
+
+
+
+
%
+
Канааттануу деңгээли
+
+
+
+
+ );
+}
diff --git a/app/components/HomeClient.tsx b/app/components/HomeClient.tsx
new file mode 100644
index 00000000..5ecfb5eb
--- /dev/null
+++ b/app/components/HomeClient.tsx
@@ -0,0 +1,134 @@
+'use client';
+
+import AOS from 'aos';
+import 'aos/dist/aos.css';
+import { useContext, useEffect } from 'react';
+import CounterBanner from './CounterBanner';
+import Link from 'next/link';
+import VideoPlay from './VideoPlay';
+import FancyLinkBtn from './buttons/FancyLinkBtn';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+
+export default function HomeClient() {
+ const { user } = useContext(LayoutContext);
+
+ useEffect(() => {
+ AOS.init();
+ }, []);
+
+ return (
+
+
+
+
+
+
+

+
+ ЫҢГАЙЛУУ ОКУУ ҮЧҮН ОНЛАЙН МЕЙКИНДИК
+
+
+
+ Аралыктан окутуу порталына кош келиңиз!
+
+
+ {' '}
+ Университеттин онлайн билим берүү жаатындагы долбоорлорун бириктирүүдөбүз:
+
+ - ачык онлайн курстар
+ - жогорку билим берүү программалары
+
+ {user &&
+
+
+
+ }
+
+
+
+
+
+
+
+ {/*
+

+
*/}
+
+

+
+
+

+
+
+
+

+
+
+ {/*
*/}
+
+ {/*
+
+
Куттуктайбыз!
+
Сиздин кабыл алуу ийгиликтүү аяктады
+
+
*/}
+
+ {/*
+
+
User experience className
+
Today at 12.00 PM
+
+
+ Join now
+
+
*/}
+
+
+
+
+
+
+ {/* Counter Statistics */}
+
+
+ {/* Oshgu Video */}
+
+
+ Видеоэкскурсия по главному зданию ОшГУ
+
+
+
+
+ );
+}
diff --git a/app/components/InfoBanner.tsx b/app/components/InfoBanner.tsx
new file mode 100644
index 00000000..862aefc0
--- /dev/null
+++ b/app/components/InfoBanner.tsx
@@ -0,0 +1,10 @@
+'use client';
+
+export default function InfoBanner({title}:{title:string}) {
+
+ return (
+
+
{title}
+
+ );
+};
diff --git a/app/components/MyFontAwesome.tsx b/app/components/MyFontAwesome.tsx
new file mode 100644
index 00000000..28e58167
--- /dev/null
+++ b/app/components/MyFontAwesome.tsx
@@ -0,0 +1,17 @@
+"use client"; // обязательно в app/ структуре
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { ComponentProps } from "react";
+
+interface IconProps extends ComponentProps {
+ icon: IconProp;
+ className?: string;
+ size?: "xs" | "lg" | "sm" | "1x" | "2x" | "3x" | "4x" | "5x" | "6x" | "7x" | "8x" | "9x" | "10x";
+}
+
+export default function MyFontAwesome({ icon, className, size, ...props }:IconProps ) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/NotFound.tsx b/app/components/NotFound.tsx
new file mode 100644
index 00000000..f770bd9b
--- /dev/null
+++ b/app/components/NotFound.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import React from 'react';
+import Link from 'next/link';
+import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn';
+
+export const NotFound = ({ titleMessage }: { titleMessage: string }) => {
+ return (
+
+ );
+};
diff --git a/app/components/SessionManager.tsx b/app/components/SessionManager.tsx
new file mode 100644
index 00000000..741cddac
--- /dev/null
+++ b/app/components/SessionManager.tsx
@@ -0,0 +1,98 @@
+'use client';
+
+import { getToken } from '@/utils/auth';
+import { useContext, useEffect } from 'react';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import { getUser } from '@/services/auth';
+import { logout } from '@/utils/logout';
+
+const SessionManager = () => {
+ const { setMessage } = useContext(LayoutContext);
+ const { user, setUser } = useContext(LayoutContext);
+ const { setGlobalLoading } = useContext(LayoutContext);
+
+ useEffect(() => {
+ console.log('Пользователь ', user);
+ }, [user]);
+
+ useEffect(() => {
+ console.log('Роутинг!');
+
+ const init = async () => {
+ const token = getToken('access_token');
+ if (token) {
+ const res = await getUser(token);
+ setGlobalLoading(true);
+ try {
+ if (res?.success) {
+ setGlobalLoading(false);
+ const userVisit = localStorage.getItem('userVisit');
+ console.log('Данные успешно пришли ', res);
+
+ if (!userVisit) {
+ localStorage.setItem('userVisit', JSON.stringify(true));
+
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Успешная авторизация!', detail: '' }
+ }); // messege - Успех!
+ }
+ setUser(res.user);
+ } else {
+ logout({setUser, setGlobalLoading});
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' }
+ }); // messege - Ошибка при авторизации
+ console.log('Ошибка при получении пользователя');
+ }
+ } catch (error) {
+ logout({setUser, setGlobalLoading});
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' }
+ }); // messege - Ошибка при авторизации
+ console.log('Ошибка при получении пользователя');
+ }
+ } else {
+ setGlobalLoading(false);
+ }
+ };
+ init();
+ }, []);
+
+ useEffect(() => {
+ const checkToken = () => {
+ const token = getToken('access_token');
+
+ if (!token) {
+ const userVisit = localStorage.getItem('userVisit');
+ if(userVisit){
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Сессия завершилось', detail: 'Войдите заново' }
+ }); // messege - Время сесси завершилось
+ }
+ logout({setUser, setGlobalLoading});
+ console.log('Токен отсутствует - завершаем сессию');
+ return false; // сигнал для остановки интервала
+ }
+ return true;
+ };
+
+ // немедленная проверка
+ if (!checkToken()) return;
+
+ const interval = setInterval(() => {
+ if (!checkToken()) {
+ clearInterval(interval);
+ }
+ }, 5000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ return null;
+};
+
+export default SessionManager;
diff --git a/app/components/VideoPlay.tsx b/app/components/VideoPlay.tsx
new file mode 100644
index 00000000..fb45968c
--- /dev/null
+++ b/app/components/VideoPlay.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import { faPlay} from "@fortawesome/free-solid-svg-icons";
+
+import Image from "next/image";
+import { useState } from "react";
+import { Dialog } from 'primereact/dialog';
+// import 'primereact/resources/themes/lara-light-blue/theme.css'; // или другая тема
+import 'primereact/resources/primereact.min.css';
+import 'primeicons/primeicons.css';
+import MyFontAwesome from "./MyFontAwesome";
+
+export default function VideoPlay() {
+ const [videoCall, setVideoCall] = useState(false);
+
+ return (
+
+
+
+
+
setVideoCall(true)}
+ >
+ {/* Волна */}
+
+
+ {/* Иконка-кнопка */}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/components/buttons/FancyLinkBtn.tsx b/app/components/buttons/FancyLinkBtn.tsx
new file mode 100644
index 00000000..5781fed4
--- /dev/null
+++ b/app/components/buttons/FancyLinkBtn.tsx
@@ -0,0 +1,34 @@
+'use client';
+
+import Link from 'next/link';
+import { useState } from 'react';
+
+export default function FancyLinkBtn({ btnWidth, backround, effectBg, title }
+ : {btnWidth: string, backround: string, effectBg: string, title: string}
+) {
+ const [position, setPosition] = useState(false);
+
+ return (
+
+ setPosition(true)}
+ onMouseLeave={() => setPosition(false)}
+ className="default-btn p-[7px] md:p-[10px] font-bold"
+ onClick={() => setPosition((prev) => !prev)}
+ style={{
+ backgroundColor: `var(${backround})`,
+ width: `${ btnWidth }`,
+ }}
+ type='submit'
+ >
+ {/* */}
+ {title}
+ {/* */}
+
+
+
+ );
+}
diff --git a/app/components/cards/LessonCard.tsx b/app/components/cards/LessonCard.tsx
new file mode 100644
index 00000000..1ac789d6
--- /dev/null
+++ b/app/components/cards/LessonCard.tsx
@@ -0,0 +1,37 @@
+import Redacting from '../popUp/Redacting';
+import { getRedactor } from '@/utils/getRedactor';
+import { getConfirmOptions } from '@/utils/getConfirmOptions';
+
+export default function LessonCard({ onSelected, onDelete, cardValue, cardBg, typeColor, type, lessonDate }:
+ {
+ onSelected: (id: number, type: string)=> void,
+ onDelete: (id: number) => void,
+ cardValue: {title: string, id: number, type: string},
+ cardBg: string,
+ type: {typeValue: string, icon: string},
+ typeColor: string,
+ lessonDate: string
+ })
+
+ {
+ return (
+
+
+
+
+ {type.typeValue}
+
+
+
+
+
+
{cardValue.title}{cardValue.id}
+
+
+ );
+}
diff --git a/app/components/cards/PrototypeCard.tsx b/app/components/cards/PrototypeCard.tsx
new file mode 100644
index 00000000..994ea944
--- /dev/null
+++ b/app/components/cards/PrototypeCard.tsx
@@ -0,0 +1,21 @@
+import { Skeleton } from 'primereact/skeleton';
+import React from 'react';
+import MySkeleton from '../skeleton/MySkeleton';
+
+export default function PrototypeCard({ value }: { value: string }) {
+
+ return (
+
+
+
+
+
+
+
+
+
+
{value ? value : }
+
+
+ );
+}
diff --git a/app/components/lessons/LessonTyping.tsx b/app/components/lessons/LessonTyping.tsx
new file mode 100644
index 00000000..6b8b821d
--- /dev/null
+++ b/app/components/lessons/LessonTyping.tsx
@@ -0,0 +1,474 @@
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import { Button } from 'primereact/button';
+import { FileUpload } from 'primereact/fileupload';
+import { InputText } from 'primereact/inputtext';
+import React, { useContext, useEffect, useState } from 'react';
+import LessonCard from '../cards/LessonCard';
+import { getToken } from '@/utils/auth';
+import { addLesson, deleteLesson, fetchLesson, updateLesson } from '@/services/courses';
+import useErrorMessage from '@/hooks/useErrorMessage';
+import FormModal from '../popUp/FormModal';
+import { useForm } from 'react-hook-form';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { lessonStateType } from '@/types/lessonStateType';
+import { lessonSchema } from '@/schemas/lessonSchema';
+import { lessonType } from '@/types/lessonType';
+
+export default function LessonTyping({ mainType, courseId, lessonId }: { mainType: string; courseId: string | null; lessonId: string | null }) {
+ // type
+ interface EditableLesson {
+ title: string;
+ description?: string;
+ document?: File | null;
+ url?: string | null;
+ }
+
+ // validate
+ const {
+ register,
+ setValue,
+ formState: { errors }
+ } = useForm({
+ resolver: yupResolver(lessonSchema),
+ mode: 'onChange'
+ });
+
+ // doc
+ const [documents, setDocuments] = useState([]);
+ const [docValue, setDocValue] = useState({
+ title: '',
+ description: '',
+ file: null,
+ url: ''
+ });
+ const [docShow, setDocShow] = useState(false);
+
+ // links
+ const [links, setLinks] = useState([]);
+ const [linksValue, setLinksValue] = useState({
+ title: '',
+ description: '',
+ file: null,
+ url: ''
+ });
+ const [linksShow, setLinksShow] = useState(false);
+
+ // auxiliary
+ const showError = useErrorMessage();
+ const { setMessage } = useContext(LayoutContext);
+ const [selectId, setSelectId] = useState(null);
+ const [selectType, setSelectType] = useState('');
+ const [visible, setVisisble] = useState(false);
+ const [editingLesson, setEditingLesson] = useState(null);
+
+ const handleUpdate = () => {
+ console.log(selectType);
+ if(selectType === 'doc'){
+ handleUpdateLesson();
+ } else if(selectType === 'link'){
+ handleUpdateLink();
+ }
+ }
+
+ const selectedForModal = (id: number, type: string) => {
+ setSelectId(id);
+ setSelectType(type);
+ //
+ editing(type, id);
+ setVisisble(true);
+ };
+
+ const editing = async (type: string, selected: number) => {
+ console.log(type);
+
+ const token = getToken('access_token');
+ const data = await fetchLesson(type, token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null);
+
+ interface test {
+ key: string;
+ file: string;
+ url: string;
+ }
+ const lesson: test = type === 'doc' ? { key: 'documents', file: 'document', url: '' }
+ : type === 'link' ? { key: 'url', file: 'url', url: '' } : {key: '', file: '', url: ''};
+
+ console.log(lesson.key, data[lesson.key][0]);
+
+ if (data.success) {
+ if (data[lesson.key]) {
+ const arr = data[lesson.key][0].id === selected ? data[lesson.key][0] : {};
+ setEditingLesson({
+ title: arr.title,
+ description: arr?.description,
+ document: arr[lesson.file],
+ url: arr[lesson.url]
+ });
+ }
+ }
+ };
+
+ const cencalEdit = () => {
+ setDocValue({ title: '', description: '', file: null, url: ''});
+ };
+
+ // DOC SECTION
+
+ const docSection = () => {
+ return (
+
+
+
{}}
+ accept="application/pdf"
+ onSelect={(e) =>
+ setDocValue((prev) => ({
+ ...prev,
+ file: e.files[0]
+ }))
+ }
+ />
+
+ {
+ setDocValue((prev) => ({ ...prev, title: e.target.value }));
+ setValue('title', e.target.value, { shouldValidate: true });
+ }}
+ />
+ {errors.title?.message}
+
+ setDocValue((prev) => ({ ...prev, description: e.target.value }))} className="w-full" />
+
+ {/* */}
+
+
+
+
+
+
+ {docShow
+ ? documents.map((item: lessonType) => (
+
+ selectedForModal(id, type)}
+ onDelete={(id: number) => handleDeleteDoc(id)}
+ cardValue={{ title: item?.title, id: item?.id, type: 'doc' }}
+ cardBg={'#ddc4f51a'}
+ type={{ typeValue: 'Документтер', icon: 'pi pi-folder' }}
+ typeColor={'var(--mainColor)'}
+ lessonDate={'xx-xx-xx'}
+ />
+
+ ))
+ : 'Шаблон если данных нет'}
+
+
+
+ );
+ };
+
+ // fetch document
+ const handleFetchDoc = async () => {
+ // skeleton = false
+ const token = getToken('access_token');
+ const data = await fetchLesson('doc', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null);
+ console.log(data);
+
+ if (data.success) {
+ if (data.documents) {
+ // Присваиваю себе. Для отображения
+ setDocValue({ title: '', description: '', file: null, url: '' });
+ setDocuments(data.documents);
+ setDocShow(true);
+ } else {
+ setDocShow(false);
+ alert('content null!!');
+ }
+ } else {
+ setDocShow(false);
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ // skeleton = false
+ }
+ };
+
+ // add document
+ const handleAddDoc = async () => {
+ const token = getToken('access_token');
+
+ const data = await addLesson('doc', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, docValue, docValue.title);
+ console.log(data);
+
+ if (data.success) {
+ handleFetchDoc();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү Кошулдуу!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ // update document
+ const handleUpdateLesson = async () => {
+ const token = getToken('access_token');
+
+ const data = await updateLesson('doc', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, Number(selectId), docValue);
+ console.log(data);
+
+ if (data.success) {
+ handleFetchDoc();
+ setSelectId(null);
+ setSelectType('');
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' }
+ });
+ } else {
+ setDocValue({ title: '', description: '', file: null, url: '' });
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при изменении урока' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ // delete document
+ const handleDeleteDoc = async (id: number) => {
+ console.log(id);
+
+ const token = getToken('access_token');
+
+ const data = await deleteLesson('doc', token, Number(courseId), Number(lessonId), id);
+ if (data.success) {
+ handleFetchDoc();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ console.log(data);
+ };
+
+ // LINKS SECTION
+
+ const linkSection = () => {
+ return (
+
+
+
{
+ setLinksValue((prev) => ({ ...prev, url: e.target.value }));
+ setValue('usefulLink', e.target.value, { shouldValidate: true });
+ }}
+ />
+ {errors.usefulLink?.message}
+
+ {
+ setLinksValue((prev) => ({ ...prev, title: e.target.value }));
+ setValue('title', e.target.value, { shouldValidate: true });
+ }}
+ />
+ {errors.title?.message}
+
+ setLinksValue((prev) => ({ ...prev, description: e.target.value }))} className="w-full" />
+
+
+
+
+
+
+
+ {/* {linksShow
+ ? links.map((item) => (
+
*/}
+ selectedForModal(id, type)}
+ onDelete={(id: number) => handleDeleteLink(id)}
+ cardValue={{ title: 'lorem', id: 9, type: 'link' }}
+ cardBg={'#7bb78112'}
+ type={{ typeValue: 'Шилтемелер', icon: 'pi pi-link' }}
+ typeColor={'var(--mainColor)'}
+ lessonDate={'xx-xx-xx'}
+ />
+ {/*
+ ))
+ : 'Шаблон если данных нет'} */}
+
+
+
+ );
+ };
+
+ // add link
+ const handleAddLink = async () => {
+ const token = getToken('access_token');
+
+ const data = await addLesson('url', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, linksValue, linksValue);
+ console.log(data);
+
+ if (data.success) {
+ // handleFetchDoc();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү Кошулдуу!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ // delete link
+ const handleDeleteLink = async (id: number) => {
+ console.log(id);
+
+ const token = getToken('access_token');
+
+ const data = await deleteLesson('doc', token, Number(courseId), Number(lessonId), id);
+ if (data.success) {
+ handleFetchDoc();
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' }
+ });
+ } else {
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ console.log(data);
+ };
+
+ // update document
+ const handleUpdateLink = async () => {
+ const token = getToken('access_token');
+
+ const data = await updateLesson('url', token, courseId ? Number(courseId) : null, lessonId ? Number(lessonId) : null, Number(selectId), linksValue);
+ console.log(data);
+
+ if (data.success) {
+ // handleFetchDoc();
+ setSelectId(null);
+ setSelectType('');
+ setMessage({
+ state: true,
+ value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' }
+ });
+ } else {
+ setDocValue({ title: '', description: '', file: null, url: '' });
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при изменении урока' }
+ });
+ if (data.response.status) {
+ showError(data.response.status);
+ }
+ }
+ };
+
+ // USEEFFECTS
+
+ useEffect(() => {
+ if (mainType === 'doc') handleFetchDoc();
+ // if (mainType === 'link') handleFetchDoc();
+ }, []);
+
+ useEffect(()=>{
+ console.log(documents);
+ },[documents])
+
+ useEffect(() => {
+ console.log('selected id ', selectId);
+ }, [selectId]);
+
+ return (
+
+
+
+
+ {selectType === 'doc' && (
+ <>
+ {}}
+ accept="application/pdf"
+ onSelect={(e) =>
+ setDocValue((prev) => ({
+ ...prev,
+ file: e.files[0]
+ }))
+ }
+ />
+ {editingLesson?.document?.name}
+ {
+ setDocValue((prev) => ({ ...prev, title: e.target.value }));
+ setValue('title', e.target.value, { shouldValidate: true });
+ // setEditingLesson((prev)=> ({...prev, title: e.target.value}));
+ }}
+ />
+ {errors.title?.message}
+
+ setDocValue((prev) => ({ ...prev, description: e.target.value }))} className="w-full" />
+ >
+ )}
+
+
+
+
+ {mainType === 'doc' && docSection()}
+ {mainType === 'link' && linkSection()}
+
+ );
+}
diff --git a/app/components/loading/GlobalLoading.tsx b/app/components/loading/GlobalLoading.tsx
new file mode 100644
index 00000000..65be1f01
--- /dev/null
+++ b/app/components/loading/GlobalLoading.tsx
@@ -0,0 +1,27 @@
+import React, { useContext } from 'react';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+
+export default function GlobalLoading() {
+ const { globalLoading } = useContext(LayoutContext);
+
+ if(globalLoading){
+ return (
+ <>
+
+ >
+ );
+ }
+}
diff --git a/app/components/messages/Message.tsx b/app/components/messages/Message.tsx
new file mode 100644
index 00000000..9224acf4
--- /dev/null
+++ b/app/components/messages/Message.tsx
@@ -0,0 +1,29 @@
+'use client';
+import { Button } from 'primereact/button';
+import { Toast } from 'primereact/toast';
+import { useContext, useEffect, useRef } from 'react';
+import { LayoutContext } from '@/layout/context/layoutcontext';
+
+export default function Message() {
+ const { message } = useContext(LayoutContext);
+ const toast = useRef(null);
+
+ const showError = () => {
+ toast.current?.show({ severity: message.value.severity, summary: message.value.summary, detail: message.value.detail, life: 3000 });
+ };
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ showError();
+ }, 0); // откладываем до следующей итерации event loop
+
+ return () => clearTimeout(timer);
+ }, [message]);
+
+ return (
+
+ {/* */}
+
+
+ );
+}
diff --git a/app/components/popUp/ConfirmModal.tsx b/app/components/popUp/ConfirmModal.tsx
new file mode 100644
index 00000000..668509ff
--- /dev/null
+++ b/app/components/popUp/ConfirmModal.tsx
@@ -0,0 +1,17 @@
+'use client';
+import { confirmDialog } from 'primereact/confirmdialog';
+import { Button } from 'primereact/button';
+
+export default function ConfirmModal({confirmVisible}) {
+ const handleClick = () => {
+ console.log('hi');
+
+ confirmDialog(confirmVisible);
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/app/components/popUp/FormModal.tsx b/app/components/popUp/FormModal.tsx
new file mode 100644
index 00000000..b9c29d70
--- /dev/null
+++ b/app/components/popUp/FormModal.tsx
@@ -0,0 +1,44 @@
+'use client';
+
+import { Button } from 'primereact/button';
+import { Dialog } from 'primereact/dialog';
+import { useEffect } from 'react';
+
+export default function FormModal({children, title, fetchValue, clearValues, visible, setVisible, start}) {
+
+ const footerContent = (
+
+ {
+ setVisible(false);
+ clearValues();
+ }}/>
+ { {
+ setVisible(false);
+ fetchValue();
+ }} autoFocus />}
+
+ );
+
+ useEffect(()=> {
+ console.log('visible', visible);
+
+ },[visible]);
+
+ return (
+
+
+
+ );
+}
diff --git a/app/components/popUp/Redacting.tsx b/app/components/popUp/Redacting.tsx
new file mode 100644
index 00000000..b42664ec
--- /dev/null
+++ b/app/components/popUp/Redacting.tsx
@@ -0,0 +1,40 @@
+import { Menu } from 'primereact/menu';
+import type { Menu as MenuRef } from 'primereact/menu';
+import { MenuItem } from 'primereact/menuitem';
+import { useRef } from 'react';
+
+export default function Redacting({ redactor, textSize }: {redactor: MenuItem[], textSize: string}) {
+ const menuLeft = useRef(null);
+
+ return (
+
+ menuLeft.current?.toggle(event)} aria-controls="popup_menu_left" aria-haspopup />
+
+
+ );
+}
diff --git a/app/components/popUp/Tiered.tsx b/app/components/popUp/Tiered.tsx
new file mode 100644
index 00000000..459debdd
--- /dev/null
+++ b/app/components/popUp/Tiered.tsx
@@ -0,0 +1,57 @@
+'use client';
+
+import { useEffect, useRef, useState } from 'react';
+import { Button } from 'primereact/button';
+import { TieredMenu } from 'primereact/tieredmenu';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
+
+export default function Tiered({title, items, insideColor}) {
+ const [mobile, setMobile] = useState(false);
+
+ const menu = useRef(null);
+ const media = useMediaQuery('(max-width: 1000px)');
+
+ const menuItems = items.map(item => ({
+ ...item,
+ url: item.link && item.link
+ }));
+
+ const toggleMenu = (e)=> {
+ menu.current.toggle(e);
+ setMobile(prev => !prev);
+ }
+ // Общий фонт который закрывает кнопку бургер меню, Close
+ return (
+
+ toggleMenu(e)}
+ style={{color: 'black', fontSize: title.name ? '16px' : '20px'}}
+ className={`${ title.font } ${
+ title.name ?
+ 'flex gap-2 items-center pb-3 px-0'
+ : 'text-blue-300'
+ } bg-white p-2`}
+ />
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/components/skeleton/GroupSkeleton.tsx b/app/components/skeleton/GroupSkeleton.tsx
new file mode 100644
index 00000000..7a79e32c
--- /dev/null
+++ b/app/components/skeleton/GroupSkeleton.tsx
@@ -0,0 +1,14 @@
+import React, { useState } from 'react';
+import { Skeleton } from 'primereact/skeleton';
+
+export default function GroupSkeleton({ count, size }:{count:number, size:{width:string, height: string}}) {
+ const usingSkeleton = (i:number) => {
+ return (
+
+
+
+ );
+ };
+
+ return {Array.from({ length: count }).map((_, i) => usingSkeleton(i))}
;
+}
diff --git a/app/components/skeleton/MySkeleton.tsx b/app/components/skeleton/MySkeleton.tsx
new file mode 100644
index 00000000..751a781a
--- /dev/null
+++ b/app/components/skeleton/MySkeleton.tsx
@@ -0,0 +1,6 @@
+import React, { useState } from 'react';
+import { Skeleton } from 'primereact/skeleton';
+
+export default function MySkeleton({ size }:{size:{width:string,height:string}}) {
+ return ;
+}
diff --git a/app/favicon.ico b/app/favicon.ico
deleted file mode 100644
index a614a31e..00000000
Binary files a/app/favicon.ico and /dev/null differ
diff --git a/app/globals.css b/app/globals.css
new file mode 100644
index 00000000..ab87c767
--- /dev/null
+++ b/app/globals.css
@@ -0,0 +1,382 @@
+@import 'primeflex/primeflex.css';
+@import 'tailwindcss/index.css';
+
+:root {
+ --bodyFonts: "Jost", sans-serif;
+ --mainColor: #08A9E6;
+ --redColor: #EC272F;
+ --redBgColor: #EC272F1A;
+ --titleColor: #21225F;
+ --bodyColor: #555555;
+ --whiteColor: #ffffff;
+ --borderBottomColor: #e5e7eb;
+ --fontSize: 16px;
+ --transition: 0.5s;
+}
+
+p {
+ color: var(--bodyColor);
+ margin-bottom: 10px;
+}
+p:last-child {
+ margin-bottom: 0;
+}
+
+a {
+ display: inline-block;
+ transition: var(--transition);
+ text-decoration: none;
+}
+a:hover, a:focus {
+ text-decoration: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: var(--titleColor);
+}
+
+.mainColor-hover:hover{
+ color: #08A9E6;
+}
+
+/* animate */
+.animateContent{
+ animation: content linear 6s infinite;
+}
+
+@keyframes content {
+ 10%{
+ transform: translateX(2px);
+ }
+ 50%{
+ transform: translateX(-4px);
+ }
+ 80%{
+ transform: translateY(-2px);
+ }
+ 100%{
+ transform: translateY(4px);
+ }
+}
+
+.animateFaster{
+ animation: faster linear 4s infinite forwards;
+}
+
+@keyframes faster {
+ 10%{
+ transform: translateX(-2px);
+ }
+ 50%{
+ transform: translateX(4px);
+ }
+ 80%{
+ transform: translateY(2px);
+ }
+ 100%{
+ transform: translateY(-4px);
+ }
+}
+
+.user-img {
+ position: relative;
+ margin-bottom: 30px;
+}
+.user-img img {
+ /* border: solid .5px ; */
+ box-shadow: 0px 1px 9px 4px;
+ animation: border-transform 10s linear infinite alternate forwards;
+}
+
+@keyframes border-transform {
+ 0%, 100% {
+ border-radius: 60% 40% 56% 33%/73% 82% 18% 27%;
+ }
+ 14% {
+ border-radius: 40% 60% 54% 46%/49% 60% 40% 51%;
+ }
+ 28% {
+ border-radius: 54% 46% 38% 62%/49% 70% 30% 51%;
+ }
+ 42% {
+ border-radius: 61% 39% 55% 45%/61% 38% 62% 39%;
+ }
+ 56% {
+ border-radius: 61% 39% 67% 33%/70% 50% 50% 30%;
+ }
+ 70% {
+ border-radius: 50% 50% 34% 66%/56% 68% 32% 44%;
+ }
+ 84% {
+ border-radius: 46% 54% 50% 50%/35% 61% 39% 65%;
+ }
+}
+
+#preloader {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 9999;
+}
+
+#preloader-area {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ height: 60px;
+ width: 60px;
+}
+#preloader-area .spinner {
+ position: absolute;
+ z-index: 9999;
+ width: 6px;
+ height: 90px;
+ margin-top: -45px;
+ border-radius: 10px;
+ background-color: var(--whiteColor);
+ animation: rotate40deg 0.8s infinite;
+ animation-direction: alternate-reverse;
+}
+#preloader-area .spinner:nth-child(1) {
+ margin-left: 0px;
+}
+#preloader-area .spinner:nth-child(2) {
+ margin-left: -14px;
+ animation-delay: 0.1s;
+}
+#preloader-area .spinner:nth-child(3) {
+ margin-left: -28px;
+ animation-delay: 0.2s;
+}
+#preloader-area .spinner:nth-child(4) {
+ margin-left: -42px;
+ animation-delay: 0.3s;
+}
+#preloader-area .spinner:nth-child(5) {
+ margin-left: -56px;
+ animation-delay: 0.4s;
+}
+#preloader-area .spinner:nth-child(6) {
+ margin-left: -70px;
+ animation-delay: 0.5s;
+}
+#preloader-area .spinner:nth-child(7) {
+ margin-left: -84px;
+ animation-delay: 0.6s;
+}
+#preloader-area .spinner:nth-child(8) {
+ margin-left: -98px;
+ animation-delay: 0.7s;
+}
+
+#preloader .preloader-section {
+ position: fixed;
+ top: 0;
+ width: 51%;
+ height: 100%;
+ background-color: var(--mainColor);
+ z-index: 999;
+ transform: translateX(0);
+}
+#preloader .preloader-section.preloader-left {
+ left: 0;
+}
+#preloader .preloader-section.preloader-right {
+ right: 0;
+}
+
+.p-password .p-password-toggle-icon {
+ top: 50% !important;
+ transform: translateY(-50%);
+}
+
+
+.loaded #preloader-area {
+ opacity: 0;
+ visibility: hidden;
+ transition: all 0.3s ease-out;
+}
+.loaded #preloader .preloader-section.preloader-left {
+ transform: translateX(-100%);
+ transition: all 0.8s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+.loaded #preloader .preloader-section.preloader-right {
+ transform: translateX(100%);
+ transition: all 0.8s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+.loaded #preloader {
+ visibility: hidden;
+ transform: translateY(-100%);
+ transition: all 0.3s 1s ease-out;
+}
+
+@keyframes rotate40deg {
+ 0% {
+ height: 5px;
+ margin-top: 0;
+ transform: rotate(40deg);
+ }
+ 100% {
+ height: 90px;
+ transform: rotate(0deg);
+ }
+}
+@keyframes ripple {
+ 0% {
+ transform: scale(1);
+ }
+ 75% {
+ transform: scale(1.5);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(1.75);
+ opacity: 0;
+ }
+}
+@keyframes moveleftbounce {
+ 0% {
+ transform: translateX(0px);
+ }
+ 50% {
+ transform: translateX(20px);
+ }
+ 100% {
+ transform: translateX(0px);
+ }
+}
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+@keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ 100% {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+@keyframes movebounce {
+ 0% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(15px);
+ }
+ 100% {
+ transform: translateY(0);
+ }
+}
+@keyframes rotate-in {
+ 0% {
+ transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+ }
+ 50% {
+ transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+ }
+ 100% {
+ transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+ }
+}
+@keyframes rotated360 {
+ 0% {
+ transform: rotateZ(0deg);
+ }
+ 100% {
+ transform: rotateZ(-360deg);
+ }
+}
+
+/* primeReact components */
+.popup_menu_left{
+ width: 300px;
+ display: none;
+}
+
+.p-menuitem-icon{
+ margin: 0;
+}
+
+/* prototype effect */
+.shadowAnimate {
+ box-shadow: 1px 1px 6px 1px ;
+ transition: all 2s;
+ animation: shadowSkale 2s ease-in infinite;
+}
+
+@keyframes shadowSkale {
+ 0% {
+ box-shadow: 1px 1px 6px 1px ;
+ }
+ 50% {
+ box-shadow: 1px 1px 6px 2px ;
+ }
+ 100%{
+ box-shadow: 1px 1px 6px 1px ;
+ }
+}
+
+.my-custom-table td,
+.my-custom-table th {
+ /* border: 1px solid #ccc; */
+ padding: 6px;
+}
+
+.my-custom-table thead {
+ /* background-color: #f5f5f5; */
+}
+
+.my-custom-table .p-datatable-tbody > tr:nth-child(even) {
+ /* background-color: rgb(255, 239, 239); */
+ background-color: #f5f5f5;
+}
+
+/* удаление встроенного underline при активаности для таб (TabView) */
+.p-tabview-ink-bar {
+ display: none !important;
+}
+
+.p-tabview-panels{
+ /* height: ; */
+}
+
+.tab-custom-text{
+ color: rgb(245, 206, 77) !important;
+}
+
+.tab-custom-text-2{
+ color: blueviolet !important;
+ /* color: rgb(221, 196, 245) !important; */
+}
+
+.tab-custom-text-3{
+ color: green !important;
+}
+
+.tab-custom-text-4{
+ color: var(--redColor) !important;
+}
+
+.poka{
+ background-color: rgb(211, 232, 202) !important;
+ /* background-color: rgb(243, 145, 145) !important; */
+}
+
+.main-bg {
+ background-color: #fff;
+ border-radius: 12px;
+ padding: 4px 12px;
+ box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.02), 0px 0px 2px rgba(0, 0, 0, 0.05), 0px 1px 4px rgba(0, 0, 0, 0.08);
+}
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
index 4114afcf..1d243994 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,12 +1,16 @@
'use client';
import { LayoutProvider } from '../layout/context/layoutcontext';
import { PrimeReactProvider } from 'primereact/api';
+import { config } from "@fortawesome/fontawesome-svg-core";
+import "@fortawesome/fontawesome-svg-core/styles.css"; // Импорт стилей
+config.autoAddCss = false;
import 'primereact/resources/primereact.css';
-import 'primeflex/primeflex.css';
import 'primeicons/primeicons.css';
import '../styles/layout/layout.scss';
import '../styles/demo/Demos.scss';
+import './globals.css';
+
interface RootLayoutProps {
children: React.ReactNode;
}
diff --git a/app/page.tsx b/app/page.tsx
new file mode 100644
index 00000000..ddd8c663
--- /dev/null
+++ b/app/page.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import BaseLayout from './components/BaseLayout'
+
+export default function page() {
+ return (
+
+
+
+ )
+}
diff --git a/hooks/useErrorMessage.tsx b/hooks/useErrorMessage.tsx
new file mode 100644
index 00000000..e8a8b3ea
--- /dev/null
+++ b/hooks/useErrorMessage.tsx
@@ -0,0 +1,63 @@
+'use client';
+
+import { LayoutContext } from '@/layout/context/layoutcontext';
+import { useContext } from 'react';
+
+export default function useErrorMessage() {
+ const { setMessage } = useContext(LayoutContext);
+
+ return (code: number | null) => {
+ console.log('Код ошибки ', code);
+
+ switch (code) {
+ case 400:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Запрос не корректен' }
+ });
+ break;
+
+ case 403:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Нет доступа' }
+ });
+ break;
+
+ case 409:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Конфликт данных' }
+ });
+ break;
+
+ case 422:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка валидации. Проверьте введенные данные' }
+ });
+ break;
+
+ case 500:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка сервера. Попробуйте позже' }
+ });
+ break;
+
+ case 503:
+ setMessage({
+ state: true,
+ value: { severity: 'error', summary: 'Ошибка', detail: 'Сервер временно недоступен' }
+ });
+ break;
+
+ // default:
+ // setMessage({
+ // state: true,
+ // value: { severity: 'error', summary: 'Ошибка', detail: 'Попробуйте позже' }
+ // });
+ }
+
+ };
+}
diff --git a/hooks/useMediaQuery.tsx b/hooks/useMediaQuery.tsx
new file mode 100644
index 00000000..89e61dca
--- /dev/null
+++ b/hooks/useMediaQuery.tsx
@@ -0,0 +1,19 @@
+"use client";
+
+import { useState, useEffect } from "react";
+
+export function useMediaQuery(query:string) {
+ const [matches, setMatches] = useState(false);
+
+ useEffect(() => {
+ const mediaQuery = window.matchMedia(query);
+ setMatches(mediaQuery.matches);
+
+ const handler = (e:MediaQueryListEvent) => setMatches(e.matches);
+ mediaQuery.addEventListener("change", handler);
+
+ return () => mediaQuery.removeEventListener("change", handler);
+ }, [query]);
+
+ return matches;
+}
diff --git a/hooks/useTypingEffect.tsx b/hooks/useTypingEffect.tsx
new file mode 100644
index 00000000..a5dac639
--- /dev/null
+++ b/hooks/useTypingEffect.tsx
@@ -0,0 +1,46 @@
+'use client';
+
+import { useEffect, useRef, useState } from 'react';
+
+export default function useTypingEffect(word: string, stop: boolean) {
+ const [typedText, setTypedText] = useState('');
+ const stopRef = useRef(stop);
+ const indexRef = useRef(0);
+ const intervalRef = useRef(null);
+
+ useEffect(() => {
+ stopRef.current = stop;
+
+ if (!stop) {
+ if (intervalRef.current) clearInterval(intervalRef.current);
+ setTypedText('');
+ indexRef.current = 0;
+ return;
+ }
+
+ intervalRef.current = setInterval(() => {
+ if (indexRef.current >= word.length) {
+ clearInterval(intervalRef.current!);
+ return;
+ }
+
+ setTypedText((prev) => {
+ const nextChar = word[indexRef.current];
+ indexRef.current++;
+ const nextText = prev + nextChar;
+
+ if (nextText.length > 50) {
+ clearInterval(intervalRef.current!);
+ alert('baby');
+ return '';
+ }
+
+ return nextText;
+ });
+ }, 60);
+
+ return () => clearInterval(intervalRef.current!);
+ }, [stop, word]);
+
+ return typedText
+}
\ No newline at end of file
diff --git a/layout/AppFooter.tsx b/layout/AppFooter.tsx
index 424b9987..9e41bccd 100644
--- a/layout/AppFooter.tsx
+++ b/layout/AppFooter.tsx
@@ -2,16 +2,62 @@
import React, { useContext } from 'react';
import { LayoutContext } from './context/layoutcontext';
+import Image from 'next/image';
+import { faChalkboard, faPhone } from '@fortawesome/free-solid-svg-icons';
+import MyFontAwesome from '@/app/components/MyFontAwesome';
+import Link from 'next/link';
const AppFooter = () => {
const { layoutConfig } = useContext(LayoutContext);
+ // dark mode
+ //
+
return (
-
-

- by
-
PrimeReact
-
+
);
};
diff --git a/layout/AppMenu.tsx b/layout/AppMenu.tsx
index b581ecf2..14f85869 100644
--- a/layout/AppMenu.tsx
+++ b/layout/AppMenu.tsx
@@ -4,7 +4,6 @@ import React, { useContext } from 'react';
import AppMenuitem from './AppMenuitem';
import { LayoutContext } from './context/layoutcontext';
import { MenuProvider } from './context/menucontext';
-import Link from 'next/link';
import { AppMenuItem } from '@/types';
const AppMenu = () => {
@@ -12,163 +11,31 @@ const AppMenu = () => {
const model: AppMenuItem[] = [
{
- label: 'Home',
- items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/' }]
+ label: 'Баракчалар',
+ items: [{ label: 'Башкы баракча', icon: 'pi pi-fw pi-home', to: '/' }]
},
{
- label: 'UI Components',
+ label: '',
items: [
- { label: 'Form Layout', icon: 'pi pi-fw pi-id-card', to: '/uikit/formlayout' },
- { label: 'Input', icon: 'pi pi-fw pi-check-square', to: '/uikit/input' },
- { label: 'Float Label', icon: 'pi pi-fw pi-bookmark', to: '/uikit/floatlabel' },
- { label: 'Invalid State', icon: 'pi pi-fw pi-exclamation-circle', to: '/uikit/invalidstate' },
- { label: 'Button', icon: 'pi pi-fw pi-mobile', to: '/uikit/button', class: 'rotated-icon' },
- { label: 'Table', icon: 'pi pi-fw pi-table', to: '/uikit/table' },
- { label: 'List', icon: 'pi pi-fw pi-list', to: '/uikit/list' },
- { label: 'Tree', icon: 'pi pi-fw pi-share-alt', to: '/uikit/tree' },
- { label: 'Panel', icon: 'pi pi-fw pi-tablet', to: '/uikit/panel' },
- { label: 'Overlay', icon: 'pi pi-fw pi-clone', to: '/uikit/overlay' },
- { label: 'Media', icon: 'pi pi-fw pi-image', to: '/uikit/media' },
- { label: 'Menu', icon: 'pi pi-fw pi-bars', to: '/uikit/menu', preventExact: true },
- { label: 'Message', icon: 'pi pi-fw pi-comment', to: '/uikit/message' },
- { label: 'File', icon: 'pi pi-fw pi-file', to: '/uikit/file' },
- { label: 'Chart', icon: 'pi pi-fw pi-chart-bar', to: '/uikit/charts' },
- { label: 'Misc', icon: 'pi pi-fw pi-circle', to: '/uikit/misc' }
+ { label: 'Dashboard', icon: 'pi pi-fw pi-id-card', to: '/' },
+ { label: 'Курстар', icon: 'pi pi-fw pi-id-card', to: '/course' },
+ // { label: 'Input', icon: 'pi pi-fw pi-check-square', to: '/uikit/input' },
+ // { label: 'Float Label', icon: 'pi pi-fw pi-bookmark', to: '/uikit/floatlabel' },
+ // { label: 'Invalid State', icon: 'pi pi-fw pi-exclamation-circle', to: '/uikit/invalidstate' },
+ // { label: 'Button', icon: 'pi pi-fw pi-mobile', to: '/uikit/button', class: 'rotated-icon' },
+ // { label: 'Table', icon: 'pi pi-fw pi-table', to: '/uikit/table' },
+ // { label: 'List', icon: 'pi pi-fw pi-list', to: '/uikit/list' },
+ // { label: 'Tree', icon: 'pi pi-fw pi-share-alt', to: '/uikit/tree' },
+ // { label: 'Panel', icon: 'pi pi-fw pi-tablet', to: '/uikit/panel' },
+ // { label: 'Overlay', icon: 'pi pi-fw pi-clone', to: '/uikit/overlay' },
+ // { label: 'Media', icon: 'pi pi-fw pi-image', to: '/uikit/media' },
+ // { label: 'Menu', icon: 'pi pi-fw pi-bars', to: '/uikit/menu', preventExact: true },
+ // { label: 'Message', icon: 'pi pi-fw pi-comment', to: '/uikit/message' },
+ // { label: 'File', icon: 'pi pi-fw pi-file', to: '/uikit/file' },
+ // { label: 'Chart', icon: 'pi pi-fw pi-chart-bar', to: '/uikit/charts' },
+ // { label: 'Misc', icon: 'pi pi-fw pi-circle', to: '/uikit/misc' }
]
},
- {
- label: 'Prime Blocks',
- items: [
- { label: 'Free Blocks', icon: 'pi pi-fw pi-eye', to: '/blocks', badge: 'NEW' },
- { label: 'All Blocks', icon: 'pi pi-fw pi-globe', url: 'https://blocks.primereact.org', target: '_blank' }
- ]
- },
- {
- label: 'Utilities',
- items: [
- { label: 'PrimeIcons', icon: 'pi pi-fw pi-prime', to: '/utilities/icons' },
- { label: 'PrimeFlex', icon: 'pi pi-fw pi-desktop', url: 'https://primeflex.org/', target: '_blank' }
- ]
- },
- {
- label: 'Pages',
- icon: 'pi pi-fw pi-briefcase',
- to: '/pages',
- items: [
- {
- label: 'Landing',
- icon: 'pi pi-fw pi-globe',
- to: '/landing'
- },
- {
- label: 'Auth',
- icon: 'pi pi-fw pi-user',
- items: [
- {
- label: 'Login',
- icon: 'pi pi-fw pi-sign-in',
- to: '/auth/login'
- },
- {
- label: 'Error',
- icon: 'pi pi-fw pi-times-circle',
- to: '/auth/error'
- },
- {
- label: 'Access Denied',
- icon: 'pi pi-fw pi-lock',
- to: '/auth/access'
- }
- ]
- },
- {
- label: 'Crud',
- icon: 'pi pi-fw pi-pencil',
- to: '/pages/crud'
- },
- {
- label: 'Timeline',
- icon: 'pi pi-fw pi-calendar',
- to: '/pages/timeline'
- },
- {
- label: 'Not Found',
- icon: 'pi pi-fw pi-exclamation-circle',
- to: '/pages/notfound'
- },
- {
- label: 'Empty',
- icon: 'pi pi-fw pi-circle-off',
- to: '/pages/empty'
- }
- ]
- },
- {
- label: 'Hierarchy',
- items: [
- {
- label: 'Submenu 1',
- icon: 'pi pi-fw pi-bookmark',
- items: [
- {
- label: 'Submenu 1.1',
- icon: 'pi pi-fw pi-bookmark',
- items: [
- { label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' },
- { label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' },
- { label: 'Submenu 1.1.3', icon: 'pi pi-fw pi-bookmark' }
- ]
- },
- {
- label: 'Submenu 1.2',
- icon: 'pi pi-fw pi-bookmark',
- items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }]
- }
- ]
- },
- {
- label: 'Submenu 2',
- icon: 'pi pi-fw pi-bookmark',
- items: [
- {
- label: 'Submenu 2.1',
- icon: 'pi pi-fw pi-bookmark',
- items: [
- { label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' },
- { label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' }
- ]
- },
- {
- label: 'Submenu 2.2',
- icon: 'pi pi-fw pi-bookmark',
- items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }]
- }
- ]
- }
- ]
- },
- {
- label: 'Get Started',
- items: [
- {
- label: 'Documentation',
- icon: 'pi pi-fw pi-question',
- to: '/documentation'
- },
- {
- label: 'Figma',
- url: 'https://www.dropbox.com/scl/fi/bhfwymnk8wu0g5530ceas/sakai-2023.fig?rlkey=u0c8n6xgn44db9t4zkd1brr3l&dl=0',
- icon: 'pi pi-fw pi-pencil',
- target: '_blank'
- },
- {
- label: 'View Source',
- icon: 'pi pi-fw pi-search',
- url: 'https://github.com/primefaces/sakai-react',
- target: '_blank'
- }
- ]
- }
];
return (
@@ -177,10 +44,6 @@ const AppMenu = () => {
{model.map((item, i) => {
return !item?.seperator ? : ;
})}
-
-
-
-
);
diff --git a/layout/AppTopbar.tsx b/layout/AppTopbar.tsx
index 9a2c8849..38da5e27 100644
--- a/layout/AppTopbar.tsx
+++ b/layout/AppTopbar.tsx
@@ -1,13 +1,19 @@
/* eslint-disable @next/next/no-img-element */
import Link from 'next/link';
-import { classNames } from 'primereact/utils';
import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'react';
+import Tiered from '@/app/components/popUp/Tiered';
+import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn';
+import { classNames } from 'primereact/utils';
import { AppTopbarRef } from '@/types';
import { LayoutContext } from './context/layoutcontext';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
+import { usePathname } from 'next/navigation';
+import { logout } from '@/utils/logout';
const AppTopbar = forwardRef((props, ref) => {
- const { layoutConfig, layoutState, onMenuToggle, showProfileSidebar } = useContext(LayoutContext);
+ const { layoutConfig, layoutState, onMenuToggle, showProfileSidebar, user, setUser, setGlobalLoading } = useContext(LayoutContext);
+
const menubuttonRef = useRef(null);
const topbarmenuRef = useRef(null);
const topbarmenubuttonRef = useRef(null);
@@ -18,36 +24,162 @@ const AppTopbar = forwardRef((props, ref) => {
topbarmenubutton: topbarmenubuttonRef.current
}));
+ const pathName = usePathname();
+ const media = useMediaQuery('(max-width: 1000px)');
+
+ const items = [
+ {
+ label: 'Ачык онлайн курстар',
+ icon: 'pi pi-file',
+ items: [],
+ link: '/login'
+ },
+ {
+ label: 'Бакалавриат',
+ icon: 'pi pi-file',
+ items: []
+ },
+ {
+ label: 'Магистратура',
+ icon: 'pi pi-file',
+ items: []
+ },
+ {
+ label: 'Кошумча билим берүү',
+ icon: 'pi pi-file',
+ items: []
+ }
+ ];
+
+ const mobileMenu = [
+ user
+ ? {
+ label: 'Профиль',
+ icon: 'pi pi-user',
+ items: [
+ {
+ label: (
+
+
+ {user?.last_name}
+ {user?.name}
+
+
{user?.email}
+
+ )
+ },
+ {
+ label: 'Чыгуу',
+ icon: 'pi pi-sign-out',
+ items: [],
+ command: () => {
+ logout({ setUser, setGlobalLoading });
+ }
+ }
+ ]
+ }
+ : {
+ label: 'Кирүү',
+ icon: 'pi pi-sign-in',
+ items: [],
+ link: '/auth/login'
+ },
+ {
+ label: 'ОшМУнун сайты',
+ icon: '',
+ items: [],
+ link: 'https://oshsu.kg'
+ },
+ {
+ label: 'Байланыш',
+ icon: '',
+ items: []
+ }
+ ];
+
+ // profile
+ const profileItems = [
+ {
+ label: (
+
+
+ {user?.last_name}
+ {user?.name}
+
+
{user?.email}
+
+ )
+ },
+ {
+ label: 'Чыгуу',
+ icon: 'pi pi-sign-out',
+ items: [],
+ command: () => {
+ logout({ setUser, setGlobalLoading });
+ }
+ }
+ ];
+
return (
-

-
SAKAI
+ {/*

*/}
+

+
Цифровой кампус ОшГУ
-
-
-
-
-
-
-
+ {pathName !== '/' ? (
+
+
+
+ ) : (
+ ''
+ )}
-
-
- Calendar
-
-
-
- Profile
-
-
-
-
- Settings
-
-
+
+ {media ? (
+
+ ) : (
+
+ {/*
+ */}
+
+ ОшМУнун сайты
+
+
+ Байланыш
+
+
+ )}
+
+ {user && user ? (
+
+
+
+ ) : (
+
+
+
+
+
+ )}
+
);
diff --git a/layout/context/layoutcontext.tsx b/layout/context/layoutcontext.tsx
index 58e491dc..3581eb9d 100644
--- a/layout/context/layoutcontext.tsx
+++ b/layout/context/layoutcontext.tsx
@@ -1,6 +1,13 @@
'use client';
-import React, { useState, createContext } from 'react';
+import React, { useState, createContext, useEffect } from 'react';
import { LayoutState, ChildContainerProps, LayoutConfig, LayoutContextProps } from '@/types';
+import SessionManager from '@/app/components/SessionManager';
+import GlobalLoading from '@/app/components/loading/GlobalLoading';
+import Message from '@/app/components/messages/Message';
+import { ConfirmDialog } from 'primereact/confirmdialog';
+import { User } from '@/types/user';
+import { MessageType } from '@/types/messageType';
+
export const LayoutContext = createContext({} as LayoutContextProps);
export const LayoutProvider = ({ children }: ChildContainerProps) => {
@@ -22,6 +29,15 @@ export const LayoutProvider = ({ children }: ChildContainerProps) => {
menuHoverActive: false
});
+ // 👇 Добавляем пользователя
+ const [user, setUser] = useState(null);
+
+ // Глобальная загрузка
+ const [globalLoading, setGlobalLoading] = useState(true);
+
+ // Сообщение об ошибке/успехе
+ const [message, setMessage] = useState({state:false, value:{}});
+
const onMenuToggle = () => {
if (isOverlay()) {
setLayoutState((prevLayoutState) => ({ ...prevLayoutState, overlayMenuActive: !prevLayoutState.overlayMenuActive }));
@@ -52,8 +68,20 @@ export const LayoutProvider = ({ children }: ChildContainerProps) => {
layoutState,
setLayoutState,
onMenuToggle,
- showProfileSidebar
+ showProfileSidebar,
+ user,
+ setUser,
+ globalLoading,
+ setGlobalLoading,
+ message,
+ setMessage
};
- return {children};
+ return
+
+
+
+ {message.state && }
+ {children}
+ ;
};
diff --git a/layout/layout.tsx b/layout/layout.tsx
index 8421b2ca..ef069787 100644
--- a/layout/layout.tsx
+++ b/layout/layout.tsx
@@ -131,7 +131,7 @@ const Layout = ({ children }: ChildContainerProps) => {
diff --git a/package-lock.json b/package-lock.json
index 332e8ef7..81b25c5a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,23 +8,38 @@
"name": "sakai-react",
"version": "10.1.0",
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.7.2",
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
+ "@fortawesome/react-fontawesome": "^0.2.2",
+ "@hookform/resolvers": "^5.1.1",
"@types/node": "20.3.1",
"@types/react": "18.2.12",
"@types/react-dom": "18.2.5",
+ "aos": "^2.3.4",
+ "axios": "^1.10.0",
"chart.js": "4.2.1",
+ "countup": "^1.8.2",
"next": "13.4.8",
- "primeflex": "^3.3.1",
- "primeicons": "^6.0.1",
- "primereact": "10.2.1",
+ "primeflex": "^4.0.0",
+ "primeicons": "^7.0.0",
+ "primereact": "^10.9.6",
+ "quill": "^2.0.3",
"react": "18.2.0",
+ "react-countup": "^6.5.3",
"react-dom": "18.2.0",
- "typescript": "5.1.3"
+ "react-hook-form": "^7.58.1",
+ "typescript": "5.1.3",
+ "yup": "^1.6.1"
},
"devDependencies": {
+ "@tailwindcss/postcss": "^4.1.8",
+ "autoprefixer": "^10.4.21",
"eslint": "8.43.0",
"eslint-config-next": "13.4.6",
+ "postcss": "^8.5.4",
"prettier": "^2.8.8",
- "sass": "^1.63.4"
+ "sass": "^1.63.4",
+ "tailwindcss": "^4.1.8"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -36,6 +51,31 @@
"node": ">=0.10.0"
}
},
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@babel/runtime": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz",
@@ -103,6 +143,59 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
+ "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
+ "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
+ "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-fontawesome": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz",
+ "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react": ">=16.3"
+ }
+ },
+ "node_modules/@hookform/resolvers": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz",
+ "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==",
+ "dependencies": {
+ "@standard-schema/utils": "^0.3.0"
+ },
+ "peerDependencies": {
+ "react-hook-form": "^7.55.0"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@@ -136,6 +229,66 @@
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
@@ -331,6 +484,11 @@
"integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==",
"dev": true
},
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
+ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
+ },
"node_modules/@swc/helpers": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
@@ -339,6 +497,267 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz",
+ "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.8"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz",
+ "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.8",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.8",
+ "@tailwindcss/oxide-darwin-x64": "4.1.8",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.8",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.8",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.8",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.8",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.8",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.8"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz",
+ "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz",
+ "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz",
+ "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz",
+ "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz",
+ "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz",
+ "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz",
+ "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz",
+ "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz",
+ "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz",
+ "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@emnapi/wasi-threads": "^1.0.2",
+ "@napi-rs/wasm-runtime": "^0.2.10",
+ "@tybys/wasm-util": "^0.9.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz",
+ "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz",
+ "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz",
+ "integrity": "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==",
+ "dev": true,
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.8",
+ "@tailwindcss/oxide": "4.1.8",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.8"
+ }
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -561,6 +980,16 @@
"node": ">= 8"
}
},
+ "node_modules/aos": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/aos/-/aos-2.3.4.tgz",
+ "integrity": "sha512-zh/ahtR2yME4I51z8IttIt4lC1Nw0ktsFtmeDzID1m9naJnWXhCoARaCgNOGXb5CLy3zm+wqmRAEgMYB5E2HUw==",
+ "dependencies": {
+ "classlist-polyfill": "^1.0.3",
+ "lodash.debounce": "^4.0.6",
+ "lodash.throttle": "^4.0.1"
+ }
+ },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -721,6 +1150,48 @@
"has-symbols": "^1.0.3"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.21",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
+ "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.24.4",
+ "caniuse-lite": "^1.0.30001702",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -742,6 +1213,16 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
+ "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -788,6 +1269,38 @@
"node": ">=8"
}
},
+ "node_modules/browserslist": {
+ "version": "4.25.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
+ "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001718",
+ "electron-to-chromium": "^1.5.160",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -813,6 +1326,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -823,9 +1348,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001572",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz",
- "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==",
+ "version": "1.0.30001720",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz",
+ "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==",
"funding": [
{
"type": "opencollective",
@@ -907,6 +1432,20 @@
"node": ">= 6"
}
},
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/classlist-polyfill": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz",
+ "integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ=="
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -930,12 +1469,33 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "node_modules/countup": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/countup/-/countup-1.8.2.tgz",
+ "integrity": "sha512-1qOuy+h+dgLY4TroLb4ZC2hK7HYS2Z7mnSUvdzoYAU7EZbqOse1mKZta/KYPhDPfd9lPDyP3Tb4pH6a10RkoCw=="
+ },
+ "node_modules/countup.js": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.2.tgz",
+ "integrity": "sha512-UtRoPH6udaru/MOhhZhI/GZHJKAyAxuKItD2Tr7AbrqrOPBX/uejWBBJt8q86169AMqKkE9h9/24kFWbUk/Bag=="
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1015,6 +1575,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -1024,6 +1592,15 @@
"node": ">=6"
}
},
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -1057,6 +1634,25 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.161",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz",
+ "integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==",
+ "dev": true
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -1064,9 +1660,9 @@
"dev": true
},
"node_modules/enhanced-resolve": {
- "version": "5.15.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
- "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
+ "version": "5.18.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
+ "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -1129,6 +1725,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-iterator-helpers": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz",
@@ -1151,15 +1763,26 @@
"safe-array-concat": "^1.0.1"
}
},
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-set-tostringtag": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
- "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
- "dev": true,
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dependencies": {
- "get-intrinsic": "^1.2.2",
- "has-tostringtag": "^1.0.0",
- "hasown": "^2.0.0"
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -1191,6 +1814,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -1614,12 +2246,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
+ },
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@@ -1729,6 +2371,25 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -1738,6 +2399,34 @@
"is-callable": "^1.1.3"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
+ "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -1762,7 +2451,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -1795,20 +2483,40 @@
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
- "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
- "dev": true,
- "dependencies": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.0"
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -1925,12 +2633,11 @@
}
},
"node_modules/gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "dev": true,
- "dependencies": {
- "get-intrinsic": "^1.1.3"
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1990,10 +2697,9 @@
}
},
"node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "dev": true,
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"engines": {
"node": ">= 0.4"
},
@@ -2002,12 +2708,11 @@
}
},
"node_modules/has-tostringtag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
- "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
- "dev": true,
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dependencies": {
- "has-symbols": "^1.0.2"
+ "has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@@ -2017,10 +2722,9 @@
}
},
"node_modules/hasown": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
- "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
- "dev": true,
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -2449,6 +3153,15 @@
"set-function-name": "^2.0.1"
}
},
+ "node_modules/jiti": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+ "dev": true,
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2508,47 +3221,275 @@
"object.values": "^1.1.6"
},
"engines": {
- "node": ">=4.0"
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.22",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+ "dev": true
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "dev": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "dependencies": {
- "json-buffer": "3.0.1"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/language-subtag-registry": {
- "version": "0.3.22",
- "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
- "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
- "dev": true
- },
- "node_modules/language-tags": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
- "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "dependencies": {
- "language-subtag-registry": "^0.3.20"
- },
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=0.10"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
},
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">= 0.8.0"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
"node_modules/locate-path": {
@@ -2566,12 +3507,38 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead."
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -2595,6 +3562,23 @@
"node": ">=10"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2617,6 +3601,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2638,6 +3641,42 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -2645,9 +3684,9 @@
"dev": true
},
"node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
@@ -2717,6 +3756,35 @@
}
}
},
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.14",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
+ "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -2726,6 +3794,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -2899,6 +3976,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/parchment": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
+ "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A=="
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -2954,9 +4036,9 @@
}
},
"node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -2971,9 +4053,10 @@
}
},
"node_modules/postcss": {
- "version": "8.4.14",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
- "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+ "version": "8.5.4",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
+ "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2982,17 +4065,27 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -3018,19 +4111,19 @@
}
},
"node_modules/primeflex": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz",
- "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ=="
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-4.0.0.tgz",
+ "integrity": "sha512-UOEZCRjR36+sm5bUpDhS1xbA068l9VC6y1aTNVqQPtXuKIdPTqAWHRUxj3mKAoPrQ9W373ooJJMgNVXfiaw04g=="
},
"node_modules/primeicons": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz",
- "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA=="
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
+ "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw=="
},
"node_modules/primereact": {
- "version": "10.2.1",
- "resolved": "https://registry.npmjs.org/primereact/-/primereact-10.2.1.tgz",
- "integrity": "sha512-F25053E1z+fod6V7AJ1Ix/DwSPkFQotk2+Idm0XkFsN+gQEywH7DgtbvNKpUA+LEq48h2+/WVK3D0V8IAm1Npg==",
+ "version": "10.9.6",
+ "resolved": "https://registry.npmjs.org/primereact/-/primereact-10.9.6.tgz",
+ "integrity": "sha512-0Jjz/KzfUURSHaPTXJwjL2Dc7CDPnbO17MivyJz7T5smGAMLY5d+IqpQhV61R22G/rDmhMh3+32LCNva2M8fRw==",
"dependencies": {
"@types/react-transition-group": "^4.4.1",
"react-transition-group": "^4.4.1"
@@ -3039,9 +4132,9 @@
"node": ">=14.0.0"
},
"peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -3059,6 +4152,16 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/property-expr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
+ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -3088,6 +4191,33 @@
}
]
},
+ "node_modules/quill": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
+ "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
+ "dependencies": {
+ "eventemitter3": "^5.0.1",
+ "lodash-es": "^4.17.21",
+ "parchment": "^3.0.0",
+ "quill-delta": "^5.1.0"
+ },
+ "engines": {
+ "npm": ">=8.2.3"
+ }
+ },
+ "node_modules/quill-delta": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
+ "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
+ "dependencies": {
+ "fast-diff": "^1.3.0",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.isequal": "^4.5.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@@ -3099,6 +4229,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-countup": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.5.3.tgz",
+ "integrity": "sha512-udnqVQitxC7QWADSPDOxVWULkLvKUWrDapn5i53HE4DPRVgs+Y5rr4bo25qEl8jSh+0l2cToJgGMx+clxPM3+w==",
+ "dependencies": {
+ "countup.js": "^2.8.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.3.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -3111,6 +4252,21 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.58.1",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.58.1.tgz",
+ "integrity": "sha512-Lml/KZYEEFfPhUVgE0RdCVpnC4yhW+PndRhbiTtdvSlQTL8IfVR+iQkBjLIvmmc6+GGoVeM11z37ktKFPAb0FA==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18 || ^19"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -3414,9 +4570,9 @@
}
},
"node_modules/source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"engines": {
"node": ">=0.10.0"
}
@@ -3573,6 +4729,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/tailwindcss": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz",
+ "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==",
+ "dev": true
+ },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -3582,12 +4744,43 @@
"node": ">=6"
}
},
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/tiny-case": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
+ "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -3600,6 +4793,11 @@
"node": ">=8.0"
}
},
+ "node_modules/toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
+ },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -3754,6 +4952,36 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -3890,6 +5118,28 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/yup": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz",
+ "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==",
+ "dependencies": {
+ "property-expr": "^2.0.5",
+ "tiny-case": "^1.0.3",
+ "toposort": "^2.0.2",
+ "type-fest": "^2.19.0"
+ }
+ },
+ "node_modules/yup/node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/zod": {
"version": "3.21.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
diff --git a/package.json b/package.json
index 8071b50b..c4d72fa3 100644
--- a/package.json
+++ b/package.json
@@ -10,22 +10,37 @@
"lint": "next lint"
},
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.7.2",
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
+ "@fortawesome/react-fontawesome": "^0.2.2",
+ "@hookform/resolvers": "^5.1.1",
"@types/node": "20.3.1",
"@types/react": "18.2.12",
"@types/react-dom": "18.2.5",
+ "aos": "^2.3.4",
+ "axios": "^1.10.0",
"chart.js": "4.2.1",
+ "countup": "^1.8.2",
"next": "13.4.8",
- "primeflex": "^3.3.1",
- "primeicons": "^6.0.1",
- "primereact": "10.2.1",
+ "primeflex": "^4.0.0",
+ "primeicons": "^7.0.0",
+ "primereact": "^10.9.6",
+ "quill": "^2.0.3",
"react": "18.2.0",
+ "react-countup": "^6.5.3",
"react-dom": "18.2.0",
- "typescript": "5.1.3"
+ "react-hook-form": "^7.58.1",
+ "typescript": "5.1.3",
+ "yup": "^1.6.1"
},
"devDependencies": {
+ "@tailwindcss/postcss": "^4.1.8",
+ "autoprefixer": "^10.4.21",
"eslint": "8.43.0",
"eslint-config-next": "13.4.6",
+ "postcss": "^8.5.4",
"prettier": "^2.8.8",
- "sass": "^1.63.4"
+ "sass": "^1.63.4",
+ "tailwindcss": "^4.1.8"
}
-}
\ No newline at end of file
+}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 00000000..8f3250b7
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ '@tailwindcss/postcss': {}, // Используем новый плагин
+ autoprefixer: {},
+ }
+}
\ No newline at end of file
diff --git a/public/layout/images/home-banner-phone.png b/public/layout/images/home-banner-phone.png
new file mode 100644
index 00000000..399a19dc
Binary files /dev/null and b/public/layout/images/home-banner-phone.png differ
diff --git a/public/layout/images/home-two.png b/public/layout/images/home-two.png
new file mode 100644
index 00000000..3372a6ad
Binary files /dev/null and b/public/layout/images/home-two.png differ
diff --git a/public/layout/images/logo-kg.svg b/public/layout/images/logo-kg.svg
new file mode 100644
index 00000000..ac33ea12
--- /dev/null
+++ b/public/layout/images/logo-kg.svg
@@ -0,0 +1,205 @@
+
+
+
+
diff --git a/public/layout/images/logo-remove-white.png b/public/layout/images/logo-remove-white.png
new file mode 100644
index 00000000..3d28f7d5
Binary files /dev/null and b/public/layout/images/logo-remove-white.png differ
diff --git a/public/layout/images/logo-remove.png b/public/layout/images/logo-remove.png
new file mode 100644
index 00000000..929f087a
Binary files /dev/null and b/public/layout/images/logo-remove.png differ
diff --git a/public/layout/images/man.png b/public/layout/images/man.png
new file mode 100644
index 00000000..0168ee16
Binary files /dev/null and b/public/layout/images/man.png differ
diff --git a/public/layout/images/no-image.png b/public/layout/images/no-image.png
new file mode 100644
index 00000000..3f257212
Binary files /dev/null and b/public/layout/images/no-image.png differ
diff --git a/public/layout/images/shape.png b/public/layout/images/shape.png
new file mode 100644
index 00000000..a82ce023
Binary files /dev/null and b/public/layout/images/shape.png differ
diff --git a/public/layout/images/shape1.png b/public/layout/images/shape1.png
new file mode 100644
index 00000000..bee24c65
Binary files /dev/null and b/public/layout/images/shape1.png differ
diff --git a/public/layout/images/shape2.png b/public/layout/images/shape2.png
new file mode 100644
index 00000000..f1fe1c5b
Binary files /dev/null and b/public/layout/images/shape2.png differ
diff --git a/public/layout/svg/red-logo.svg b/public/layout/svg/red-logo.svg
new file mode 100644
index 00000000..69deed08
--- /dev/null
+++ b/public/layout/svg/red-logo.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/public/layout/svg/white-logo.svg b/public/layout/svg/white-logo.svg
new file mode 100644
index 00000000..d76c8ff6
--- /dev/null
+++ b/public/layout/svg/white-logo.svg
@@ -0,0 +1,47 @@
+
+
+
+
diff --git a/schemas/authSchema.tsx b/schemas/authSchema.tsx
new file mode 100644
index 00000000..6d8159a5
--- /dev/null
+++ b/schemas/authSchema.tsx
@@ -0,0 +1,22 @@
+import * as yup from 'yup';
+
+export const schema = yup.object().shape({
+ email: yup.string()
+ .email('email туура эмес форматта')
+ .required('email талап кылынат!')
+ .matches(
+ /^[a-zA-Z0-9._%+-]+@oshsu\.kg$/,
+ 'email "@oshsu.kg" форматында болуш керек'
+ )
+ .matches(
+ /^[a-zA-Z0-9._%+-]+@oshsu\.kg$/, // Стандартный формат email (только буквы, цифры, точки, подчеркивания и плюсы)
+ 'email "@oshsu.kg" форматында жана тыюу салынган символдор камтылбашы керек'
+ ),
+ password: yup.string()
+ .required('Сырсөз талап кылынат!')
+ .max(20, 'Сырсөз эң көп дегенде 20 белгиден турушу керек')
+ .matches(
+ /^[^!@#$%^&*()_+={}[\]:;"'`<>,.?/\\|]*$/,
+ 'Сырсөздө тыюу салынган символдор камтылбашы керек'
+ ),
+});
\ No newline at end of file
diff --git a/schemas/baseSchema.tsx b/schemas/baseSchema.tsx
new file mode 100644
index 00000000..bbeacaf3
--- /dev/null
+++ b/schemas/baseSchema.tsx
@@ -0,0 +1,8 @@
+import * as yup from 'yup';
+
+export const baseSchema = yup.object().shape({
+ title: yup
+ .string()
+ .required('Талап кылынат!')
+ .max(20, 'Аталыштын узундугу макс 20 тамга')
+});
diff --git a/schemas/lessonSchema.tsx b/schemas/lessonSchema.tsx
new file mode 100644
index 00000000..a188394c
--- /dev/null
+++ b/schemas/lessonSchema.tsx
@@ -0,0 +1,13 @@
+import * as yup from 'yup';
+
+export const lessonSchema = yup.object().shape({
+ videoReq: yup
+ .string()
+ .required('Талап кылынат!')
+ .matches(/^https?:\/\/.+/, 'Видео шилтеме "http://" "https://" форматында болуш керек'),
+ usefulLink: yup
+ .string()
+ .required('Талап кылынат!')
+ .matches(/^[^!@#$%^&*()_+={}[\]:;"'`<>,.?/\\|]*$/, 'Сырсөздө тыюу салынган символдор камтылбашы керек'),
+ title: yup.string().required('Талап кылынат!').max(20, 'Аталыштын узундугу макс 20 тамга')
+});
diff --git a/services/auth.tsx b/services/auth.tsx
new file mode 100644
index 00000000..dc5c432a
--- /dev/null
+++ b/services/auth.tsx
@@ -0,0 +1,51 @@
+import { LoginType } from "@/types/login";
+
+let url = '';
+
+// const params = new URLSearchParams({
+// category_id: String(id),
+// active: '' + param.active,
+// ending: '' + param.ending,
+// });
+
+export const login = async (value:LoginType) => {
+ url = process.env.NEXT_PUBLIC_BASE_URL + '/login?';
+
+ try {
+ const res = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(value)
+ });
+ const data = await res.json();
+ return data;
+ } catch (err) {
+ console.log('Ошибка при авторизации', err);
+ return [];
+ }
+};
+
+export const getUser = async (token:string) => {
+ url = process.env.NEXT_PUBLIC_BASE_URL + '/v1/user';
+ console.log(token);
+
+ const headers: HeadersInit = token
+ ? {
+ Authorization: `Bearer ${token}`
+ }
+ : {};
+
+ try {
+ const res = await fetch(url, {
+ headers
+ });
+
+ const data = await res.json();
+ return data;
+ } catch (err) {
+ console.log('Ошибка при получении пользователя', err);
+ return [];
+ }
+};
diff --git a/services/courses.tsx b/services/courses.tsx
new file mode 100644
index 00000000..019734f5
--- /dev/null
+++ b/services/courses.tsx
@@ -0,0 +1,325 @@
+import { CourseCreateType } from '@/types/courseCreateType';
+import { lessonStateType } from '@/types/lessonStateType';
+import axiosInstance from '@/utils/axiosInstance';
+
+let url = '';
+
+export const fetchCourses = async (token: string | null, page: number | null, limit: number | null) => {
+ try {
+ console.log('Номер запрашиваемой страницы ', page);
+
+ const res = await axiosInstance.get(`/v1/teacher/courses?page=${Number(page)}&limit=${''}`, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ });
+ const data = await res.data;
+
+ return data;
+ } catch (error) {
+ console.error('Ошибка загрузки:', error);
+ }
+};
+
+export const addCourse = async (token: string | null, value: CourseCreateType) => {
+ const formData = new FormData();
+ formData.append('title', value.title);
+ formData.append('description', value.description);
+ formData.append('image', String(value.image));
+ formData.append('video_url', value.video_url);
+
+ try {
+ const res = await axiosInstance.post(`/v1/teacher/courses/store`, formData, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ console.log('Урок: ', res.data);
+
+ return res.data;
+ } catch (err) {
+ console.log('Ошибка при добавлении курса', err);
+ return err;
+ }
+};
+
+export const deleteCourse = async (token: string | null, id: number) => {
+ url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/courses/delete?course_id=${id}`;
+
+ try {
+ const res = await axiosInstance.delete(`/v1/teacher/courses/delete?course_id=${id}`, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ console.log(res.data);
+ return res.data;
+ } catch (err) {
+ console.log('Ошибка при удалении курса', err);
+ return err;
+ }
+};
+
+export const updateCourse = async (token: string | null, id: number | null, value: CourseCreateType) => {
+ console.log(value);
+
+ url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/courses/update?course_id=${id}`;
+
+ const formData = new FormData();
+ formData.append('title', value.title);
+ formData.append('description', value.description);
+ if (value.image instanceof File) {
+ formData.append('image', value.image);
+ }
+ formData.append('video_url', value.video_url);
+
+ try {
+ const res = await axiosInstance.post(`/v1/teacher/courses/update?course_id=${id}`, formData, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при обновлении курса', err);
+ return err;
+ }
+};
+
+// Themes
+
+export const fetchCourseInfo = async (token: string | null, id: number | null) => {
+ try {
+ const res = await axiosInstance.get(`/v1/teacher/courses/show?course_id=${id}`, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при получении темы', err);
+ return err;
+ }
+};
+
+export const addThemes = async (token: string | null, id: number, value: string) => {
+ const formData = new FormData();
+ formData.append('title', value);
+
+ try {
+ const res = await axiosInstance.post(`/v1/teacher/lessons/store?course_id=${id}&title=${value}`, formData, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при добавлении темы', err);
+ return err;
+ }
+};
+
+export const fetchThemes = async (token: string | null, id: number) => {
+ try {
+ const res = await axiosInstance(`/v1/teacher/lessons?course_id=${id}`, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при получении темы', err);
+ return err;
+ }
+};
+
+export const updateTheme = async (token: string | null, course_id: number | null, theme_id: number | null, value: CourseCreateType) => {
+ const formData = new FormData();
+ formData.append('title', value.title);
+
+ try {
+ const res = await axiosInstance.post(`/v1/teacher/lessons/update?course_id=${course_id}&title=${value}&lesson_id=${theme_id}`, formData, {
+ headers: {
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при обновлении темы', err);
+ return err;
+ }
+};
+
+export const deleteTheme = async (token: string | null, id: number) => {
+ try {
+ const res = await axiosInstance.delete(`/v1/teacher/lessons/delete?lesson_id=${id}`, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при удалении темы', err);
+ return err;
+ }
+};
+
+// Lessons
+
+export const addLesson = async (
+ type: string,
+ token: string | null,
+ courseId: number | null,
+ lessonId: number | null,
+ value: lessonStateType | string,
+ title: lessonStateType | string) => {
+
+ let formData = new FormData();
+ console.log(value, title);
+ let headers: Record
= token ? { Authorization: `Bearer ${token}` } : {};
+ let url = '';
+ let body: lessonStateType | string | FormData = value;
+
+ if (type === 'text') {
+ url = `/v1/teacher/textcontent/store?course_id=${courseId}&lesson_id=${lessonId}&content=${value}`;
+ headers['Content-Type'] = 'application/json';
+ body = value;
+ } else if(type === 'doc' && (typeof value === 'object') && value !== null){
+ url = `/v1/teacher/document/store?lesson_id=${lessonId}`;
+ formData.append('lesson_id', String(lessonId));
+ if(value.file){
+ formData.append('document', value.file && value.file);
+ }
+ formData.append('title', String(title));
+ formData.append('description', String(value?.description));
+ body = formData;
+ } else if(type === 'url' && typeof value === 'object' && value !== null){
+ url = `v1/teacher/usefullinks/store?lesson_id=${lessonId}&title=${title}&description=${value.description}&url=${value.url}`;
+ formData.append('lesson_id', String(lessonId));
+ formData.append('document', String(value?.url));
+ formData.append('title', String(title));
+ formData.append('description', String(value?.description));
+ body = formData;
+ }
+
+ try {
+ const res = await axiosInstance.post(url, body, {
+ headers
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при добавлении урока', err);
+ return err;
+ }
+};
+
+export const fetchLesson = async (type: string, token: string | null, courseId: number | null, lessonId: number | null) => {
+ let url = '';
+ let formData = new FormData();
+
+ if (type === 'text') {
+ url = `/v1/teacher/textcontent?course_id=${courseId}&lesson_id=${lessonId}`;
+ } else if (type === 'doc') {
+ url = `/v1/teacher/document?lesson_id=${lessonId}`
+ // formData.append('title', value.title);
+ // formData.append('description', value.description);
+ }
+
+ try {
+ const res = await axiosInstance.get(
+ url,
+
+ {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ }
+ );
+
+ // const data = await res();
+ console.log('Урок: ', res.data);
+
+ return res.data;
+ } catch (err) {
+ console.log('Ошибка при получении урока', err);
+ return err;
+ }
+};
+
+export const deleteLesson = async (type:string, token: string | null, courseId: number | null, lesson_id: number | null, content_id: number | null) => {
+ let url = '';
+ // console.log('content id: ', content_id);
+
+ if (type === 'text') {
+ url = `/v1/teacher/textcontent/delete?course_id=${courseId}&lesson_id=${lesson_id}&content_id=${content_id}`;
+ } else if(type === 'doc'){
+ url = `/v1/teacher/document/delete?lesson_id=${lesson_id}&document_id=${content_id}`;
+ } else if(type === 'url'){
+ url = `/v1/teacher/document/delete?lesson_id=${lesson_id}&document_id=${content_id}`;
+ }
+
+ try {
+ const res = await axiosInstance.delete(url, {
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при удалении урока', err);
+ return err;
+ }
+};
+
+export const updateLesson = async (type: string, token: string | null, course_id: number | null, lesson_id: number | null, contentId: number | null, value: any) => {
+ console.log(contentId, value);
+ let headers: Record = token ? { Authorization: `Bearer ${token}` } : {};
+ let formData = new FormData();
+ let url = '';
+
+ let body = value;
+ if (type === 'text') {
+ url = `/v1/teacher/textcontent/update?course_id=${course_id}&lesson_id=${lesson_id}&content_id=${contentId}&content=${value}`;
+ headers['Content-Type'] = 'application/json';
+ } else if(type === 'doc'){
+ url = `/v1/teacher/document/update?lesson_id=${lesson_id}&document_id=${contentId}&document=${value.file}`;
+ formData.append('lesson_id', String(lesson_id));
+ formData.append('document', value.file);
+ formData.append('document_id', String(contentId));
+ formData.append('title', String(value.title));
+ formData.append('description', String(value.description));
+ body = formData;
+ } else if(type === 'url'){
+ url = `/v1/teacher/document/update?lesson_id=${lesson_id}&document_id=${contentId}&document=${value.file}`;
+ formData.append('lesson_id', String(lesson_id));
+ formData.append('url?', value.file);
+ formData.append('url_id?', String(contentId));
+ formData.append('title', String(value.title));
+ formData.append('description', String(value.description));
+ body = formData;
+ }
+
+ try {
+ const res = await axiosInstance.post(url, body, {
+ // headers: token ? { Authorization: `Bearer ${token}` } : {}
+ headers
+ });
+
+ const data = await res.data;
+ return data;
+ } catch (err) {
+ console.log('Ошибка при обновлении урока', err);
+ return err;
+ }
+};
\ No newline at end of file
diff --git a/styles/layout/_button.scss b/styles/layout/_button.scss
new file mode 100644
index 00000000..980c20d2
--- /dev/null
+++ b/styles/layout/_button.scss
@@ -0,0 +1,67 @@
+button {
+ outline: 0;
+ border: none;
+
+ &:focus {
+ outline: 0;
+ border: 0;
+ }
+}
+
+.default-btn {
+ text-align: center;
+ color: var(--whiteColor);
+ // font-size: var(--fontSize);
+ font-weight: 600;
+ transition: var(--transition);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ z-index: 0;
+ box-shadow: none;
+ overflow: hidden;
+ white-space: nowrap;
+ cursor: pointer;
+}
+
+.fancyEffect {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 550px;
+ height: 550px;
+ margin: auto;
+ border-radius: 50%;
+ z-index: -1;
+}
+
+.fancyBefore {
+ transform-origin: top center;
+ transform: translateX(-50%) translateY(-5%) scale(0.4);
+ transition: transform 0.9s;
+}
+
+.fancyAfter {
+ transition: transform 1s;
+ transform: translateX(-45%) translateY(0) scale(1);
+ transform-origin: bottom center;
+}
+
+.p-button{
+ background-color: var(--mainColor);
+ padding: 7px 14px;
+ font-weight: bold;
+}
+
+@media screen and (max-width: '640px') {
+ .p-button {
+ padding: 5px 10px;
+ }
+}
+
+.reject-button {
+ background-color: transparent;
+ color: var(--bodyColor);
+}
\ No newline at end of file
diff --git a/styles/layout/_topbar.scss b/styles/layout/_topbar.scss
index 79402366..d6a02350 100644
--- a/styles/layout/_topbar.scss
+++ b/styles/layout/_topbar.scss
@@ -1,6 +1,6 @@
.layout-topbar {
position: fixed;
- height: 5rem;
+ height: 6rem;
z-index: 997;
left: 0;
top: 0;
@@ -18,12 +18,12 @@
color: var(--surface-900);
font-size: 1.5rem;
font-weight: 500;
- width: 300px;
+ // width: 300px;
border-radius: 12px;
img {
- height: 2.5rem;
- margin-right: .5rem;
+ // height: 2.5rem;
+ // margin-right: .5rem;
}
&:focus {
@@ -63,7 +63,7 @@
}
.layout-menu-button {
- margin-left: 2rem;
+ // margin-left: 2rem;
}
.layout-topbar-menu-button {
@@ -81,7 +81,7 @@
display: flex;
.layout-topbar-button {
- margin-left: 1rem;
+ // margin-left: 1rem;
}
}
}
@@ -91,35 +91,35 @@
justify-content: space-between;
.layout-topbar-logo {
- width: auto;
+ // width: auto;
order: 2;
}
.layout-menu-button {
margin-left: 0;
- order: 1;
+ order: 3;
}
.layout-topbar-menu-button {
display: inline-flex;
margin-left: 0;
- order: 3;
+ // order: 1;
}
.layout-topbar-menu {
margin-left: 0;
- position: absolute;
- flex-direction: column;
- background-color: var(--surface-overlay);
- box-shadow: 0px 3px 5px rgba(0,0,0,.02), 0px 0px 2px rgba(0,0,0,.05), 0px 1px 4px rgba(0,0,0,.08);
- border-radius: 12px;
- padding: 1rem;
- right: 2rem;
- top: 5rem;
- min-width: 15rem;
- display: none;
- -webkit-animation: scalein 0.15s linear;
- animation: scalein 0.15s linear;
+ // position: absolute;
+ // flex-direction: column;
+ // background-color: var(--surface-overlay);
+ // box-shadow: 0px 3px 5px rgba(0,0,0,.02), 0px 0px 2px rgba(0,0,0,.05), 0px 1px 4px rgba(0,0,0,.08);
+ // border-radius: 12px;
+ // padding: 1rem;
+ // right: 2rem;
+ // top: 5rem;
+ // min-width: 15rem;
+ // display: none;
+ // -webkit-animation: scalein 0.15s linear;
+ // animation: scalein 0.15s linear;
&.layout-topbar-menu-mobile-active {
display: block
diff --git a/styles/layout/_typography.scss b/styles/layout/_typography.scss
index b9a0c8ff..6278de2f 100644
--- a/styles/layout/_typography.scss
+++ b/styles/layout/_typography.scss
@@ -3,37 +3,13 @@ h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.2;
- color: var(--surface-900);
+ // color: var(--surface-900);
&:first-child {
margin-top: 0;
}
}
-h1 {
- font-size: 2.5rem;
-}
-
-h2 {
- font-size: 2rem;
-}
-
-h3 {
- font-size: 1.75rem;
-}
-
-h4 {
- font-size: 1.5rem;
-}
-
-h5 {
- font-size: 1.25rem;
-}
-
-h6 {
- font-size: 1rem;
-}
-
mark {
background: #FFF8E1;
padding: .25rem .4rem;
diff --git a/styles/layout/layout.scss b/styles/layout/layout.scss
index fef2ed48..0b5314a9 100644
--- a/styles/layout/layout.scss
+++ b/styles/layout/layout.scss
@@ -8,4 +8,5 @@
@import "./_footer";
@import "./_responsive";
@import "./_utils";
-@import "./_typography";
\ No newline at end of file
+@import "./_typography";
+@import "./_button";
\ No newline at end of file
diff --git a/types/authBaseType.tsx b/types/authBaseType.tsx
new file mode 100644
index 00000000..2aab9c9e
--- /dev/null
+++ b/types/authBaseType.tsx
@@ -0,0 +1,4 @@
+export interface AuthBaseType {
+ token: string | null;
+ id: number;
+}
\ No newline at end of file
diff --git a/types/courseCreateType.tsx b/types/courseCreateType.tsx
new file mode 100644
index 00000000..af341344
--- /dev/null
+++ b/types/courseCreateType.tsx
@@ -0,0 +1,7 @@
+export interface CourseCreateType {
+ title: string;
+ description: string;
+ video_url: string;
+ image?: string | File;
+ created_at?: string | null;
+}
\ No newline at end of file
diff --git a/types/courseType.tsx b/types/courseType.tsx
new file mode 100644
index 00000000..7b339e63
--- /dev/null
+++ b/types/courseType.tsx
@@ -0,0 +1,10 @@
+export interface CourseType {
+ created_at: string;
+ description: string;
+ id: number;
+ image: string;
+ status: boolean;
+ title: string;
+ user_id: number;
+ video_url: string;
+}
diff --git a/types/layout.d.ts b/types/layout.d.ts
index fe94e601..fd7dbba4 100644
--- a/types/layout.d.ts
+++ b/types/layout.d.ts
@@ -2,6 +2,8 @@ import React, { ReactElement, Dispatch, SetStateAction, HTMLAttributeAnchorTarge
import { NextPage } from 'next';
import { Demo } from './demo';
import { Toast } from 'primereact/toast';
+import { User } from './user';
+import { MessageType } from './messageType';
/* Breadcrumb Types */
export interface AppBreadcrumbProps {
@@ -45,6 +47,14 @@ export interface LayoutContextProps {
setLayoutState: Dispatch>;
onMenuToggle: () => void;
showProfileSidebar: () => void;
+ user: User | null;
+ setUser: React.Dispatch>;
+ globalLoading: boolean;
+ setGlobalLoading: React.Dispatch>;
+ message: MessageType;
+ setMessage: React.Dispatch>;
+ // message: { state: boolean; value: MessageType };
+ // setMessage: React.Dispatch>;
}
export interface MenuContextProps {
diff --git a/types/lessonStateType.tsx b/types/lessonStateType.tsx
new file mode 100644
index 00000000..df86d653
--- /dev/null
+++ b/types/lessonStateType.tsx
@@ -0,0 +1,6 @@
+export interface lessonStateType {
+ title: string;
+ description: string | null;
+ file: File | null;
+ url: string | null;
+}
diff --git a/types/lessonType.tsx b/types/lessonType.tsx
new file mode 100644
index 00000000..c8f55fda
--- /dev/null
+++ b/types/lessonType.tsx
@@ -0,0 +1,12 @@
+ export interface lessonType {
+ id: number;
+ course_id: number | null;
+ created_at: string;
+ description: string;
+ document: string;
+ lesson_id: number;
+ status: boolean;
+ title: string;
+ updated_at: string;
+ user_id: number;
+ }
diff --git a/types/login.tsx b/types/login.tsx
new file mode 100644
index 00000000..2cae126b
--- /dev/null
+++ b/types/login.tsx
@@ -0,0 +1,4 @@
+export interface LoginType {
+ email: string;
+ password: string;
+}
diff --git a/types/messageType.tsx b/types/messageType.tsx
new file mode 100644
index 00000000..589dbb8c
--- /dev/null
+++ b/types/messageType.tsx
@@ -0,0 +1,8 @@
+export interface MessageType {
+ state: boolean;
+ value: {
+ severity?:string;
+ summary?:string;
+ detail?:string;
+ }
+}
\ No newline at end of file
diff --git a/types/tabViewChange.tsx b/types/tabViewChange.tsx
new file mode 100644
index 00000000..82122bb9
--- /dev/null
+++ b/types/tabViewChange.tsx
@@ -0,0 +1,4 @@
+export interface TabViewChange {
+ originalEvent: React.SyntheticEvent;
+ index: number;
+}
diff --git a/types/user.tsx b/types/user.tsx
new file mode 100644
index 00000000..442e50e9
--- /dev/null
+++ b/types/user.tsx
@@ -0,0 +1,12 @@
+export interface User {
+ birth_date: null;
+ email: string;
+ father_name: string;
+ id: number;
+ is_student: boolean;
+ is_working: boolean;
+ last_name: string;
+ name: string;
+ phone: string;
+ pin: null;
+}
diff --git a/utils/auth.tsx b/utils/auth.tsx
new file mode 100644
index 00000000..d4d6a629
--- /dev/null
+++ b/utils/auth.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+export function getToken(name:string) : string | null{
+ if (typeof document === 'undefined') {
+ return null;
+ }
+
+ const cookies = document.cookie ? document.cookie.split("; ") : [];
+ for (let cookie of cookies) {
+ const [key, value] = cookie.split("=");
+ if (key === name) return decodeURIComponent(value);
+ }
+ return null;
+};
+
+export function isTokenExpired() {
+ const testToken = getToken('access_token');
+
+ try {
+ if (!testToken) throw new Error("Токен отсутствует");
+
+ const { exp } = JSON.parse(atob(testToken.split(".")[1]));
+ return exp - Math.floor(Date.now() / 1000) <= 300;
+ } catch (error) {
+ console.log("Ошибка при разборе токена:", error);
+ return true; // В случае ошибки считаем, что истёк
+ }
+}
\ No newline at end of file
diff --git a/utils/axiosInstance.tsx b/utils/axiosInstance.tsx
new file mode 100644
index 00000000..d61eef9f
--- /dev/null
+++ b/utils/axiosInstance.tsx
@@ -0,0 +1,40 @@
+import axios from 'axios';
+
+const axiosInstance = axios.create({
+ baseURL: process.env.NEXT_PUBLIC_BASE_URL,
+ timeout: 10000,
+});
+
+axiosInstance.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (error) => {
+
+ const status = error.response?.status;
+ console.log(error);
+
+ if (status === 401) {
+ console.warn('Неавторизован. Удаляю токен...');
+ document.cookie = 'access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
+ localStorage.removeItem('userVisit');
+ window.location.href = '/auth/login';
+ }
+
+ if (status === 403) {
+ console.warn('Не имеет доступ. Перенаправляю...');
+ window.location.href = '/';
+ }
+
+ if (status === 404) {
+ console.warn('404 - Перенаправляю...');
+ // document.cookie = 'access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
+ // localStorage.removeItem('userVisit');
+ // window.location.href = '/pages/notfound';
+ }
+
+ return Promise.reject(error);
+ }
+);
+
+export default axiosInstance;
diff --git a/utils/getConfirmOptions.tsx b/utils/getConfirmOptions.tsx
new file mode 100644
index 00000000..1e5ea5d3
--- /dev/null
+++ b/utils/getConfirmOptions.tsx
@@ -0,0 +1,24 @@
+type ConfirmDialogOptions = {
+ message: string;
+ header?: string;
+ icon?: string;
+ defaultFocus?: 'accept' | 'reject';
+ accept?: () => void;
+ reject?: () => void;
+ acceptLabel?: string;
+ rejectLabel?: string;
+ acceptClassName?: string;
+ rejectClassName?: string;
+};
+
+export const getConfirmOptions = (id: number, onDelete: (id: number)=> void ): ConfirmDialogOptions => ({
+ message: 'Сиз чын эле өчүрүүнү каалайсызбы?',
+ header: 'Өчүрүү',
+ icon: 'pi pi-info-circle',
+ defaultFocus: 'reject',
+ acceptClassName: 'p-button-danger',
+ acceptLabel: 'Өчүрүү',
+ rejectLabel: 'Артка',
+ rejectClassName: 'p-button-secondary reject-button',
+ accept: () => onDelete(id)
+});
diff --git a/utils/getRedactor.tsx b/utils/getRedactor.tsx
new file mode 100644
index 00000000..18b6a4b3
--- /dev/null
+++ b/utils/getRedactor.tsx
@@ -0,0 +1,40 @@
+import { confirmDialog } from 'primereact/confirmdialog';
+
+type ConfirmDialogOptions = {
+ message: string;
+ header?: string;
+ icon?: string;
+ defaultFocus?: 'accept' | 'reject';
+ accept?: () => void;
+ reject?: () => void;
+ acceptLabel?: string;
+ rejectLabel?: string;
+ acceptClassName?: string;
+ rejectClassName?: string;
+};
+
+interface RedactorType {
+ onEdit: (id: number, type: string) => void;
+ getConfirmOptions: (id: number, onDelete: (id: number) => void) => ConfirmDialogOptions;
+ onDelete: (id: number) => void;
+}
+
+export const getRedactor = (rowData: any, handlers: RedactorType) => [
+ {
+ label: '',
+ icon: 'pi pi-pencil',
+ command: () => {
+ handlers.onEdit(rowData.id, rowData?.type);
+ }
+ },
+ {
+ label: '',
+ icon: 'pi pi-trash',
+ command: () => {
+ const options = handlers.getConfirmOptions(Number(rowData.id), handlers.onDelete );
+ if(options){
+ confirmDialog(options);
+ }
+ }
+ }
+];
diff --git a/utils/logout.tsx b/utils/logout.tsx
new file mode 100644
index 00000000..38d88fbf
--- /dev/null
+++ b/utils/logout.tsx
@@ -0,0 +1,14 @@
+'use client';
+
+export const logout = ({setGlobalLoading, setUser}) => {
+ console.log('logout');
+ setGlobalLoading(true);
+
+ setTimeout(() => {
+ setGlobalLoading(false);
+ }, 1000);
+
+ setUser(null);
+ localStorage.removeItem('userVisit');
+ document.cookie = 'access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
+};