diff --git a/app/(full-page)/auth/login/page.tsx b/app/(full-page)/auth/login/page.tsx index d3dd85ca..5c5bbdb4 100644 --- a/app/(full-page)/auth/login/page.tsx +++ b/app/(full-page)/auth/login/page.tsx @@ -1,63 +1,115 @@ /* eslint-disable @next/next/no-img-element */ 'use client'; import { useRouter } from 'next/navigation'; -import React, { useContext, useState } from 'react'; +import React, { use, useContext, useState } from 'react'; import { Checkbox } from 'primereact/checkbox'; import { Button } from 'primereact/button'; 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/schema'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { Controller } from 'react-hook-form'; +import { json } from 'stream/consumers'; +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' }); + + const { + register, + handleSubmit, + formState: { errors }, + control + } = useForm({ + resolver: yupResolver(schema), + mode: 'onChange' + }); + + const onSubmit = async (value:LoginType) => { + console.log('Данные пользователя: ', value); + + 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('Ошибка при авторизации'); + } + }; return ( -
-
- Sakai logo -
-
-
- Image -
Welcome, Isabel!
- Sign in to continue -
+
+ +
+
+ +
-
-
diff --git a/app/(full-page)/pages/notfound/page.tsx b/app/(full-page)/pages/notfound/page.tsx index 52c0b4ce..77df8132 100644 --- a/app/(full-page)/pages/notfound/page.tsx +++ b/app/(full-page)/pages/notfound/page.tsx @@ -1,54 +1,63 @@ import React from 'react'; import Link from 'next/link'; +import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn'; -const NotFoundPage = () => { - return ( -
-
- Sakai logo -
-
- 404 -

Not Found

-
Requested resource is not available
- - - - - - Frequently Asked Questions - Ultricies mi quis hendrerit dolor. - - - - - - - - Solution Center - Phasellus faucibus scelerisque eleifend. - - - - - - - - Permission Manager - Accumsan in nisl nisi scelerisque - - -
-
-
+const NotFoundPage = ({titleMessege}) => { + return
+
+

{titleMessege}

+ + +
- ); -}; +
; + // return ( + //
+ //
+ // Sakai logo + //
+ //
+ // 404 + //

Not Found

+ //
Requested resource is not available
+ // + // + // + // + // + // Frequently Asked Questions + // Ultricies mi quis hendrerit dolor. + // + // + // + // + // + // + // + // Solution Center + // Phasellus faucibus scelerisque eleifend. + // + // + // + // + // + // + // + // Permission Manager + // Accumsan in nisl nisi scelerisque + // + // + //
+ //
+ //
+ //
+ // ); +}; export default NotFoundPage; diff --git a/app/(main)/course/[courseTheme]/page.tsx b/app/(main)/course/[courseTheme]/page.tsx new file mode 100644 index 00000000..7ee3de69 --- /dev/null +++ b/app/(main)/course/[courseTheme]/page.tsx @@ -0,0 +1,260 @@ +'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 NotFoundPage from '@/app/(full-page)/pages/notfound/page'; + +export default function CourseTheme() { + const [hasThemes, setHasThemes] = useState(false); + const [themes, setThemes] = useState([]); + const [themeValue, setThemeValue] = useState({ title: '' }); + const [themeInfo, setThemeInfo] = useState(); + const [courseTitle, setCourseTitle] = useState(''); + const [selectedCourse, setSelectedCourse] = useState(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 handleFetchThemes = async () => { + const token = getToken('access_token'); + const data = await fetchThemes(token, courseTheme); + + if (data.lessons) { + setThemes(data.lessons.data); + setHasThemes(false); + } else { + setHasThemes(true); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Проблема с соединением. Повторите заново' } + }); // messege - Ошибка загрузки курсов + } + }; + + const handleFetchInfo = async () => { + const token = getToken('access_token'); + const data = await fetchCourseInfo(token, courseTheme); + + 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, 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 - Ошибка при добавлении + } + }; + + 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 - Ошибка при добавлении + } + }; + + const handleUpdateTheme = async () => { + const token = getToken('access_token'); + + const data = await updateTheme(token, courseTheme, selectedCourse.id, themeValue); + if (data.success) { + toggleSkeleton(); + handleFetchThemes(); + clearValues(); + setEditMode(false); + setSelectedCourse(null); + setMessage({ + state: true, + value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' } + }); // messege - Успех! + } else { + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка при при изменении темы', detail: 'Заполняйте поля правильно' } + }); // messege - Ошибка при изменении курса + } + }; + + const clearValues = () => { + setThemeValue({ title: '' }); + setCourseTitle(''); + setEditMode(false); + setSelectedCourse(null); + }; + + const getConfirmOptions = (id) => ({ + 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/lessons/[lessons]/page.tsx b/app/(main)/course/lessons/[lessons]/page.tsx new file mode 100644 index 00000000..0b71f965 --- /dev/null +++ b/app/(main)/course/lessons/[lessons]/page.tsx @@ -0,0 +1,95 @@ +'use client'; + +import { useRef, useState } from 'react'; +import { TabView, TabPanel } from 'primereact/tabview'; +import CKEditorWrapper from '@/app/components/CKEditorWrapper.tsx'; +import { Button } from 'primereact/button'; + +export default function Lesson() { + + const stepperRef = useRef(null); + + const [activeIndex, setActiveIndex] = useState(0); + const [contentShow, setContentShow] = useState(true); + + const handleTabChange = (e) => { + // console.log('Переход на шаг:', e); + // // fetchDataForStep(e.index); + setActiveIndex(e.index); + }; + + return ( +
+ handleTabChange(e)} + activeIndex={activeIndex} + className='' + pt={{ + nav: { className: 'flex flex-wrap justify-around' }, + panelContainer: { className: 'flex-1 pl-4' } + }} + > + + {contentShow && ( +
+ +
+ )} +
+ + {contentShow && ( +

+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo + enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius modi. +

+ )} +
+ + {contentShow && ( +

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in + culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus. +

+ )} +
+ + {contentShow && ( +

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in + culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus. +

+ )} +
+
+
+ ); +} diff --git a/app/(main)/course/page.tsx b/app/(main)/course/page.tsx new file mode 100644 index 00000000..472bdd6b --- /dev/null +++ b/app/(main)/course/page.tsx @@ -0,0 +1,408 @@ +'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 ConfirmModal from '@/app/components/popUp/ConfirmModal'; +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'; + +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 [courseCash, setCourseCash] = useState<{ [key: number]: any[] }>({}); + const [pagination, setPagination] = useState({ + currentPage: 1, + total: 0, + perPage: 0 + }); + + const { setMessage } = useContext(LayoutContext); + + const fetchData = async (page = 1) => { + console.log('Запрашиваем курсы...'); + + const token = getToken('access_token'); + const headers: HeadersInit = token ? { Authorization: `Bearer ${token}` } : {}; + + try { + console.log('Номер запрашиваемой страницы ', page); + + const res = await fetch(`http://api.mooc.oshsu.kg/public/api/v1/teacher/courses?page=${Number(page)}&limit=3`, { + headers + }); + const data = await res.json(); + + if (data.courses) { + setHasCourses(false); + setCourses(data.courses.data); + setPagination({ + currentPage: data.courses.current_page, + total: data.courses.total, + perPage: data.courses.per_page + }); + setCourseCash((prev) => ({ ...prev, [page]: data.courses.data })); + localStorage.setItem('lastPage', JSON.stringify(page)); + } else { + setHasCourses(true); + setCourseCash({}); + localStorage.removeItem('lastPage'); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Проблема с соединением. Повторите заново' } + }); // messege - Ошибка загрузки курсов + } + } catch (error) { + console.error('Ошибка загрузки:', error); + localStorage.removeItem('lastPage'); + setCourseCash({}); + setHasCourses(true); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Повторите заново' } + }); // messege - Ошибка загрузки курсов + } + }; + + 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(); + fetchData(); + setCourseCash({}); + setMessage({ + state: true, + value: { severity: 'success', summary: 'Ийгиликтүү кошулду!', detail: '' } + }); // messege - Успех! + } else { + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при добавлении' } + }); // messege - Ошибка при добавлении + } + }; + + const handleDeleteCourse = async (id: number) => { + const token = getToken('access_token'); + + const data = await deleteCourse(token, id); + if (data.success) { + toggleSkeleton(); + fetchData(); + setCourseCash({}); + setMessage({ + state: true, + value: { severity: 'success', summary: 'Ийгиликтүү өчүрүлдү!', detail: '' } + }); // messege - Успех! + } else { + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при при удалении' } + }); // messege - Ошибка при добавлении + } + }; + + const handleUpdateCourse = async () => { + const token = getToken('access_token'); + + const data = await updateCourse(token, selectedCourse, courseValue); + if (data.success) { + toggleSkeleton(); + fetchData(); + clearValues(); + setEditMode(false); + setSelectedCourse(null); + setCourseCash({}); + setMessage({ + state: true, + value: { severity: 'success', summary: 'Ийгиликтүү өзгөртүлдү!', detail: '' } + }); // messege - Успех! + } else { + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка при при изменении курса', detail: 'Заполняйте поля правильно' } + }); // messege - Ошибка при изменении курса + } + }; + + const clearValues = () => { + setCourseValue({ title: '', description: '', video_url: '', image: '' }); + setCourseTitle(''); + setVideo_url(''); + setDesciption(''); + setImage(''); + setEditMode(false); + setSelectedCourse(null); + setCourseCash({}); + }; + + const onSelect = (e: FileUploadSelectEvent) => { + setImage(e.files[0].name); // сохраняешь файл + 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 ( +
+ Course image +
+ ); + } + + return ( +
+ Course image +
+ ); + }; + + 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); + }; + + // Ручное управление пагинацией + const handlePageChange = (page: number) => { + console.log(courseCash, courseCash[page]); + + if (page in courseCash) { + setCourses(courseCash[page]); + setPagination((prev) => ({ ...prev, currentPage: page })); + } else { + fetchData(page); + } + }; + + useEffect(() => { + const title = courseTitle.trim(); + if (title.length > 0) { + setForStart(false); + } else { + setForStart(true); + } + }, [courseTitle]); + + useEffect(() => { + fetchData(); + }, []); + + useEffect(() => { + console.log('Курсы ', courses); + courses.length < 1 ? setHasCourses(true) : setHasCourses(false); + }, [courses]); + + useEffect(() => { + console.log('edit mode ', editMode); + + 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]); + + return ( +
+ +
+
+
+ + { + 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 ? ( +
+ Сүрөт: {image} +
+ ) : ( + jpeg, png, jpg + )} +
+
+
+
+ +
+

Курстар

+ +
+ + {hasCourses ? ( + + ) : ( +
+ {skeleton ? ( + + ) : ( + <> + + + ( + + {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..525d8af1 --- /dev/null +++ b/app/components/CKEditorWrapper.tsx.tsx @@ -0,0 +1,29 @@ +'use client'; +import useTypingEffect from '@/hooks/useTypingEffect'; +import { Editor } from 'primereact/editor'; +import { useEffect, useState } from 'react'; + +export default function CKEditorWrapper() { + const [text, setText] = useState(''); + const [toggleTyping, setToggleTyping] = useState(true); + + const typedText = useTypingEffect( + 'Пример фейкового ввода', + toggleTyping + ); + + useEffect(()=> { + // console.log(typedText, typedText.length); + },[typedText]); + + return ( +
+ {/* {typedText.length > 0 ? setToggleTyping(false)} className='w-[800px] h-[300px]' /> + : setText(e.htmlValue)} className='w-[800px] h-[300px]' /> + } */} + setToggleTyping(false)} onChange={(e)=> setText(e.target.value)} value={typedText || text} className={`${typedText ? 'text-2xl' : ''}`} name="" id="" /> + +
{typedText}
+
+ ); +} diff --git a/app/components/CounterBanner.tsx b/app/components/CounterBanner.tsx new file mode 100644 index 00000000..efa045af --- /dev/null +++ b/app/components/CounterBanner.tsx @@ -0,0 +1,58 @@ +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..0ecdc498 --- /dev/null +++ b/app/components/HomeClient.tsx @@ -0,0 +1,171 @@ +"use client"; + +import AOS from "aos"; +import "aos/dist/aos.css"; +import { useEffect } from "react"; +import CounterBanner from "./CounterBanner"; +import Link from "next/link"; +import { faClock, faVideo,} from "@fortawesome/free-solid-svg-icons"; +import MyFontAwesome from "./MyFontAwesome"; +import VideoPlay from "./VideoPlay"; + +export default function HomeClient() { + useEffect(() => { + AOS.init(); + }, []); + + return ( +
+
+
+
+
+
+ Фото + + ЫҢГАЙЛУУ ОКУУ ҮЧҮН ОНЛАЙН МЕЙКИНДИК + +
+

+ Аралыктан окутуу порталына кош келиңиз! +

+
+ {" "} + Университеттин онлайн билим берүү жаатындагы долбоорлорун + бириктирүүдөбүз: +
    +
  • ачык онлайн курстар
  • +
  • жогорку билим берүү программалары
  • +
+
+
+
+ +
+
+ +
+
+ Shape +
+ + Пользователь + +
+ Shape +
+ +
+ Shape +
+ +
+
+ 13000 +

Студент

+
+
+ +
+
+ Куттуктайбыз! +

Сиздин кабыл алуу ийгиликтүү аяктады

+
+
+ +
+
+ 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/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 ( +
+ {if (!videoCall) return; setVideoCall(false); }}> +
+ +
+
+
+
+
setVideoCall(true)} + > + {/* Волна */} + + + {/* Иконка-кнопка */} +
+ +
+
+
+ Логотип ОшГУ +
+
+ ) +} diff --git a/app/components/buttons/FancyLinkBtn.tsx b/app/components/buttons/FancyLinkBtn.tsx new file mode 100644 index 00000000..64cd4d2c --- /dev/null +++ b/app/components/buttons/FancyLinkBtn.tsx @@ -0,0 +1,31 @@ +'use client'; + +import Link from 'next/link'; +import { useState } from 'react'; + +export default function FancyLinkBtn({ btnWidth, backround, effectBg, title }) { + const [position, setPosition] = useState(false); + + return ( +
+ +
+ ); +} 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..017fc105 --- /dev/null +++ b/app/components/popUp/ConfirmModal.tsx @@ -0,0 +1,15 @@ +'use client'; +import { confirmDialog } from 'primereact/confirmdialog'; +import { Button } from 'primereact/button'; + +export default function ConfirmModal({confirmVisible}) { + const handleClick = () => { + confirmDialog(confirmVisible); + }; + + return ( +
+
+ ); +} diff --git a/app/components/popUp/FormModal.tsx b/app/components/popUp/FormModal.tsx new file mode 100644 index 00000000..ebc1a9e9 --- /dev/null +++ b/app/components/popUp/FormModal.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { Button } from 'primereact/button'; +import { Dialog } from 'primereact/dialog'; + +export default function FormModal({children, title, fetchValue, clearValues, visible, setVisible, start}) { + + const footerContent = ( +
+
+ ); + + return ( +
+ { + if (!visible) return; + setVisible(false); + }} + footer={footerContent} + > + {children} + +
+ ); +} diff --git a/app/components/popUp/Tiered.tsx b/app/components/popUp/Tiered.tsx new file mode 100644 index 00000000..bc8082a5 --- /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 ( +
+
+ ); +} \ 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..be1a31e8 --- /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 }) { + const usingSkeleton = () => { + return ( +
+ +
+ ); + }; + + return
{Array.from({ length: count }).map((_, i) => usingSkeleton())}
; +} 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..6cc40130 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,346 @@ +@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; + --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); + } +} + +.my-custom-table { + /* border-collapse: collapse; + border: 1px solid #ccc; */ + /* width: 90%; */ + /* margin: auto; */ +} + +.my-custom-table td, +.my-custom-table th { + border: 1px solid #ccc; + padding: 8px; +} + +.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; +} + +.tab-custom-text-3{ + color: green !important; +} + +.tab-custom-text-4{ + color: var(--redColor) !important; +} \ 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..0ba00a0c --- /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/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..a44dd670 --- /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; + }); + }, 100); + + 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..628b21cf 100644 --- a/layout/AppFooter.tsx +++ b/layout/AppFooter.tsx @@ -2,16 +2,59 @@ 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 + // Logo return ( -
- Logo - by - PrimeReact +
+
+
+ Логотип +
+ Кыргызстан, 723500, г. Ош, ул. Ленина, 331, ОшГУ Главный корпус + Общий отдел: +996 3222 7-07-12, + факс +996 3222 7-09-15, + edu@oshsu.kg +
+
+
+
+ +

Курстар

+
+
+ lorem ipsum + lorem ipsum + lorem ipsum +
+
+
+
+ +

Байланыш

+
+
+ lorem ipsum + lorem ipsum + lorem ipsum +
+
+

+ © 2025 ОшГУ | 2025 OshSU - IT Academy Ошский Государственный Университет oshsu.kg +

+
); }; 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 ? :
  • ; })} - - - Prime Blocks - ); diff --git a/layout/AppTopbar.tsx b/layout/AppTopbar.tsx index 9a2c8849..3356cbe4 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,223 @@ 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: 'pi pi-list', + 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: [] + } + ] + }, + { + label: 'Бакалавриат', + icon: 'pi pi-file', + items: [] + }, + { + label: 'Магистратура', + icon: 'pi pi-file', + items: [] + }, + { + label: 'Кошумча билим берүү', + icon: 'pi pi-file', + items: [] + }, + { + label: 'Кошумча билим берүү', + icon: 'pi pi-file', + items: [] + }, + { + label: 'КАТАЛОГ', + icon: 'pi pi-list', + 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: [] + } + ] + } + ]; + + // profile + const profileItems = [ + { + label: ( +
    +
    + {user?.last_name} + {user?.name} +
    + {user?.email} +
    + ) + }, + { + label: 'Чыгуу', + icon: 'pi pi-sign-out', + items: [], + command: () => { + logout({ setUser, setGlobalLoading }); + } + } + ]; + return (
    - logo - SAKAI + {/* logo */} + logo +

    Цифровой кампус ОшГУ

    - - - + {pathName !== '/' ? ( + + ) : ( + '' + )}
    - - - - - +
    + {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) => {
    {children}
    - + {/* */}
    diff --git a/package-lock.json b/package-lock.json index 332e8ef7..94041227 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,23 +8,37 @@ "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.0.1", "@types/node": "20.3.1", "@types/react": "18.2.12", "@types/react-dom": "18.2.5", + "aos": "^2.3.4", "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.57.0", + "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 +50,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 +142,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.0.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", + "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "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 +228,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 +483,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 +496,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 +979,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 +1149,43 @@ "has-symbols": "^1.0.3" } }, + "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", @@ -788,6 +1253,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", @@ -823,9 +1320,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 +1404,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", @@ -936,6 +1447,16 @@ "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", @@ -1024,6 +1545,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 +1587,12 @@ "csstype": "^3.0.2" } }, + "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 +1600,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", @@ -1191,6 +1727,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 +2159,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", @@ -1738,6 +2293,19 @@ "is-callable": "^1.1.3" } }, + "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", @@ -2449,6 +3017,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", @@ -2489,66 +3066,294 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "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" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "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": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4.0" + "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-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": { - "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-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": { - "language-subtag-registry": "^0.3.20" - }, + "optional": true, + "os": [ + "win32" + ], "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-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, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, + "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 +3371,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 +3426,15 @@ "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/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2638,6 +3478,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 +3521,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 +3593,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 +3631,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 +3813,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 +3873,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 +3890,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 +3902,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 +3948,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 +3969,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 +3989,11 @@ "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/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3088,6 +4023,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 +4061,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 +4084,21 @@ "react": "^18.2.0" } }, + "node_modules/react-hook-form": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz", + "integrity": "sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg==", + "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 +4402,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 +4561,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 +4576,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 +4625,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 +4784,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 +4950,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..93d7e72b 100644 --- a/package.json +++ b/package.json @@ -10,22 +10,36 @@ "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.0.1", "@types/node": "20.3.1", "@types/react": "18.2.12", "@types/react-dom": "18.2.5", + "aos": "^2.3.4", "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.57.0", + "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/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/schema.tsx b/schemas/schema.tsx new file mode 100644 index 00000000..6d8159a5 --- /dev/null +++ b/schemas/schema.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/services/auth.tsx b/services/auth.tsx new file mode 100644 index 00000000..72fa6843 --- /dev/null +++ b/services/auth.tsx @@ -0,0 +1,50 @@ +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'; + + 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..a14e15a9 --- /dev/null +++ b/services/courses.tsx @@ -0,0 +1,234 @@ +import { AuthBaseType } from "@/types/authBaseType"; +import { CourseCreateType } from "@/types/courseCreateType"; + +let url = ''; + +type OnlyTitle = Pick; + +export const fetchCourses = async (token:string | null) => { + url = process.env.NEXT_PUBLIC_BASE_URL + '/v1/teacher/courses'; + + 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 []; + } +}; + +export const addCourse = async (token:string | null, value:CourseCreateType) => { + url = process.env.NEXT_PUBLIC_BASE_URL + '/v1/teacher/courses/store'; + console.log(value); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + console.log(value); + + const formData = new FormData(); + formData.append('title', value.title); + formData.append('description', value.description); + formData.append('image', value.image); + formData.append('video_url', value.video_url); + + try { + const res = await fetch(url, { + method: 'POST', + headers, + body: formData + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при добавлении курса', err); + return []; + } +}; + +export const deleteCourse = async (token:string | null, id:number) => { + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/courses/delete?course_id=${id}`; + console.log(id); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + try { + const res = await fetch(url, { + method: 'DELETE', + headers + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при удалении курса', err); + return []; + } +}; + +export const updateCourse = async (token: string | null, id:number | null, value) => { + 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); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + try { + const res = await fetch(url, { + method: 'POST', + headers, + body: formData + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при обновлении курса', err); + return []; + } +}; + +// Themes + +export const fetchCourseInfo = async (token:AuthBaseType, id:AuthBaseType) => { + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/courses/show?course_id=${id}`; + + 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 []; + } +}; + +export const addThemes = async (token:string | null, id:number, value:OnlyTitle) => { + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/lessons/store?course_id=${id}&title=${value}`; + console.log(value); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + const formData = new FormData(); + formData.append('title', value); + + try { + const res = await fetch(url, { + method: 'POST', + headers, + body: formData + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при добавлении темы', err); + return []; + } +}; + +export const fetchThemes = async (token:string | null, id:number) => { + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/lessons?course_id=${id}`; + + 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 []; + } +}; + +export const updateTheme = async (token: string | null, course_id:number | null, theme_id: number, value:CourseCreateType) => { + console.log(value); + + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/lessons/update?course_id=${course_id}&title=${value}&lesson_id=${theme_id}`; + + const formData = new FormData(); + formData.append('title', value.title); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + try { + const res = await fetch(url, { + method: 'POST', + headers, + body: formData + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при обновлении темы', err); + return []; + } +}; + +export const deleteTheme = async (token:string | null, id:number) => { + url = process.env.NEXT_PUBLIC_BASE_URL + `/v1/teacher/lessons/delete?lesson_id=${id}`; + console.log(id); + + const headers: HeadersInit = token + ? { Authorization: `Bearer ${token}` } + : {}; + + try { + const res = await fetch(url, { + method: 'DELETE', + headers + }); + + const data = await res.json(); + return data; + } catch (err) { + console.log('Ошибка при удалении темы', err); + return []; + } +}; \ No newline at end of file diff --git a/styles/layout/_button.scss b/styles/layout/_button.scss new file mode 100644 index 00000000..91dd1188 --- /dev/null +++ b/styles/layout/_button.scss @@ -0,0 +1,51 @@ +button { + outline: 0; + border: none; + + &:focus { + outline: 0; + border: 0; + } +} + +.default-btn { + // padding: 15px 35px; + text-align: center; + color: var(--whiteColor); + font-size: var(--fontSize); + font-weight: 400; + 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; +} 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..3be660c4 --- /dev/null +++ b/types/courseCreateType.tsx @@ -0,0 +1,6 @@ +export interface CourseCreateType { + title: string; + description: string; + video_url: string; + image?: string | File; +} \ 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/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/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..8ec683b5 --- /dev/null +++ b/utils/auth.tsx @@ -0,0 +1,28 @@ +'use client'; + +export function getToken(name:string) { + 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/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;'; +};