diff --git a/app/(full-page)/auth/login/page.tsx b/app/(full-page)/auth/login/page.tsx index d3dd85ca..1e8d8728 100644 --- a/app/(full-page)/auth/login/page.tsx +++ b/app/(full-page)/auth/login/page.tsx @@ -8,56 +8,110 @@ import { Password } from 'primereact/password'; import { LayoutContext } from '../../../../layout/context/layoutcontext'; import { InputText } from 'primereact/inputtext'; import { classNames } from 'primereact/utils'; +import InfoBanner from '@/app/components/InfoBanner'; + +import { useForm } from 'react-hook-form'; +import { schema } from '@/schemas/authSchema'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { Controller } from 'react-hook-form'; +import { getUser, login } from '@/services/auth'; +import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn'; +import { getToken } from '@/utils/auth'; +import { logout } from '@/utils/logout'; +import { LoginType } from '@/types/login'; const LoginPage = () => { - const [password, setPassword] = useState(''); - const [checked, setChecked] = useState(false); - const { layoutConfig } = useContext(LayoutContext); + const { layoutConfig, setUser, setMessage, setGlobalLoading } = useContext(LayoutContext); const router = useRouter(); - const containerClassName = classNames('surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden', { 'p-input-filled': layoutConfig.inputStyle === 'filled' }); + // const containerClassName = classNames('surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden', { 'p-input-filled': layoutConfig.inputStyle === 'filled' }); - return ( -
-
- Sakai logo -
-
-
- Image -
Welcome, Isabel!
- Sign in to continue -
+ const { + register, + handleSubmit, + formState: { errors }, + control + } = useForm({ + resolver: yupResolver(schema), + mode: 'onChange' + }); -
- - + const onSubmit = async (value: LoginType) => { + console.log('Данные пользователя: ', value); - - setPassword(e.target.value)} placeholder="Password" toggleMask className="w-full mb-5" inputClassName="w-full p-3 md:w-30rem"> + const user = await login(value); + console.log(user); + if (user && user.success) { + document.cookie = `access_token=${user.token.access_token}; path=/; Secure; SameSite=Strict; expires=${user.token.expires_at}`; + + const token = user.token.access_token; + if (token) { + const res = await getUser(token); + try { + if (res?.success) { + console.log(res); + if (res?.user.is_working) { + window.location.href = '/course'; + } + } else { + logout({ setUser, setGlobalLoading }); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' } + }); // messege - Ошибка при авторизации + console.log('Ошибка при получении пользователя'); + } + } catch (error) { + logout({ setUser, setGlobalLoading }); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Ошибка', detail: 'Ошибка при авторизации' } + }); // messege - Ошибка при авторизации + console.log('Ошибка при получении пользователя'); + } + } + } else { + console.log('Ошибка при авторизации'); + } + }; + + const onError = (errors:any) => { + console.log('Ошибки формы:', errors); + setMessage({ + state: true, + value: { severity: 'error', summary: 'Катаа', detail: 'ПОвтор' } + }); // messege - Ошибка при авторизации + }; -
-
- setChecked(e.checked ?? false)} className="mr-2"> - -
- - Forgot password? - -
- + return ( +
+ +
+
+ +
+ +
+
+
+ + + {errors.email && {errors.email.message}} +
+
+ } + /> + {errors.password && {errors.password.message}}
-
+ + +
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..f25916a6 --- /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..850eb2b0 --- /dev/null +++ b/app/(main)/course/lessons/[lessons]/page.tsx @@ -0,0 +1,266 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { TabView, TabPanel } from 'primereact/tabview'; +import CKEditorWrapper from '@/app/components/CKEditorWrapper.tsx'; +import { Button } from 'primereact/button'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { lessonSchema } from '@/schemas/lessonSchema'; +import { InputText } from 'primereact/inputtext'; +import FancyLinkBtn from '@/app/components/buttons/FancyLinkBtn'; +import { LoginType } from '@/types/login'; +import useTypingEffect from '@/hooks/useTypingEffect'; +import Test from '@/app/components/Test'; +import { FileUpload } from 'primereact/fileupload'; +import PrototypeCard from '@/app/components/cards/PrototypeCard'; +import LessonCard from '@/app/components/cards/LessonCard'; +import Tiered from '@/app/components/popUp/Tiered'; +import { Menu } from 'primereact/menu'; +import Redacting from '@/app/components/popUp/Redacting'; + +export default function Lesson() { + const stepperRef = useRef(null); + + const [activeIndex, setActiveIndex] = useState(0); + const [contentShow, setContentShow] = useState(true); + const [textShow, setTextShow] = useState(false); + const [videoLink, setVideoLink] = useState(''); + const [usefulLink, setUsefullLink] = useState(''); + + // for typing effects + const [videoTyping, setVideoTyping] = useState(true); + const [linkTyping, setLinkTyping] = useState(true); + const [docTyping, setDocTyping] = useState(true); + + const handleTabChange = (e) => { + // console.log('Переход на шаг:', e); + // // fetchDataForStep(e.index); + setActiveIndex(e.index); + }; + + const { + register, + handleSubmit, + setValue, + trigger, + formState: { errors } + } = useForm({ + resolver: yupResolver(lessonSchema), + mode: 'onChange' + }); + + const handleVideoChange = (value: string) => { + console.log(value); + + setVideoLink(value); + setValue('videoReq', value, { shouldValidate: true }); // ✅ обновляем форму и запускаем валидацию + }; + + const addVideo = async () => { + console.log('hi'); + }; + + const videoTyped = useTypingEffect('lreomlroemlfjslj sdlfkjlksdjk ksjdf l', videoTyping); + const linkTyped = useTypingEffect('lreomlroemlfjslj sdlfkjlksdjk ksjdf lldfd', linkTyping); + const docTyped = useTypingEffect('lreo lfjs djf lks sdjf lsdfk sdjfks ', docTyping); + + useEffect(() => { + switch (activeIndex) { + case 3: + setVideoTyping(true); // включить эффект + break; + case 2: + setLinkTyping(true); + break; + case 1: + console.log(activeIndex); + setDocTyping(true); + break; + default: + setLinkTyping(false); + setDocTyping(false); + setVideoTyping(false); // выключить при уходе + } + }, [activeIndex]); + + const redactor = [ + { + label: '', + icon: 'pi pi-pencil', + command: () => { + alert('redactor'); + } + }, + { + label: '', + icon: 'pi pi-user', + command: () => { + alert('delete'); + } + } + ]; + + return ( +
+ handleTabChange(e)} + activeIndex={activeIndex} + className="" + pt={{ + nav: { className: 'flex flex-wrap justify-around' }, + panelContainer: { className: 'flex-1 pl-4' } + }} + > + {/* CKEDITOR */} + + {contentShow && + (!textShow ? ( +
+
+
+
+ + Текст +
+ + + {/* */} +
+
+ + xx-xx-xx +
+
+
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum, distinctio mollitia fuga aperiam quod cumque totam nemo nam? Pariatur earum molestiae, itaque ullam veritatis maiores ipsam quaerat fuga voluptatem + temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum, distinctio mollitia fuga aperiam quod cumque totam nemo nam? Pariatur earum molestiae, itaque ullam veritatis maiores ipsam quaerat + fuga voluptatem temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum, distinctio mollitia fuga aperiam quod cumque totam nemo nam? Pariatur earum molestiae, itaque ullam veritatis maiores + ipsam quaerat fuga voluptatem temporibus. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum, distinctio mollitia fuga aperiam quod cumque totam nemo nam? Pariatur earum molestiae, itaque ullam + veritatis maiores ipsam quaerat fuga voluptatem temporibus. + +
+
+ ) : ( +
+ +
+ ))} +
+ + {/* DOC */} + + {contentShow && ( +
+
+
+ + {docTyping ? docTyped : ''} +
+
+ +
+
+
+ {/* */} + +
+
+ )} +
+ + {/* USEFUL LINKS */} + + {contentShow && ( +
+
+
+ setLinkTyping(false)} + onChange={(e) => { + setUsefullLink(e.target.value); + setValue('usefulLink', e.target.value, { shouldValidate: true }); + }} + placeholder="https://..." + className="w-full p-2 sm:p-3" + /> + {errors.usefulLink && {errors.usefulLink.message}} +
+ +
+
+ {/* */} + +
+
+ )} +
+ + {/* VIDEO */} + + {contentShow && ( +
+
+
+ setVideoTyping(false)} + onChange={(e) => { + setVideoLink(e.target.value); + setValue('videoReq', e.target.value, { shouldValidate: true }); + }} + placeholder="https://..." + className="w-full p-2 sm:p-3" + /> + {errors.videoReq && {errors.videoReq.message}} +
+ +
+
+ {/* */} + +
+
+ )} +
+
+
+ ); +} 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..8e1cae2a --- /dev/null +++ b/app/components/CKEditorWrapper.tsx.tsx @@ -0,0 +1,26 @@ +'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 ? ${typedText}`} onClick={()=> setToggleTyping(false)} className='w-[800px] h-[300px]' /> + : setText(e.htmlValue)} className='w-[800px] h-[300px]'/> + } +
+ ); +} 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/Test.tsx b/app/components/Test.tsx new file mode 100644 index 00000000..3fb9ea34 --- /dev/null +++ b/app/components/Test.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function Test({ children }) { + return
{children}
; +} 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..09560689 --- /dev/null +++ b/app/components/buttons/FancyLinkBtn.tsx @@ -0,0 +1,32 @@ +'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/cards/LessonCard.tsx b/app/components/cards/LessonCard.tsx new file mode 100644 index 00000000..936aa38c --- /dev/null +++ b/app/components/cards/LessonCard.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Redacting from '../popUp/Redacting'; + +export default function LessonCard({ cardValue, cardBg, typeColor, type, lessonDate }) { + const redactor = [ + { + label: '', + icon: 'pi pi-pencil', + command: () => { + alert('redactor'); + } + }, + { + label: '', + icon: 'pi pi-user', + command: () => { + alert('delete'); + } + } + ]; + + return ( +
+
+
+ + {type.typeValue} +
+ + {/* */} +
+
+
+
+ + {lessonDate} +
+
+
{cardValue}
+
+
+ ); +} diff --git a/app/components/cards/PrototypeCard.tsx b/app/components/cards/PrototypeCard.tsx new file mode 100644 index 00000000..994ea944 --- /dev/null +++ b/app/components/cards/PrototypeCard.tsx @@ -0,0 +1,21 @@ +import { Skeleton } from 'primereact/skeleton'; +import React from 'react'; +import MySkeleton from '../skeleton/MySkeleton'; + +export default function PrototypeCard({ value }: { value: string }) { + + return ( +
+
+ + +
+
+
+ +
+
{value ? value : }
+
+
+ ); +} diff --git a/app/components/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/Redacting.tsx b/app/components/popUp/Redacting.tsx new file mode 100644 index 00000000..1264c0f9 --- /dev/null +++ b/app/components/popUp/Redacting.tsx @@ -0,0 +1,38 @@ +import { Menu } from 'primereact/menu'; +import React, { useRef } from 'react'; + +export default function Redacting({ redactor, textSize }) { + const menuLeft = useRef(null); + + return ( +
+ menuLeft.current.toggle(event)} aria-controls="popup_menu_left" aria-haspopup /> + +
+ ); +} diff --git a/app/components/popUp/Tiered.tsx b/app/components/popUp/Tiered.tsx new file mode 100644 index 00000000..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/components/skeleton/MySkeleton.tsx b/app/components/skeleton/MySkeleton.tsx new file mode 100644 index 00000000..751a781a --- /dev/null +++ b/app/components/skeleton/MySkeleton.tsx @@ -0,0 +1,6 @@ +import React, { useState } from 'react'; +import { Skeleton } from 'primereact/skeleton'; + +export default function MySkeleton({ size }:{size:{width:string,height:string}}) { + return ; +} diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index a614a31e..00000000 Binary files a/app/favicon.ico and /dev/null differ diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 00000000..b15a3e24 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,375 @@ +@import 'primeflex/primeflex.css'; +@import 'tailwindcss/index.css'; + +:root { + --bodyFonts: "Jost", sans-serif; + --mainColor: #08A9E6; + --redColor: #EC272F; + --redBgColor: #EC272F1A; + --titleColor: #21225F; + --bodyColor: #555555; + --whiteColor: #ffffff; + --borderBottomColor: #e5e7eb; + --fontSize: 16px; + --transition: 0.5s; +} + +p { + color: var(--bodyColor); + margin-bottom: 10px; +} +p:last-child { + margin-bottom: 0; +} + +a { + display: inline-block; + transition: var(--transition); + text-decoration: none; +} +a:hover, a:focus { + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + color: var(--titleColor); +} + +.mainColor-hover:hover{ + color: #08A9E6; +} + +/* animate */ +.animateContent{ + animation: content linear 6s infinite; +} + +@keyframes content { + 10%{ + transform: translateX(2px); + } + 50%{ + transform: translateX(-4px); + } + 80%{ + transform: translateY(-2px); + } + 100%{ + transform: translateY(4px); + } +} + +.animateFaster{ + animation: faster linear 4s infinite forwards; +} + +@keyframes faster { + 10%{ + transform: translateX(-2px); + } + 50%{ + transform: translateX(4px); + } + 80%{ + transform: translateY(2px); + } + 100%{ + transform: translateY(-4px); + } +} + +.user-img { + position: relative; + margin-bottom: 30px; +} +.user-img img { + /* border: solid .5px ; */ + box-shadow: 0px 1px 9px 4px; + animation: border-transform 10s linear infinite alternate forwards; +} + +@keyframes border-transform { + 0%, 100% { + border-radius: 60% 40% 56% 33%/73% 82% 18% 27%; + } + 14% { + border-radius: 40% 60% 54% 46%/49% 60% 40% 51%; + } + 28% { + border-radius: 54% 46% 38% 62%/49% 70% 30% 51%; + } + 42% { + border-radius: 61% 39% 55% 45%/61% 38% 62% 39%; + } + 56% { + border-radius: 61% 39% 67% 33%/70% 50% 50% 30%; + } + 70% { + border-radius: 50% 50% 34% 66%/56% 68% 32% 44%; + } + 84% { + border-radius: 46% 54% 50% 50%/35% 61% 39% 65%; + } +} + +#preloader { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 9999; +} + +#preloader-area { + position: absolute; + left: 50%; + top: 50%; + height: 60px; + width: 60px; +} +#preloader-area .spinner { + position: absolute; + z-index: 9999; + width: 6px; + height: 90px; + margin-top: -45px; + border-radius: 10px; + background-color: var(--whiteColor); + animation: rotate40deg 0.8s infinite; + animation-direction: alternate-reverse; +} +#preloader-area .spinner:nth-child(1) { + margin-left: 0px; +} +#preloader-area .spinner:nth-child(2) { + margin-left: -14px; + animation-delay: 0.1s; +} +#preloader-area .spinner:nth-child(3) { + margin-left: -28px; + animation-delay: 0.2s; +} +#preloader-area .spinner:nth-child(4) { + margin-left: -42px; + animation-delay: 0.3s; +} +#preloader-area .spinner:nth-child(5) { + margin-left: -56px; + animation-delay: 0.4s; +} +#preloader-area .spinner:nth-child(6) { + margin-left: -70px; + animation-delay: 0.5s; +} +#preloader-area .spinner:nth-child(7) { + margin-left: -84px; + animation-delay: 0.6s; +} +#preloader-area .spinner:nth-child(8) { + margin-left: -98px; + animation-delay: 0.7s; +} + +#preloader .preloader-section { + position: fixed; + top: 0; + width: 51%; + height: 100%; + background-color: var(--mainColor); + z-index: 999; + transform: translateX(0); +} +#preloader .preloader-section.preloader-left { + left: 0; +} +#preloader .preloader-section.preloader-right { + right: 0; +} + +.p-password .p-password-toggle-icon { + top: 50% !important; + transform: translateY(-50%); +} + + +.loaded #preloader-area { + opacity: 0; + visibility: hidden; + transition: all 0.3s ease-out; +} +.loaded #preloader .preloader-section.preloader-left { + transform: translateX(-100%); + transition: all 0.8s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); +} +.loaded #preloader .preloader-section.preloader-right { + transform: translateX(100%); + transition: all 0.8s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); +} + +.loaded #preloader { + visibility: hidden; + transform: translateY(-100%); + transition: all 0.3s 1s ease-out; +} + +@keyframes rotate40deg { + 0% { + height: 5px; + margin-top: 0; + transform: rotate(40deg); + } + 100% { + height: 90px; + transform: rotate(0deg); + } +} +@keyframes ripple { + 0% { + transform: scale(1); + } + 75% { + transform: scale(1.5); + opacity: 1; + } + 100% { + transform: scale(1.75); + opacity: 0; + } +} +@keyframes moveleftbounce { + 0% { + transform: translateX(0px); + } + 50% { + transform: translateX(20px); + } + 100% { + transform: translateX(0px); + } +} +@keyframes fadeInUp { + 0% { + opacity: 0; + transform: translateY(20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +@keyframes fadeInDown { + 0% { + opacity: 0; + transform: translateY(-20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +@keyframes movebounce { + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(15px); + } + 100% { + transform: translateY(0); + } +} +@keyframes rotate-in { + 0% { + transform: perspective(120px) rotateX(0deg) rotateY(0deg); + } + 50% { + transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); + } + 100% { + transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); + } +} +@keyframes rotated360 { + 0% { + transform: rotateZ(0deg); + } + 100% { + transform: rotateZ(-360deg); + } +} + +/* primeReact components */ +.popup_menu_left{ + width: 300px; + display: none; +} + +.p-menuitem-icon{ + margin: 0; +} + +/* prototype effect */ +.shadowAnimate { + box-shadow: 1px 1px 6px 1px ; + transition: all 2s; + animation: shadowSkale 2s ease-in infinite; +} + +@keyframes shadowSkale { + 0% { + box-shadow: 1px 1px 6px 1px ; + } + 50% { + box-shadow: 1px 1px 6px 2px ; + } + 100%{ + box-shadow: 1px 1px 6px 1px ; + } +} + +.my-custom-table td, +.my-custom-table th { + border: 1px solid #ccc; + padding: 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; + /* color: rgb(221, 196, 245) !important; */ +} + +.tab-custom-text-3{ + color: green !important; +} + +.tab-custom-text-4{ + color: var(--redColor) !important; +} + +.poka{ + background-color: rgb(211, 232, 202) !important; + /* background-color: rgb(243, 145, 145) !important; */ +} \ 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..a5dac639 --- /dev/null +++ b/hooks/useTypingEffect.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; + +export default function useTypingEffect(word: string, stop: boolean) { + const [typedText, setTypedText] = useState(''); + const stopRef = useRef(stop); + const indexRef = useRef(0); + const intervalRef = useRef(null); + + useEffect(() => { + stopRef.current = stop; + + if (!stop) { + if (intervalRef.current) clearInterval(intervalRef.current); + setTypedText(''); + indexRef.current = 0; + return; + } + + intervalRef.current = setInterval(() => { + if (indexRef.current >= word.length) { + clearInterval(intervalRef.current!); + return; + } + + setTypedText((prev) => { + const nextChar = word[indexRef.current]; + indexRef.current++; + const nextText = prev + nextChar; + + if (nextText.length > 50) { + clearInterval(intervalRef.current!); + alert('baby'); + return ''; + } + + return nextText; + }); + }, 60); + + return () => clearInterval(intervalRef.current!); + }, [stop, word]); + + return typedText +} \ No newline at end of file diff --git a/layout/AppFooter.tsx b/layout/AppFooter.tsx index 424b9987..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..f2d66700 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.1.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.58.1", + "typescript": "5.1.3", + "yup": "^1.6.1" }, "devDependencies": { + "@tailwindcss/postcss": "^4.1.8", + "autoprefixer": "^10.4.21", "eslint": "8.43.0", "eslint-config-next": "13.4.6", + "postcss": "^8.5.4", "prettier": "^2.8.8", - "sass": "^1.63.4" + "sass": "^1.63.4", + "tailwindcss": "^4.1.8" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -36,6 +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.1.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz", + "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -136,6 +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.58.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.58.1.tgz", + "integrity": "sha512-Lml/KZYEEFfPhUVgE0RdCVpnC4yhW+PndRhbiTtdvSlQTL8IfVR+iQkBjLIvmmc6+GGoVeM11z37ktKFPAb0FA==", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3414,9 +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..4537b719 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.1.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.58.1", + "typescript": "5.1.3", + "yup": "^1.6.1" }, "devDependencies": { + "@tailwindcss/postcss": "^4.1.8", + "autoprefixer": "^10.4.21", "eslint": "8.43.0", "eslint-config-next": "13.4.6", + "postcss": "^8.5.4", "prettier": "^2.8.8", - "sass": "^1.63.4" + "sass": "^1.63.4", + "tailwindcss": "^4.1.8" } -} \ No newline at end of file +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..8f3250b7 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + '@tailwindcss/postcss': {}, // Используем новый плагин + autoprefixer: {}, + } +} \ No newline at end of file diff --git a/public/layout/images/home-banner-phone.png b/public/layout/images/home-banner-phone.png new file mode 100644 index 00000000..399a19dc Binary files /dev/null and b/public/layout/images/home-banner-phone.png differ diff --git a/public/layout/images/logo-kg.svg b/public/layout/images/logo-kg.svg new file mode 100644 index 00000000..ac33ea12 --- /dev/null +++ b/public/layout/images/logo-kg.svg @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + В + + + + + Р + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/layout/images/logo-remove-white.png b/public/layout/images/logo-remove-white.png new file mode 100644 index 00000000..3d28f7d5 Binary files /dev/null and b/public/layout/images/logo-remove-white.png differ diff --git a/public/layout/images/logo-remove.png b/public/layout/images/logo-remove.png new file mode 100644 index 00000000..929f087a Binary files /dev/null and b/public/layout/images/logo-remove.png differ diff --git a/public/layout/images/man.png b/public/layout/images/man.png new file mode 100644 index 00000000..0168ee16 Binary files /dev/null and b/public/layout/images/man.png differ diff --git a/public/layout/images/no-image.png b/public/layout/images/no-image.png new file mode 100644 index 00000000..3f257212 Binary files /dev/null and b/public/layout/images/no-image.png differ diff --git a/public/layout/images/shape.png b/public/layout/images/shape.png new file mode 100644 index 00000000..a82ce023 Binary files /dev/null and b/public/layout/images/shape.png differ diff --git a/public/layout/images/shape1.png b/public/layout/images/shape1.png new file mode 100644 index 00000000..bee24c65 Binary files /dev/null and b/public/layout/images/shape1.png differ diff --git a/public/layout/images/shape2.png b/public/layout/images/shape2.png new file mode 100644 index 00000000..f1fe1c5b Binary files /dev/null and b/public/layout/images/shape2.png differ diff --git a/public/layout/svg/red-logo.svg b/public/layout/svg/red-logo.svg new file mode 100644 index 00000000..69deed08 --- /dev/null +++ b/public/layout/svg/red-logo.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/layout/svg/white-logo.svg b/public/layout/svg/white-logo.svg new file mode 100644 index 00000000..d76c8ff6 --- /dev/null +++ b/public/layout/svg/white-logo.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/authSchema.tsx b/schemas/authSchema.tsx new file mode 100644 index 00000000..6d8159a5 --- /dev/null +++ b/schemas/authSchema.tsx @@ -0,0 +1,22 @@ +import * as yup from 'yup'; + +export const schema = yup.object().shape({ + email: yup.string() + .email('email туура эмес форматта') + .required('email талап кылынат!') + .matches( + /^[a-zA-Z0-9._%+-]+@oshsu\.kg$/, + 'email "@oshsu.kg" форматында болуш керек' + ) + .matches( + /^[a-zA-Z0-9._%+-]+@oshsu\.kg$/, // Стандартный формат email (только буквы, цифры, точки, подчеркивания и плюсы) + 'email "@oshsu.kg" форматында жана тыюу салынган символдор камтылбашы керек' + ), + password: yup.string() + .required('Сырсөз талап кылынат!') + .max(20, 'Сырсөз эң көп дегенде 20 белгиден турушу керек') + .matches( + /^[^!@#$%^&*()_+={}[\]:;"'`<>,.?/\\|]*$/, + 'Сырсөздө тыюу салынган символдор камтылбашы керек' + ), +}); \ No newline at end of file diff --git a/schemas/lessonSchema.tsx b/schemas/lessonSchema.tsx new file mode 100644 index 00000000..256a7c01 --- /dev/null +++ b/schemas/lessonSchema.tsx @@ -0,0 +1,16 @@ +import * as yup from 'yup'; + +export const lessonSchema = yup.object().shape({ + videoReq: yup.string() + .required('Талап кылынат!') + .matches( + /^https?:\/\/.+/, + 'Видео шилтеме "http://" "https://" форматында болуш керек' + ), + usefulLink: yup.string() + .required('Талап кылынат!') + .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;'; +};