mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
Merge pull request #476 from learnhouse/feat/more-ux-updates
More ux updates
This commit is contained in:
commit
bf94855d0c
10 changed files with 1147 additions and 413 deletions
|
|
@ -60,7 +60,11 @@ async def authorization_verify_if_user_is_author(
|
||||||
element_uuid: str,
|
element_uuid: str,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
if action == "update" or "delete" or "read":
|
# For create action, we don't need to check existing resource
|
||||||
|
if action == "create":
|
||||||
|
return True # Allow creation if user is authenticated
|
||||||
|
|
||||||
|
if action in ["update", "delete", "read"]:
|
||||||
statement = select(ResourceAuthor).where(
|
statement = select(ResourceAuthor).where(
|
||||||
ResourceAuthor.resource_uuid == element_uuid
|
ResourceAuthor.resource_uuid == element_uuid
|
||||||
)
|
)
|
||||||
|
|
@ -79,6 +83,7 @@ async def authorization_verify_if_user_is_author(
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Tested and working
|
# Tested and working
|
||||||
|
|
@ -101,16 +106,16 @@ async def authorization_verify_based_on_roles(
|
||||||
|
|
||||||
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
|
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
|
||||||
|
|
||||||
# Find in roles list if there is a role that matches users action for this type of element
|
# Check all roles until we find one that grants the permission
|
||||||
for role in user_roles_in_organization_and_standard_roles:
|
for role in user_roles_in_organization_and_standard_roles:
|
||||||
role = Role.model_validate(role)
|
role = Role.model_validate(role)
|
||||||
if role.rights:
|
if role.rights:
|
||||||
rights = role.rights
|
rights = role.rights
|
||||||
if rights[element_type][f"action_{action}"] is True:
|
element_rights = getattr(rights, element_type, None)
|
||||||
|
if element_rights and getattr(element_rights, f"action_{action}", False):
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
# If we get here, no role granted the permission
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -133,12 +138,12 @@ async def authorization_verify_based_on_org_admin_status(
|
||||||
|
|
||||||
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
|
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
|
||||||
|
|
||||||
# Find in roles list if there is a role that matches users action for this type of element
|
# Check if user has admin role (role_id 1 or 2) in any organization
|
||||||
for role in user_roles_in_organization_and_standard_roles:
|
for role in user_roles_in_organization_and_standard_roles:
|
||||||
role = Role.model_validate(role)
|
role = Role.model_validate(role)
|
||||||
if role.id == 1 or role.id == 2:
|
if role.id in [1, 2]: # Assuming 1 and 2 are admin role IDs
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import Link from 'next/link'
|
||||||
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
|
||||||
import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva'
|
import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva'
|
||||||
import VideoActivity from '@components/Objects/Activities/Video/Video'
|
import VideoActivity from '@components/Objects/Activities/Video/Video'
|
||||||
import { BookOpenCheck, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, FileText, Folder, List, Menu, MoreVertical, UserRoundPen, Video, Layers, ListFilter, ListTree, X, Edit2 } from 'lucide-react'
|
import { BookOpenCheck, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, FileText, Folder, List, Menu, MoreVertical, UserRoundPen, Video, Layers, ListFilter, ListTree, X, Edit2, EllipsisVertical, Maximize2, Minimize2 } from 'lucide-react'
|
||||||
import { markActivityAsComplete, unmarkActivityAsComplete } from '@services/courses/activity'
|
import { markActivityAsComplete, unmarkActivityAsComplete } from '@services/courses/activity'
|
||||||
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
|
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
|
||||||
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
|
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
|
||||||
|
|
@ -16,7 +16,7 @@ import { CourseProvider } from '@components/Contexts/CourseContext'
|
||||||
import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk'
|
import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk'
|
||||||
import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext'
|
import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
import { getAssignmentFromActivityUUID, getFinalGrade, submitAssignmentForGrading } from '@services/courses/assignments'
|
import { getAssignmentFromActivityUUID, getFinalGrade, submitAssignmentForGrading } from '@services/courses/assignments'
|
||||||
import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity'
|
import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity'
|
||||||
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
|
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
|
||||||
|
|
@ -33,6 +33,7 @@ import ActivityNavigation from '@components/Pages/Activity/ActivityNavigation'
|
||||||
import ActivityChapterDropdown from '@components/Pages/Activity/ActivityChapterDropdown'
|
import ActivityChapterDropdown from '@components/Pages/Activity/ActivityChapterDropdown'
|
||||||
import FixedActivitySecondaryBar from '@components/Pages/Activity/FixedActivitySecondaryBar'
|
import FixedActivitySecondaryBar from '@components/Pages/Activity/FixedActivitySecondaryBar'
|
||||||
import CourseEndView from '@components/Pages/Activity/CourseEndView'
|
import CourseEndView from '@components/Pages/Activity/CourseEndView'
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
interface ActivityClientProps {
|
interface ActivityClientProps {
|
||||||
activityid: string
|
activityid: string
|
||||||
|
|
@ -42,6 +43,55 @@ interface ActivityClientProps {
|
||||||
course: any
|
course: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ActivityActionsProps {
|
||||||
|
activity: any
|
||||||
|
activityid: string
|
||||||
|
course: any
|
||||||
|
orgslug: string
|
||||||
|
assignment: any
|
||||||
|
showNavigation?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActivityActions({ activity, activityid, course, orgslug, assignment, showNavigation = true }: ActivityActionsProps) {
|
||||||
|
const session = useLHSession() as any;
|
||||||
|
const { contributorStatus } = useContributorStatus(course.course_uuid);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex space-x-2 items-center">
|
||||||
|
{activity && activity.published == true && activity.content.paid_access != false && (
|
||||||
|
<AuthenticatedClientElement checkMethod="authentication">
|
||||||
|
{activity.activity_type != 'TYPE_ASSIGNMENT' && (
|
||||||
|
<>
|
||||||
|
<MarkStatus
|
||||||
|
activity={activity}
|
||||||
|
activityid={activityid}
|
||||||
|
course={course}
|
||||||
|
orgslug={orgslug}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{activity.activity_type == 'TYPE_ASSIGNMENT' && (
|
||||||
|
<>
|
||||||
|
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
|
||||||
|
<AssignmentTools
|
||||||
|
assignment={assignment}
|
||||||
|
activity={activity}
|
||||||
|
activityid={activityid}
|
||||||
|
course={course}
|
||||||
|
orgslug={orgslug}
|
||||||
|
/>
|
||||||
|
</AssignmentSubmissionProvider>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{showNavigation && (
|
||||||
|
<NextActivityButton course={course} currentActivityId={activity.id} orgslug={orgslug} />
|
||||||
|
)}
|
||||||
|
</AuthenticatedClientElement>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ActivityClient(props: ActivityClientProps) {
|
function ActivityClient(props: ActivityClientProps) {
|
||||||
const activityid = props.activityid
|
const activityid = props.activityid
|
||||||
const courseuuid = props.courseuuid
|
const courseuuid = props.courseuuid
|
||||||
|
|
@ -55,8 +105,69 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
const [bgColor, setBgColor] = React.useState('bg-white')
|
const [bgColor, setBgColor] = React.useState('bg-white')
|
||||||
const [assignment, setAssignment] = React.useState(null) as any;
|
const [assignment, setAssignment] = React.useState(null) as any;
|
||||||
const [markStatusButtonActive, setMarkStatusButtonActive] = React.useState(false);
|
const [markStatusButtonActive, setMarkStatusButtonActive] = React.useState(false);
|
||||||
|
const [isFocusMode, setIsFocusMode] = React.useState(false);
|
||||||
|
const isInitialRender = useRef(true);
|
||||||
const { contributorStatus } = useContributorStatus(courseuuid);
|
const { contributorStatus } = useContributorStatus(courseuuid);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// Function to find the current activity's position in the course
|
||||||
|
const findActivityPosition = () => {
|
||||||
|
let allActivities: any[] = [];
|
||||||
|
let currentIndex = -1;
|
||||||
|
|
||||||
|
// Flatten all activities from all chapters
|
||||||
|
course.chapters.forEach((chapter: any) => {
|
||||||
|
chapter.activities.forEach((activity: any) => {
|
||||||
|
const cleanActivityUuid = activity.activity_uuid?.replace('activity_', '');
|
||||||
|
allActivities.push({
|
||||||
|
...activity,
|
||||||
|
cleanUuid: cleanActivityUuid,
|
||||||
|
chapterName: chapter.name
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if this is the current activity
|
||||||
|
if (cleanActivityUuid === activityid.replace('activity_', '')) {
|
||||||
|
currentIndex = allActivities.length - 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return { allActivities, currentIndex };
|
||||||
|
};
|
||||||
|
|
||||||
|
const { allActivities, currentIndex } = findActivityPosition();
|
||||||
|
|
||||||
|
// Get previous and next activities
|
||||||
|
const prevActivity = currentIndex > 0 ? allActivities[currentIndex - 1] : null;
|
||||||
|
const nextActivity = currentIndex < allActivities.length - 1 ? allActivities[currentIndex + 1] : null;
|
||||||
|
|
||||||
|
// Navigate to an activity
|
||||||
|
const navigateToActivity = (activity: any) => {
|
||||||
|
if (!activity) return;
|
||||||
|
|
||||||
|
const cleanCourseUuid = course.course_uuid?.replace('course_', '');
|
||||||
|
router.push(getUriWithOrg(orgslug, '') + `/course/${cleanCourseUuid}/activity/${activity.cleanUuid}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize focus mode from localStorage
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const saved = localStorage.getItem('globalFocusMode');
|
||||||
|
setIsFocusMode(saved === 'true');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Save focus mode to localStorage
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
localStorage.setItem('globalFocusMode', isFocusMode.toString());
|
||||||
|
// Dispatch custom event for focus mode change
|
||||||
|
window.dispatchEvent(new CustomEvent('focusModeChange', {
|
||||||
|
detail: { isFocusMode }
|
||||||
|
}));
|
||||||
|
isInitialRender.current = false;
|
||||||
|
}
|
||||||
|
}, [isFocusMode]);
|
||||||
|
|
||||||
function getChapterNameByActivityId(course: any, activity_id: any) {
|
function getChapterNameByActivityId(course: any, activity_id: any) {
|
||||||
for (let i = 0; i < course.chapters.length; i++) {
|
for (let i = 0; i < course.chapters.length; i++) {
|
||||||
|
|
@ -78,24 +189,251 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activity.activity_type == 'TYPE_DYNAMIC') {
|
if (activity.activity_type == 'TYPE_DYNAMIC') {
|
||||||
setBgColor('bg-white nice-shadow');
|
setBgColor(isFocusMode ? 'bg-white' : 'bg-white nice-shadow');
|
||||||
}
|
}
|
||||||
else if (activity.activity_type == 'TYPE_ASSIGNMENT') {
|
else if (activity.activity_type == 'TYPE_ASSIGNMENT') {
|
||||||
setMarkStatusButtonActive(false);
|
setMarkStatusButtonActive(false);
|
||||||
setBgColor('bg-white nice-shadow');
|
setBgColor(isFocusMode ? 'bg-white' : 'bg-white nice-shadow');
|
||||||
getAssignmentUI();
|
getAssignmentUI();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setBgColor('bg-zinc-950');
|
setBgColor(isFocusMode ? 'bg-zinc-950' : 'bg-zinc-950 nice-shadow');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
, [activity, pathname])
|
, [activity, pathname, isFocusMode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CourseProvider courseuuid={course?.course_uuid}>
|
<CourseProvider courseuuid={course?.course_uuid}>
|
||||||
<AIChatBotProvider>
|
<AIChatBotProvider>
|
||||||
|
{isFocusMode ? (
|
||||||
|
<AnimatePresence>
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="fixed inset-0 bg-white z-50"
|
||||||
|
>
|
||||||
|
{/* Focus Mode Top Bar */}
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { y: -100 }}
|
||||||
|
animate={{ y: 0 }}
|
||||||
|
exit={{ y: -100 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="fixed top-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-xl border-b border-gray-100"
|
||||||
|
>
|
||||||
|
<div className="container mx-auto px-4 py-2">
|
||||||
|
<div className="flex items-center justify-between h-14">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
onClick={() => setIsFocusMode(false)}
|
||||||
|
className="bg-white nice-shadow p-2 rounded-full cursor-pointer hover:bg-gray-50"
|
||||||
|
title="Exit focus mode"
|
||||||
|
>
|
||||||
|
<Minimize2 size={16} className="text-gray-700" />
|
||||||
|
</motion.button>
|
||||||
|
<ActivityChapterDropdown
|
||||||
|
course={course}
|
||||||
|
currentActivityId={activity.activity_uuid ? activity.activity_uuid.replace('activity_', '') : activityid.replace('activity_', '')}
|
||||||
|
orgslug={orgslug}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Center Course Info */}
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { opacity: 0, y: -20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1 }}
|
||||||
|
className="flex items-center space-x-4"
|
||||||
|
>
|
||||||
|
<div className="flex">
|
||||||
|
<Link
|
||||||
|
href={getUriWithOrg(orgslug, '') + `/course/${courseuuid}`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="w-[60px] h-[34px] rounded-md drop-shadow-md"
|
||||||
|
src={`${getCourseThumbnailMediaDirectory(
|
||||||
|
org?.org_uuid,
|
||||||
|
course.course_uuid,
|
||||||
|
course.thumbnail_image
|
||||||
|
)}`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col -space-y-1">
|
||||||
|
<p className="font-bold text-gray-700 text-sm">Course </p>
|
||||||
|
<h1 className="font-bold text-gray-950 text-lg first-letter:uppercase">
|
||||||
|
{course.name}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Progress Indicator */}
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { opacity: 0, x: 20 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ delay: 0.2 }}
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<div className="relative w-8 h-8">
|
||||||
|
<svg className="w-full h-full transform -rotate-90">
|
||||||
|
<circle
|
||||||
|
cx="16"
|
||||||
|
cy="16"
|
||||||
|
r="14"
|
||||||
|
stroke="#e5e7eb"
|
||||||
|
strokeWidth="3"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="16"
|
||||||
|
cy="16"
|
||||||
|
r="14"
|
||||||
|
stroke="#10b981"
|
||||||
|
strokeWidth="3"
|
||||||
|
fill="none"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeDasharray={2 * Math.PI * 14}
|
||||||
|
strokeDashoffset={2 * Math.PI * 14 * (1 - (course.trail?.runs?.find((run: any) => run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0) / (course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 1))}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<span className="text-xs font-bold text-gray-800">
|
||||||
|
{Math.round(((course.trail?.runs?.find((run: any) => run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0) / (course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 1)) * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-600">
|
||||||
|
{course.trail?.runs?.find((run: any) => run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0} of {course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 0}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Focus Mode Content */}
|
||||||
|
<div className="pt-16 pb-20 h-full overflow-auto">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
{activity && activity.published == true && (
|
||||||
|
<>
|
||||||
|
{activity.content.paid_access == false ? (
|
||||||
|
<PaidCourseActivityDisclaimer course={course} />
|
||||||
|
) : (
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { scale: 0.95, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
transition={{ delay: 0.3 }}
|
||||||
|
className={`p-7 rounded-lg ${bgColor} mt-4`}
|
||||||
|
>
|
||||||
|
{/* Activity Types */}
|
||||||
|
<div>
|
||||||
|
{activity.activity_type == 'TYPE_DYNAMIC' && (
|
||||||
|
<Canva content={activity.content} activity={activity} />
|
||||||
|
)}
|
||||||
|
{activity.activity_type == 'TYPE_VIDEO' && (
|
||||||
|
<VideoActivity course={course} activity={activity} />
|
||||||
|
)}
|
||||||
|
{activity.activity_type == 'TYPE_DOCUMENT' && (
|
||||||
|
<DocumentPdfActivity
|
||||||
|
course={course}
|
||||||
|
activity={activity}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{activity.activity_type == 'TYPE_ASSIGNMENT' && (
|
||||||
|
<div>
|
||||||
|
{assignment ? (
|
||||||
|
<AssignmentProvider assignment_uuid={assignment?.assignment_uuid}>
|
||||||
|
<AssignmentsTaskProvider>
|
||||||
|
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
|
||||||
|
<AssignmentStudentActivity />
|
||||||
|
</AssignmentSubmissionProvider>
|
||||||
|
</AssignmentsTaskProvider>
|
||||||
|
</AssignmentProvider>
|
||||||
|
) : (
|
||||||
|
<div></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Focus Mode Bottom Bar */}
|
||||||
|
{activity && activity.published == true && activity.content.paid_access != false && (
|
||||||
|
<motion.div
|
||||||
|
initial={isInitialRender.current ? false : { y: 100 }}
|
||||||
|
animate={{ y: 0 }}
|
||||||
|
exit={{ y: 100 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="fixed bottom-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-xl border-t border-gray-100"
|
||||||
|
>
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="flex items-center justify-between h-16">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<button
|
||||||
|
onClick={() => navigateToActivity(prevActivity)}
|
||||||
|
className={`flex items-center space-x-1.5 p-2 rounded-md transition-all duration-200 cursor-pointer ${
|
||||||
|
prevActivity
|
||||||
|
? 'text-gray-700'
|
||||||
|
: 'opacity-50 text-gray-400 cursor-not-allowed'
|
||||||
|
}`}
|
||||||
|
disabled={!prevActivity}
|
||||||
|
title={prevActivity ? `Previous: ${prevActivity.name}` : 'No previous activity'}
|
||||||
|
>
|
||||||
|
<ChevronLeft size={20} className="text-gray-800 shrink-0" />
|
||||||
|
<div className="flex flex-col items-start">
|
||||||
|
<span className="text-xs text-gray-500">Previous</span>
|
||||||
|
<span className="text-sm capitalize font-semibold text-left">
|
||||||
|
{prevActivity ? prevActivity.name : 'No previous activity'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<ActivityActions
|
||||||
|
activity={activity}
|
||||||
|
activityid={activityid}
|
||||||
|
course={course}
|
||||||
|
orgslug={orgslug}
|
||||||
|
assignment={assignment}
|
||||||
|
showNavigation={false}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => navigateToActivity(nextActivity)}
|
||||||
|
className={`flex items-center space-x-1.5 p-2 rounded-md transition-all duration-200 cursor-pointer ${
|
||||||
|
nextActivity
|
||||||
|
? 'text-gray-700'
|
||||||
|
: 'opacity-50 text-gray-400 cursor-not-allowed'
|
||||||
|
}`}
|
||||||
|
disabled={!nextActivity}
|
||||||
|
title={nextActivity ? `Next: ${nextActivity.name}` : 'No next activity'}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
<span className="text-xs text-gray-500">Next</span>
|
||||||
|
<span className="text-sm capitalize font-semibold text-right">
|
||||||
|
{nextActivity ? nextActivity.name : 'No next activity'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ChevronRight size={20} className="text-gray-800 shrink-0" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
) : (
|
||||||
<GeneralWrapperStyled>
|
<GeneralWrapperStyled>
|
||||||
|
{/* Original non-focus mode UI */}
|
||||||
{activityid === 'end' ? (
|
{activityid === 'end' ? (
|
||||||
<CourseEndView
|
<CourseEndView
|
||||||
courseName={course.name}
|
courseName={course.name}
|
||||||
|
|
@ -131,6 +469,24 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{activity && activity.published == true && activity.content.paid_access != false && (
|
||||||
|
<AuthenticatedClientElement checkMethod="authentication">
|
||||||
|
{ (
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<PreviousActivityButton
|
||||||
|
course={course}
|
||||||
|
currentActivityId={activity.id}
|
||||||
|
orgslug={orgslug}
|
||||||
|
/>
|
||||||
|
<NextActivityButton
|
||||||
|
course={course}
|
||||||
|
currentActivityId={activity.id}
|
||||||
|
orgslug={orgslug}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AuthenticatedClientElement>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ActivityIndicators
|
<ActivityIndicators
|
||||||
|
|
@ -140,8 +496,15 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
course={course}
|
course={course}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center w-full">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex flex-1/3 items-center space-x-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsFocusMode(true)}
|
||||||
|
className="bg-white nice-shadow p-2 rounded-full cursor-pointer hover:bg-gray-50 transition-all duration-200"
|
||||||
|
title="Enter focus mode"
|
||||||
|
>
|
||||||
|
<Maximize2 size={16} className="text-gray-700" />
|
||||||
|
</button>
|
||||||
<ActivityChapterDropdown
|
<ActivityChapterDropdown
|
||||||
course={course}
|
course={course}
|
||||||
currentActivityId={activity.activity_uuid ? activity.activity_uuid.replace('activity_', '') : activityid.replace('activity_', '')}
|
currentActivityId={activity.activity_uuid ? activity.activity_uuid.replace('activity_', '') : activityid.replace('activity_', '')}
|
||||||
|
|
@ -168,30 +531,9 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
className="bg-emerald-600 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out"
|
className="bg-emerald-600 rounded-full px-5 drop-shadow-md flex items-center space-x-2 p-2.5 text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out"
|
||||||
>
|
>
|
||||||
<Edit2 size={17} />
|
<Edit2 size={17} />
|
||||||
<span className="text-xs font-bold">Contribute to Activity</span>
|
<span className="text-xs font-bold">Contribute</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<MoreVertical size={17} className="text-gray-300" />
|
|
||||||
<MarkStatus
|
|
||||||
activity={activity}
|
|
||||||
activityid={activityid}
|
|
||||||
course={course}
|
|
||||||
orgslug={orgslug}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{activity.activity_type == 'TYPE_ASSIGNMENT' && (
|
|
||||||
<>
|
|
||||||
<MoreVertical size={17} className="text-gray-300 " />
|
|
||||||
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
|
|
||||||
<AssignmentTools
|
|
||||||
assignment={assignment}
|
|
||||||
activity={activity}
|
|
||||||
activityid={activityid}
|
|
||||||
course={course}
|
|
||||||
orgslug={orgslug}
|
|
||||||
/>
|
|
||||||
</AssignmentSubmissionProvider>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</AuthenticatedClientElement>
|
</AuthenticatedClientElement>
|
||||||
|
|
@ -199,6 +541,7 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activity && activity.published == false && (
|
{activity && activity.published == false && (
|
||||||
<div className="p-7 drop-shadow-xs rounded-lg bg-gray-800">
|
<div className="p-7 drop-shadow-xs rounded-lg bg-gray-800">
|
||||||
<div className="text-white">
|
<div className="text-white">
|
||||||
|
|
@ -250,6 +593,18 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Activity Actions below the content box */}
|
||||||
|
{activity && activity.published == true && activity.content.paid_access != false && (
|
||||||
|
<div className="flex justify-end mt-4">
|
||||||
|
<ActivityActions
|
||||||
|
activity={activity}
|
||||||
|
activityid={activityid}
|
||||||
|
course={course}
|
||||||
|
orgslug={orgslug}
|
||||||
|
assignment={assignment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Fixed Activity Secondary Bar */}
|
{/* Fixed Activity Secondary Bar */}
|
||||||
{activity && activity.published == true && activity.content.paid_access != false && (
|
{activity && activity.published == true && activity.content.paid_access != false && (
|
||||||
|
|
@ -266,6 +621,7 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</GeneralWrapperStyled>
|
</GeneralWrapperStyled>
|
||||||
|
)}
|
||||||
</AIChatBotProvider>
|
</AIChatBotProvider>
|
||||||
</CourseProvider>
|
</CourseProvider>
|
||||||
</>
|
</>
|
||||||
|
|
@ -415,7 +771,6 @@ export function MarkStatus(props: {
|
||||||
status="warning"
|
status="warning"
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<NextActivityButton course={props.course} currentActivityId={props.activity.id} orgslug={props.orgslug} />
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
@ -437,7 +792,6 @@ export function MarkStatus(props: {
|
||||||
)}{' '}
|
)}{' '}
|
||||||
{!isMobile && <i className="not-italic text-xs font-bold">{isLoading ? 'Marking...' : 'Mark as complete'}</i>}
|
{!isMobile && <i className="not-italic text-xs font-bold">{isLoading ? 'Marking...' : 'Mark as complete'}</i>}
|
||||||
</div>
|
</div>
|
||||||
<NextActivityButton course={props.course} currentActivityId={props.activity.id} orgslug={props.orgslug} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
@ -483,15 +837,66 @@ function NextActivityButton({ course, currentActivityId, orgslug }: { course: an
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolTip content={`Next: ${nextActivity.name}`} side="top">
|
|
||||||
<div
|
<div
|
||||||
onClick={navigateToActivity}
|
onClick={navigateToActivity}
|
||||||
className="bg-gray-300 rounded-full px-5 nice-shadow flex items-center space-x-2 p-2.5 text-gray-600 hover:cursor-pointer transition delay-150 duration-300 ease-in-out"
|
className="bg-white rounded-full px-5 nice-shadow flex items-center space-x-1 p-2.5 text-gray-600 hover:cursor-pointer transition delay-150 duration-300 ease-in-out"
|
||||||
>
|
>
|
||||||
{!isMobile && <span className="text-xs font-bold">Next</span>}
|
<span className="text-xs font-bold text-gray-500">Next</span>
|
||||||
|
<EllipsisVertical className='text-gray-400' size={13} />
|
||||||
|
<span className="text-sm font-semibold truncate max-w-[200px]">{nextActivity.name}</span>
|
||||||
<ChevronRight size={17} />
|
<ChevronRight size={17} />
|
||||||
</div>
|
</div>
|
||||||
</ToolTip>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PreviousActivityButton({ course, currentActivityId, orgslug }: { course: any, currentActivityId: string, orgslug: string }) {
|
||||||
|
const router = useRouter();
|
||||||
|
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||||
|
|
||||||
|
const findPreviousActivity = () => {
|
||||||
|
let allActivities: any[] = [];
|
||||||
|
let currentIndex = -1;
|
||||||
|
|
||||||
|
// Flatten all activities from all chapters
|
||||||
|
course.chapters.forEach((chapter: any) => {
|
||||||
|
chapter.activities.forEach((activity: any) => {
|
||||||
|
const cleanActivityUuid = activity.activity_uuid?.replace('activity_', '');
|
||||||
|
allActivities.push({
|
||||||
|
...activity,
|
||||||
|
cleanUuid: cleanActivityUuid,
|
||||||
|
chapterName: chapter.name
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if this is the current activity
|
||||||
|
if (activity.id === currentActivityId) {
|
||||||
|
currentIndex = allActivities.length - 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get previous activity
|
||||||
|
return currentIndex > 0 ? allActivities[currentIndex - 1] : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const previousActivity = findPreviousActivity();
|
||||||
|
|
||||||
|
if (!previousActivity) return null;
|
||||||
|
|
||||||
|
const navigateToActivity = () => {
|
||||||
|
const cleanCourseUuid = course.course_uuid?.replace('course_', '');
|
||||||
|
router.push(getUriWithOrg(orgslug, '') + `/course/${cleanCourseUuid}/activity/${previousActivity.cleanUuid}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={navigateToActivity}
|
||||||
|
className="bg-white rounded-full px-5 nice-shadow flex items-center space-x-1 p-2.5 text-gray-600 hover:cursor-pointer transition delay-150 duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<ChevronLeft size={17} />
|
||||||
|
<span className="text-xs font-bold text-gray-500">Previous</span>
|
||||||
|
<EllipsisVertical className='text-gray-400' size={13} />
|
||||||
|
<span className="text-sm font-semibold truncate max-w-[200px]">{previousActivity.name}</span>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,16 @@ const CourseClient = (props: any) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getLearningTags()
|
getLearningTags()
|
||||||
|
|
||||||
|
// Collapse chapters by default if more than 5 activities in total
|
||||||
|
if (course?.chapters) {
|
||||||
|
const totalActivities = course.chapters.reduce((sum: number, chapter: any) => sum + (chapter.activities?.length || 0), 0)
|
||||||
|
const defaultExpanded: {[key: string]: boolean} = {}
|
||||||
|
course.chapters.forEach((chapter: any) => {
|
||||||
|
defaultExpanded[chapter.chapter_uuid] = totalActivities <= 5
|
||||||
|
})
|
||||||
|
setExpandedChapters(defaultExpanded)
|
||||||
|
}
|
||||||
}, [org, course])
|
}, [org, course])
|
||||||
|
|
||||||
const getActivityTypeLabel = (activityType: string) => {
|
const getActivityTypeLabel = (activityType: string) => {
|
||||||
|
|
@ -117,16 +127,18 @@ const CourseClient = (props: any) => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<GeneralWrapperStyled>
|
<GeneralWrapperStyled>
|
||||||
<div className="pb-3 flex flex-col md:flex-row justify-between items-start md:items-center">
|
<div className="pb-2 pt-5 flex flex-col md:flex-row justify-between items-start md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-md font-bold text-gray-400 pb-2">Course</p>
|
<p className="text-md font-bold text-gray-400 pb-2">Course</p>
|
||||||
<h1 className="text-3xl md:text-3xl -mt-3 font-bold">{course.name}</h1>
|
<h1 className="text-3xl md:text-3xl -mt-3 font-bold">{course.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col md:flex-row gap-8 pt-2">
|
||||||
|
<div className="w-full md:w-3/4 space-y-4">
|
||||||
{props.course?.thumbnail_image && org ? (
|
{props.course?.thumbnail_image && org ? (
|
||||||
<div
|
<div
|
||||||
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-auto h-[200px] md:h-[400px] bg-cover bg-center mb-4"
|
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-full h-[200px] md:h-[400px] bg-cover bg-center"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url(${getCourseThumbnailMediaDirectory(
|
backgroundImage: `url(${getCourseThumbnailMediaDirectory(
|
||||||
org?.org_uuid,
|
org?.org_uuid,
|
||||||
|
|
@ -137,7 +149,7 @@ const CourseClient = (props: any) => {
|
||||||
></div>
|
></div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-auto h-[400px] bg-cover bg-center mb-4"
|
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-full h-[400px] bg-cover bg-center"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('../empty_thumbnail.png')`,
|
backgroundImage: `url('../empty_thumbnail.png')`,
|
||||||
backgroundSize: 'auto',
|
backgroundSize: 'auto',
|
||||||
|
|
@ -145,24 +157,37 @@ const CourseClient = (props: any) => {
|
||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{course?.trail?.runs?.find((run: any) => run.course_id == course.id) && (
|
||||||
<ActivityIndicators
|
<ActivityIndicators
|
||||||
course_uuid={props.course.course_uuid}
|
course_uuid={props.course.course_uuid}
|
||||||
orgslug={orgslug}
|
orgslug={orgslug}
|
||||||
course={course}
|
course={course}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row md:space-x-10 space-y-6 md:space-y-0 pt-10">
|
<div className="course_metadata_left space-y-2">
|
||||||
<div className="course_metadata_left w-full md:basis-3/4 space-y-2">
|
|
||||||
<h2 className="py-3 text-2xl font-bold">About</h2>
|
|
||||||
<div className="">
|
<div className="">
|
||||||
<p className="py-5 whitespace-pre-wrap">{course.about}</p>
|
<p className="py-5 whitespace-pre-wrap">{course.about}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='course_metadata_right w-full md:w-1/4 space-y-4'>
|
||||||
|
{/* Actions Box */}
|
||||||
|
<CoursesActions courseuuid={courseuuid} orgslug={orgslug} course={course} />
|
||||||
|
|
||||||
|
{/* Authors & Updates Box */}
|
||||||
|
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden p-4">
|
||||||
|
<CourseProvider courseuuid={course.course_uuid}>
|
||||||
|
<CourseAuthors authors={course.authors} />
|
||||||
|
</CourseProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{learnings.length > 0 && learnings[0]?.text !== 'null' && (
|
{learnings.length > 0 && learnings[0]?.text !== 'null' && (
|
||||||
<div>
|
<div className="w-full">
|
||||||
<h2 className="py-3 text-2xl font-bold">
|
<h2 className="py-5 text-xl md:text-2xl font-bold">What you will learn</h2>
|
||||||
What you will learn
|
|
||||||
</h2>
|
|
||||||
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden px-5 py-5 space-y-2">
|
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden px-5 py-5 space-y-2">
|
||||||
{learnings.map((learning: any) => {
|
{learnings.map((learning: any) => {
|
||||||
// Handle both new format (object with text and emoji) and legacy format (string)
|
// Handle both new format (object with text and emoji) and legacy format (string)
|
||||||
|
|
@ -203,7 +228,8 @@ const CourseClient = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h2 className="py-3 text-xl md:text-2xl font-bold">Course Lessons</h2>
|
<div className="w-full my-5 mb-10">
|
||||||
|
<h2 className="py-5 text-xl md:text-2xl font-bold">Course Lessons</h2>
|
||||||
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden">
|
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden">
|
||||||
{course.chapters.map((chapter: any) => {
|
{course.chapters.map((chapter: any) => {
|
||||||
const isExpanded = expandedChapters[chapter.chapter_uuid] ?? true; // Default to expanded
|
const isExpanded = expandedChapters[chapter.chapter_uuid] ?? true; // Default to expanded
|
||||||
|
|
@ -231,37 +257,34 @@ const CourseClient = (props: any) => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`py-3 transition-all duration-200 ${isExpanded ? 'block' : 'hidden'}`}>
|
<div className={`transition-all duration-200 ${isExpanded ? 'block' : 'hidden'}`}>
|
||||||
<div className="py-3">
|
<div className="">
|
||||||
{chapter.activities.map((activity: any) => {
|
{chapter.activities.map((activity: any) => {
|
||||||
return (
|
return (
|
||||||
<div key={activity.activity_uuid} className="activity-container">
|
<Link
|
||||||
<div className="group hover:bg-neutral-50 transition-colors px-4 py-3">
|
key={activity.activity_uuid}
|
||||||
|
href={
|
||||||
|
getUriWithOrg(orgslug, '') +
|
||||||
|
`/course/${courseuuid}/activity/${activity.activity_uuid.replace('activity_', '')}`
|
||||||
|
}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
prefetch={false}
|
||||||
|
className="block group activity-container transition-all duration-200 px-4 py-4"
|
||||||
|
>
|
||||||
<div className="flex space-x-3 items-center">
|
<div className="flex space-x-3 items-center">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{isActivityDone(activity) ? (
|
{isActivityDone(activity) ? (
|
||||||
<div className="relative cursor-pointer">
|
<div className="relative cursor-pointer">
|
||||||
<Square size={18} className="stroke-[2] text-teal-600" />
|
<Square size={16} className="stroke-[2] text-teal-600" />
|
||||||
<Check size={18} className="stroke-[2.5] text-teal-600 absolute top-0 left-0" />
|
<Check size={16} className="stroke-[2.5] text-teal-600 absolute top-0 left-0" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-neutral-300 cursor-pointer">
|
<div className="text-neutral-300 cursor-pointer">
|
||||||
<Square size={18} className="stroke-[2]" />
|
<Square size={16} className="stroke-[2]" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<div className="flex flex-col grow">
|
||||||
className="flex flex-col grow"
|
|
||||||
href={
|
|
||||||
getUriWithOrg(orgslug, '') +
|
|
||||||
`/course/${courseuuid}/activity/${activity.activity_uuid.replace(
|
|
||||||
'activity_',
|
|
||||||
''
|
|
||||||
)}`
|
|
||||||
}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
prefetch={false}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2 w-full">
|
<div className="flex items-center space-x-2 w-full">
|
||||||
<p className="font-semibold text-neutral-600 group-hover:text-neutral-800 transition-colors">{activity.name}</p>
|
<p className="font-semibold text-neutral-600 group-hover:text-neutral-800 transition-colors">{activity.name}</p>
|
||||||
{isActivityCurrent(activity) && (
|
{isActivityCurrent(activity) && (
|
||||||
|
|
@ -270,28 +293,27 @@ const CourseClient = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-1.5 mt-1 text-neutral-400">
|
<div className="flex items-center space-x-1.5 mt-0.5 text-neutral-400">
|
||||||
{activity.activity_type === 'TYPE_DYNAMIC' && (
|
{activity.activity_type === 'TYPE_DYNAMIC' && (
|
||||||
<StickyNote size={11} />
|
<StickyNote size={10} />
|
||||||
)}
|
)}
|
||||||
{activity.activity_type === 'TYPE_VIDEO' && (
|
{activity.activity_type === 'TYPE_VIDEO' && (
|
||||||
<Video size={11} />
|
<Video size={10} />
|
||||||
)}
|
)}
|
||||||
{activity.activity_type === 'TYPE_DOCUMENT' && (
|
{activity.activity_type === 'TYPE_DOCUMENT' && (
|
||||||
<File size={11} />
|
<File size={10} />
|
||||||
)}
|
)}
|
||||||
{activity.activity_type === 'TYPE_ASSIGNMENT' && (
|
{activity.activity_type === 'TYPE_ASSIGNMENT' && (
|
||||||
<Backpack size={11} />
|
<Backpack size={10} />
|
||||||
)}
|
)}
|
||||||
<span className="text-xs font-medium">{getActivityTypeLabel(activity.activity_type)}</span>
|
<span className="text-xs font-medium">{getActivityTypeLabel(activity.activity_type)}</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</div>
|
||||||
<div className="text-neutral-300 group-hover:text-neutral-400 transition-colors cursor-pointer">
|
<div className="text-neutral-300 group-hover:text-neutral-400 transition-colors cursor-pointer">
|
||||||
<ArrowRight size={16} />
|
<ArrowRight size={14} />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -301,18 +323,6 @@ const CourseClient = (props: any) => {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='course_metadata_right basis-1/4 space-y-4'>
|
|
||||||
{/* Actions Box */}
|
|
||||||
<CoursesActions courseuuid={courseuuid} orgslug={orgslug} course={course} />
|
|
||||||
|
|
||||||
{/* Authors & Updates Box */}
|
|
||||||
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden p-4">
|
|
||||||
<CourseProvider courseuuid={course.course_uuid}>
|
|
||||||
<CourseAuthors authors={course.authors} />
|
|
||||||
</CourseProvider>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</GeneralWrapperStyled>
|
</GeneralWrapperStyled>
|
||||||
|
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ export default Node.create({
|
||||||
size: {
|
size: {
|
||||||
width: 300,
|
width: 300,
|
||||||
},
|
},
|
||||||
|
alignment: {
|
||||||
|
default: 'center',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { NodeViewWrapper } from '@tiptap/react'
|
import { NodeViewWrapper } from '@tiptap/react'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { Resizable } from 're-resizable'
|
import { Resizable } from 're-resizable'
|
||||||
import { AlertTriangle, Image, Download } from 'lucide-react'
|
import { AlertTriangle, Image, Download, AlignLeft, AlignCenter, AlignRight } from 'lucide-react'
|
||||||
import { uploadNewImageFile } from '../../../../../services/blocks/Image/images'
|
import { uploadNewImageFile } from '../../../../../services/blocks/Image/images'
|
||||||
import { getActivityBlockMediaDirectory } from '@services/media/media'
|
import { getActivityBlockMediaDirectory } from '@services/media/media'
|
||||||
import { useOrg } from '@components/Contexts/OrgContext'
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
|
|
@ -29,10 +29,12 @@ function ImageBlockComponent(props: any) {
|
||||||
const [imageSize, setImageSize] = React.useState({
|
const [imageSize, setImageSize] = React.useState({
|
||||||
width: props.node.attrs.size ? props.node.attrs.size.width : 300,
|
width: props.node.attrs.size ? props.node.attrs.size.width : 300,
|
||||||
})
|
})
|
||||||
|
const [alignment, setAlignment] = React.useState(props.node.attrs.alignment || 'center')
|
||||||
|
|
||||||
const fileId = blockObject
|
const fileId = blockObject
|
||||||
? `${blockObject.content.file_id}.${blockObject.content.file_format}`
|
? `${blockObject.content.file_id}.${blockObject.content.file_format}`
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const handleImageChange = (event: React.ChangeEvent<any>) => {
|
const handleImageChange = (event: React.ChangeEvent<any>) => {
|
||||||
setImage(event.target.files[0])
|
setImage(event.target.files[0])
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +51,7 @@ function ImageBlockComponent(props: any) {
|
||||||
props.updateAttributes({
|
props.updateAttributes({
|
||||||
blockObject: object,
|
blockObject: object,
|
||||||
size: imageSize,
|
size: imageSize,
|
||||||
|
alignment: alignment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,8 +78,26 @@ function ImageBlockComponent(props: any) {
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAlignmentChange = (newAlignment: string) => {
|
||||||
|
setAlignment(newAlignment);
|
||||||
|
props.updateAttributes({
|
||||||
|
alignment: newAlignment,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {}, [course, org])
|
useEffect(() => {}, [course, org])
|
||||||
|
|
||||||
|
const getAlignmentClass = () => {
|
||||||
|
switch (alignment) {
|
||||||
|
case 'left':
|
||||||
|
return 'justify-start';
|
||||||
|
case 'right':
|
||||||
|
return 'justify-end';
|
||||||
|
default:
|
||||||
|
return 'justify-center';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper className="block-image w-full">
|
<NodeViewWrapper className="block-image w-full">
|
||||||
<FileUploadBlock isEditable={isEditable} isLoading={isLoading} isEmpty={!blockObject} Icon={Image}>
|
<FileUploadBlock isEditable={isEditable} isLoading={isLoading} isEmpty={!blockObject} Icon={Image}>
|
||||||
|
|
@ -85,7 +106,7 @@ function ImageBlockComponent(props: any) {
|
||||||
</FileUploadBlock>
|
</FileUploadBlock>
|
||||||
|
|
||||||
{blockObject && isEditable && (
|
{blockObject && isEditable && (
|
||||||
<div className="w-full flex justify-center">
|
<div className={`w-full flex ${getAlignmentClass()}`}>
|
||||||
<Resizable
|
<Resizable
|
||||||
defaultSize={{ width: imageSize.width, height: '100%' }}
|
defaultSize={{ width: imageSize.width, height: '100%' }}
|
||||||
handleStyles={{
|
handleStyles={{
|
||||||
|
|
@ -123,6 +144,7 @@ function ImageBlockComponent(props: any) {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className="relative">
|
||||||
<img
|
<img
|
||||||
src={`${getActivityBlockMediaDirectory(
|
src={`${getActivityBlockMediaDirectory(
|
||||||
org?.org_uuid,
|
org?.org_uuid,
|
||||||
|
|
@ -136,12 +158,36 @@ function ImageBlockComponent(props: any) {
|
||||||
className="rounded-lg shadow-sm max-w-full h-auto"
|
className="rounded-lg shadow-sm max-w-full h-auto"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
|
<div className="absolute top-2 right-2 flex items-center gap-1.5 bg-white bg-opacity-90 backdrop-blur-xs rounded-lg p-1 shadow-xs transition-opacity opacity-70 hover:opacity-100">
|
||||||
|
<button
|
||||||
|
onClick={() => handleAlignmentChange('left')}
|
||||||
|
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'left' ? 'bg-gray-100' : ''}`}
|
||||||
|
title="Align left"
|
||||||
|
>
|
||||||
|
<AlignLeft size={16} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleAlignmentChange('center')}
|
||||||
|
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'center' ? 'bg-gray-100' : ''}`}
|
||||||
|
title="Center align"
|
||||||
|
>
|
||||||
|
<AlignCenter size={16} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleAlignmentChange('right')}
|
||||||
|
className={`p-1.5 rounded-md hover:bg-gray-100 text-gray-600 ${alignment === 'right' ? 'bg-gray-100' : ''}`}
|
||||||
|
title="Align right"
|
||||||
|
>
|
||||||
|
<AlignRight size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Resizable>
|
</Resizable>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{blockObject && !isEditable && (
|
{blockObject && !isEditable && (
|
||||||
<div className="w-full flex justify-center">
|
<div className={`w-full flex ${getAlignmentClass()}`}>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img
|
<img
|
||||||
src={`${getActivityBlockMediaDirectory(
|
src={`${getActivityBlockMediaDirectory(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Search } from 'lucide-react'
|
import { Search } from 'lucide-react'
|
||||||
import { getUriWithOrg } from '@services/config/config'
|
import { getUriWithOrg } from '@services/config/config'
|
||||||
|
|
@ -9,6 +9,7 @@ import { getOrgLogoMediaDirectory } from '@services/media/media'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import { useOrg } from '@components/Contexts/OrgContext'
|
import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
import { SearchBar } from '@components/Objects/Search/SearchBar'
|
import { SearchBar } from '@components/Objects/Search/SearchBar'
|
||||||
|
import { usePathname } from 'next/navigation'
|
||||||
|
|
||||||
export const OrgMenu = (props: any) => {
|
export const OrgMenu = (props: any) => {
|
||||||
const orgslug = props.orgslug
|
const orgslug = props.orgslug
|
||||||
|
|
@ -17,6 +18,41 @@ export const OrgMenu = (props: any) => {
|
||||||
const [feedbackModal, setFeedbackModal] = React.useState(false)
|
const [feedbackModal, setFeedbackModal] = React.useState(false)
|
||||||
const org = useOrg() as any;
|
const org = useOrg() as any;
|
||||||
const [isMenuOpen, setIsMenuOpen] = React.useState(false)
|
const [isMenuOpen, setIsMenuOpen] = React.useState(false)
|
||||||
|
const [isFocusMode, setIsFocusMode] = useState(false)
|
||||||
|
const pathname = usePathname()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Only check focus mode if we're in an activity page
|
||||||
|
if (typeof window !== 'undefined' && pathname?.includes('/activity/')) {
|
||||||
|
const saved = localStorage.getItem('globalFocusMode');
|
||||||
|
setIsFocusMode(saved === 'true');
|
||||||
|
} else {
|
||||||
|
setIsFocusMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add storage event listener for cross-window changes
|
||||||
|
const handleStorageChange = (e: StorageEvent) => {
|
||||||
|
if (e.key === 'globalFocusMode' && pathname?.includes('/activity/')) {
|
||||||
|
setIsFocusMode(e.newValue === 'true');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add custom event listener for same-window changes
|
||||||
|
const handleFocusModeChange = (e: CustomEvent) => {
|
||||||
|
if (pathname?.includes('/activity/')) {
|
||||||
|
setIsFocusMode(e.detail.isFocusMode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
window.addEventListener('focusModeChange', handleFocusModeChange as EventListener);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
window.removeEventListener('focusModeChange', handleFocusModeChange as EventListener);
|
||||||
|
};
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
function closeFeedbackModal() {
|
function closeFeedbackModal() {
|
||||||
setFeedbackModal(false)
|
setFeedbackModal(false)
|
||||||
|
|
@ -26,6 +62,11 @@ export const OrgMenu = (props: any) => {
|
||||||
setIsMenuOpen(!isMenuOpen)
|
setIsMenuOpen(!isMenuOpen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only hide menu if we're in an activity page and focus mode is enabled
|
||||||
|
if (pathname?.includes('/activity/') && isFocusMode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="backdrop-blur-lg h-[60px] blur-3xl -z-10"></div>
|
<div className="backdrop-blur-lg h-[60px] blur-3xl -z-10"></div>
|
||||||
|
|
|
||||||
|
|
@ -160,11 +160,7 @@ export default function FixedActivitySecondaryBar(props: FixedActivitySecondaryB
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateToActivity(nextActivity)}
|
onClick={() => navigateToActivity(nextActivity)}
|
||||||
className={`flex items-center space-x-1 sm:space-x-2 py-1.5 px-1.5 sm:px-2 rounded-md transition-all duration-200 ${
|
className={`flex items-center space-x-1 sm:space-x-2 py-1.5 px-1.5 sm:px-2 rounded-md transition-all duration-200`}
|
||||||
nextActivity
|
|
||||||
? 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
|
||||||
: 'text-gray-300 cursor-not-allowed'
|
|
||||||
}`}
|
|
||||||
disabled={!nextActivity}
|
disabled={!nextActivity}
|
||||||
title={nextActivity ? `Next: ${nextActivity.name}` : 'No next activity'}
|
title={nextActivity ? `Next: ${nextActivity.name}` : 'No next activity'}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@ import toast from 'react-hot-toast';
|
||||||
|
|
||||||
export type ContributorStatus = 'NONE' | 'PENDING' | 'ACTIVE' | 'INACTIVE';
|
export type ContributorStatus = 'NONE' | 'PENDING' | 'ACTIVE' | 'INACTIVE';
|
||||||
|
|
||||||
|
interface Contributor {
|
||||||
|
user_id: string;
|
||||||
|
authorship_status: ContributorStatus;
|
||||||
|
}
|
||||||
|
|
||||||
export function useContributorStatus(courseUuid: string) {
|
export function useContributorStatus(courseUuid: string) {
|
||||||
const session = useLHSession() as any;
|
const session = useLHSession() as any;
|
||||||
const [contributorStatus, setContributorStatus] = useState<ContributorStatus>('NONE');
|
const [contributorStatus, setContributorStatus] = useState<ContributorStatus>('NONE');
|
||||||
|
|
@ -22,9 +27,9 @@ export function useContributorStatus(courseUuid: string) {
|
||||||
session.data?.tokens?.access_token
|
session.data?.tokens?.access_token
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response && response.data) {
|
if (response && response.data && Array.isArray(response.data)) {
|
||||||
const currentUser = response.data.find(
|
const currentUser = response.data.find(
|
||||||
(contributor: any) => contributor.user_id === session.data.user.id
|
(contributor: Contributor) => contributor.user_id === session.data.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
|
|
@ -32,10 +37,13 @@ export function useContributorStatus(courseUuid: string) {
|
||||||
} else {
|
} else {
|
||||||
setContributorStatus('NONE');
|
setContributorStatus('NONE');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setContributorStatus('NONE');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check contributor status:', error);
|
console.error('Failed to check contributor status:', error);
|
||||||
toast.error('Failed to check contributor status');
|
toast.error('Failed to check contributor status');
|
||||||
|
setContributorStatus('NONE');
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"lowlight": "^3.3.0",
|
"lowlight": "^3.3.0",
|
||||||
"lucide-react": "^0.453.0",
|
"lucide-react": "^0.453.0",
|
||||||
"next": "15.2.4",
|
"next": "15.3.1",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"nextjs-toploader": "^1.6.12",
|
"nextjs-toploader": "^1.6.12",
|
||||||
"prosemirror-state": "^1.4.3",
|
"prosemirror-state": "^1.4.3",
|
||||||
|
|
|
||||||
316
apps/web/pnpm-lock.yaml
generated
316
apps/web/pnpm-lock.yaml
generated
|
|
@ -169,14 +169,14 @@ importers:
|
||||||
specifier: ^0.453.0
|
specifier: ^0.453.0
|
||||||
version: 0.453.0(react@19.0.0)
|
version: 0.453.0(react@19.0.0)
|
||||||
next:
|
next:
|
||||||
specifier: 15.2.4
|
specifier: 15.3.1
|
||||||
version: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: ^4.24.11
|
specifier: ^4.24.11
|
||||||
version: 4.24.11(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 4.24.11(next@15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
nextjs-toploader:
|
nextjs-toploader:
|
||||||
specifier: ^1.6.12
|
specifier: ^1.6.12
|
||||||
version: 1.6.12(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 1.6.12(next@15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
prosemirror-state:
|
prosemirror-state:
|
||||||
specifier: ^1.4.3
|
specifier: ^1.4.3
|
||||||
version: 1.4.3
|
version: 1.4.3
|
||||||
|
|
@ -412,158 +412,268 @@ packages:
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-darwin-x64@0.33.5':
|
'@img/sharp-darwin-x64@0.33.5':
|
||||||
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||||
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||||
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.1.0':
|
||||||
|
resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||||
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.1.0':
|
||||||
|
resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
|
||||||
|
resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-arm64@0.33.5':
|
'@img/sharp-linux-arm64@0.33.5':
|
||||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-arm@0.33.5':
|
'@img/sharp-linux-arm@0.33.5':
|
||||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.1':
|
||||||
|
resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-s390x@0.33.5':
|
'@img/sharp-linux-s390x@0.33.5':
|
||||||
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.1':
|
||||||
|
resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linux-x64@0.33.5':
|
'@img/sharp-linux-x64@0.33.5':
|
||||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
'@img/sharp-wasm32@0.33.5':
|
'@img/sharp-wasm32@0.33.5':
|
||||||
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [wasm32]
|
cpu: [wasm32]
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.1':
|
||||||
|
resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [wasm32]
|
||||||
|
|
||||||
'@img/sharp-win32-ia32@0.33.5':
|
'@img/sharp-win32-ia32@0.33.5':
|
||||||
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
|
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.1':
|
||||||
|
resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@img/sharp-win32-x64@0.33.5':
|
'@img/sharp-win32-x64@0.33.5':
|
||||||
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.1':
|
||||||
|
resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@0.2.8':
|
'@napi-rs/wasm-runtime@0.2.8':
|
||||||
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
||||||
|
|
||||||
'@next/env@15.2.4':
|
'@next/env@15.3.1':
|
||||||
resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==}
|
resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.2.1':
|
'@next/eslint-plugin-next@15.2.1':
|
||||||
resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==}
|
resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==}
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.2.4':
|
'@next/swc-darwin-arm64@15.3.1':
|
||||||
resolution: {integrity: sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==}
|
resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.2.4':
|
'@next/swc-darwin-x64@15.3.1':
|
||||||
resolution: {integrity: sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==}
|
resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.2.4':
|
'@next/swc-linux-arm64-gnu@15.3.1':
|
||||||
resolution: {integrity: sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==}
|
resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.2.4':
|
'@next/swc-linux-arm64-musl@15.3.1':
|
||||||
resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
|
resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.2.4':
|
'@next/swc-linux-x64-gnu@15.3.1':
|
||||||
resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
|
resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.2.4':
|
'@next/swc-linux-x64-musl@15.3.1':
|
||||||
resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
|
resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.2.4':
|
'@next/swc-win32-arm64-msvc@15.3.1':
|
||||||
resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
|
resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.2.4':
|
'@next/swc-win32-x64-msvc@15.3.1':
|
||||||
resolution: {integrity: sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==}
|
resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
@ -2768,8 +2878,8 @@ packages:
|
||||||
nodemailer:
|
nodemailer:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
next@15.2.4:
|
next@15.3.1:
|
||||||
resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
|
resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -3216,6 +3326,10 @@ packages:
|
||||||
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
||||||
|
sharp@0.34.1:
|
||||||
|
resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -3657,76 +3771,154 @@ snapshots:
|
||||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-darwin-x64@0.33.5':
|
'@img/sharp-darwin-x64@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
'@img/sharp-libvips-darwin-x64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-arm64@0.33.5':
|
'@img/sharp-linux-arm64@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
'@img/sharp-libvips-linux-arm64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-arm@0.33.5':
|
'@img/sharp-linux-arm@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
'@img/sharp-libvips-linux-arm': 1.0.5
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-s390x@0.33.5':
|
'@img/sharp-linux-s390x@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-s390x': 1.0.4
|
'@img/sharp-libvips-linux-s390x': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linux-x64@0.33.5':
|
'@img/sharp-linux-x64@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
'@img/sharp-libvips-linux-x64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.1':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-wasm32@0.33.5':
|
'@img/sharp-wasm32@0.33.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/runtime': 1.4.0
|
'@emnapi/runtime': 1.4.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.1':
|
||||||
|
dependencies:
|
||||||
|
'@emnapi/runtime': 1.4.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-win32-ia32@0.33.5':
|
'@img/sharp-win32-ia32@0.33.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@img/sharp-win32-x64@0.33.5':
|
'@img/sharp-win32-x64@0.33.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.1':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@0.2.8':
|
'@napi-rs/wasm-runtime@0.2.8':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/core': 1.4.0
|
'@emnapi/core': 1.4.0
|
||||||
|
|
@ -3734,34 +3926,34 @@ snapshots:
|
||||||
'@tybys/wasm-util': 0.9.0
|
'@tybys/wasm-util': 0.9.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/env@15.2.4': {}
|
'@next/env@15.3.1': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.2.1':
|
'@next/eslint-plugin-next@15.2.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.2.4':
|
'@next/swc-darwin-arm64@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.2.4':
|
'@next/swc-darwin-x64@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.2.4':
|
'@next/swc-linux-arm64-gnu@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.2.4':
|
'@next/swc-linux-arm64-musl@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.2.4':
|
'@next/swc-linux-x64-gnu@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.2.4':
|
'@next/swc-linux-x64-musl@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.2.4':
|
'@next/swc-win32-arm64-msvc@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.2.4':
|
'@next/swc-win32-x64-msvc@15.3.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
|
|
@ -6119,13 +6311,13 @@ snapshots:
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
next-auth@4.24.11(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
next-auth@4.24.11(next@15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.27.0
|
'@babel/runtime': 7.27.0
|
||||||
'@panva/hkdf': 1.2.1
|
'@panva/hkdf': 1.2.1
|
||||||
cookie: 0.7.2
|
cookie: 0.7.2
|
||||||
jose: 4.15.9
|
jose: 4.15.9
|
||||||
next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
next: 15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
oauth: 0.9.15
|
oauth: 0.9.15
|
||||||
openid-client: 5.7.1
|
openid-client: 5.7.1
|
||||||
preact: 10.26.5
|
preact: 10.26.5
|
||||||
|
|
@ -6134,9 +6326,9 @@ snapshots:
|
||||||
react-dom: 19.0.0(react@19.0.0)
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
|
|
||||||
next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
next@15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.2.4
|
'@next/env': 15.3.1
|
||||||
'@swc/counter': 0.1.3
|
'@swc/counter': 0.1.3
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
|
|
@ -6146,23 +6338,23 @@ snapshots:
|
||||||
react-dom: 19.0.0(react@19.0.0)
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
styled-jsx: 5.1.6(react@19.0.0)
|
styled-jsx: 5.1.6(react@19.0.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-darwin-arm64': 15.2.4
|
'@next/swc-darwin-arm64': 15.3.1
|
||||||
'@next/swc-darwin-x64': 15.2.4
|
'@next/swc-darwin-x64': 15.3.1
|
||||||
'@next/swc-linux-arm64-gnu': 15.2.4
|
'@next/swc-linux-arm64-gnu': 15.3.1
|
||||||
'@next/swc-linux-arm64-musl': 15.2.4
|
'@next/swc-linux-arm64-musl': 15.3.1
|
||||||
'@next/swc-linux-x64-gnu': 15.2.4
|
'@next/swc-linux-x64-gnu': 15.3.1
|
||||||
'@next/swc-linux-x64-musl': 15.2.4
|
'@next/swc-linux-x64-musl': 15.3.1
|
||||||
'@next/swc-win32-arm64-msvc': 15.2.4
|
'@next/swc-win32-arm64-msvc': 15.3.1
|
||||||
'@next/swc-win32-x64-msvc': 15.2.4
|
'@next/swc-win32-x64-msvc': 15.3.1
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
sharp: 0.33.5
|
sharp: 0.34.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
nextjs-toploader@1.6.12(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
nextjs-toploader@1.6.12(next@15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
next: 15.3.1(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
nprogress: 0.2.0
|
nprogress: 0.2.0
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
@ -6666,6 +6858,34 @@ snapshots:
|
||||||
'@img/sharp-win32-ia32': 0.33.5
|
'@img/sharp-win32-ia32': 0.33.5
|
||||||
'@img/sharp-win32-x64': 0.33.5
|
'@img/sharp-win32-x64': 0.33.5
|
||||||
|
|
||||||
|
sharp@0.34.1:
|
||||||
|
dependencies:
|
||||||
|
color: 4.2.3
|
||||||
|
detect-libc: 2.0.3
|
||||||
|
semver: 7.7.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-darwin-arm64': 0.34.1
|
||||||
|
'@img/sharp-darwin-x64': 0.34.1
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.1.0
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.1.0
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.1.0
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.1.0
|
||||||
|
'@img/sharp-libvips-linux-ppc64': 1.1.0
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.1.0
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.1.0
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.1.0
|
||||||
|
'@img/sharp-linux-arm': 0.34.1
|
||||||
|
'@img/sharp-linux-arm64': 0.34.1
|
||||||
|
'@img/sharp-linux-s390x': 0.34.1
|
||||||
|
'@img/sharp-linux-x64': 0.34.1
|
||||||
|
'@img/sharp-linuxmusl-arm64': 0.34.1
|
||||||
|
'@img/sharp-linuxmusl-x64': 0.34.1
|
||||||
|
'@img/sharp-wasm32': 0.34.1
|
||||||
|
'@img/sharp-win32-ia32': 0.34.1
|
||||||
|
'@img/sharp-win32-x64': 0.34.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue