mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat(wip): initial ui and functionality for certifications
This commit is contained in:
parent
aabb4d190c
commit
86f7a80eb7
5 changed files with 1155 additions and 1 deletions
|
|
@ -5,11 +5,12 @@ import { CourseProvider } from '../../../../../../../../components/Contexts/Cour
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { CourseOverviewTop } from '@components/Dashboard/Misc/CourseOverviewTop'
|
import { CourseOverviewTop } from '@components/Dashboard/Misc/CourseOverviewTop'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { GalleryVerticalEnd, Globe, Info, UserPen, UserRoundCog, Users } from 'lucide-react'
|
import { GalleryVerticalEnd, Globe, Info, UserPen, UserRoundCog, Users, Award } from 'lucide-react'
|
||||||
import EditCourseStructure from '@components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure'
|
import EditCourseStructure from '@components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure'
|
||||||
import EditCourseGeneral from '@components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral'
|
import EditCourseGeneral from '@components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral'
|
||||||
import EditCourseAccess from '@components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess'
|
import EditCourseAccess from '@components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess'
|
||||||
import EditCourseContributors from '@components/Dashboard/Pages/Course/EditCourseContributors/EditCourseContributors'
|
import EditCourseContributors from '@components/Dashboard/Pages/Course/EditCourseContributors/EditCourseContributors'
|
||||||
|
import EditCourseCertification from '@components/Dashboard/Pages/Course/EditCourseCertification/EditCourseCertification'
|
||||||
export type CourseOverviewParams = {
|
export type CourseOverviewParams = {
|
||||||
orgslug: string
|
orgslug: string
|
||||||
courseuuid: string
|
courseuuid: string
|
||||||
|
|
@ -102,6 +103,24 @@ function CourseOverviewPage(props: { params: Promise<CourseOverviewParams> }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
getUriWithOrg(params.orgslug, '') +
|
||||||
|
`/dash/courses/course/${params.courseuuid}/certification`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${params.subpage.toString() === 'certification'
|
||||||
|
? 'border-b-4'
|
||||||
|
: 'opacity-50'
|
||||||
|
} cursor-pointer`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2.5 mx-2">
|
||||||
|
<Award size={16} />
|
||||||
|
<div>Certification</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -117,6 +136,8 @@ function CourseOverviewPage(props: { params: Promise<CourseOverviewParams> }) {
|
||||||
{params.subpage == 'general' ? (<EditCourseGeneral orgslug={params.orgslug} />) : ('')}
|
{params.subpage == 'general' ? (<EditCourseGeneral orgslug={params.orgslug} />) : ('')}
|
||||||
{params.subpage == 'access' ? (<EditCourseAccess orgslug={params.orgslug} />) : ('')}
|
{params.subpage == 'access' ? (<EditCourseAccess orgslug={params.orgslug} />) : ('')}
|
||||||
{params.subpage == 'contributors' ? (<EditCourseContributors orgslug={params.orgslug} />) : ('')}
|
{params.subpage == 'contributors' ? (<EditCourseContributors orgslug={params.orgslug} />) : ('')}
|
||||||
|
{params.subpage == 'certification' ? (<EditCourseCertification orgslug={params.orgslug} />) : ('')}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</CourseProvider>
|
</CourseProvider>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,566 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Award, CheckCircle, QrCode, Building, User, Calendar, Hash } from 'lucide-react';
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
|
import { getOrgLogoMediaDirectory } from '@services/media/media';
|
||||||
|
|
||||||
|
interface CertificatePreviewProps {
|
||||||
|
certificationName: string;
|
||||||
|
certificationDescription: string;
|
||||||
|
certificationType: string;
|
||||||
|
certificatePattern: string;
|
||||||
|
certificateInstructor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CertificatePreview: React.FC<CertificatePreviewProps> = ({
|
||||||
|
certificationName,
|
||||||
|
certificationDescription,
|
||||||
|
certificationType,
|
||||||
|
certificatePattern,
|
||||||
|
certificateInstructor
|
||||||
|
}) => {
|
||||||
|
const [qrCodeUrl, setQrCodeUrl] = useState<string>('');
|
||||||
|
const org = useOrg() as any;
|
||||||
|
|
||||||
|
// Generate QR code
|
||||||
|
useEffect(() => {
|
||||||
|
const generateQRCode = async () => {
|
||||||
|
try {
|
||||||
|
const certificateData = `https://learnhouse.app/verify/LH-2024-001`;
|
||||||
|
const qrUrl = await QRCode.toDataURL(certificateData, {
|
||||||
|
width: 185,
|
||||||
|
margin: 1,
|
||||||
|
color: {
|
||||||
|
dark: '#000000',
|
||||||
|
light: '#FFFFFF'
|
||||||
|
},
|
||||||
|
errorCorrectionLevel: 'M',
|
||||||
|
type: 'image/png'
|
||||||
|
});
|
||||||
|
setQrCodeUrl(qrUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating QR code:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
generateQRCode();
|
||||||
|
}, []);
|
||||||
|
// Function to get theme colors for each pattern
|
||||||
|
const getPatternTheme = (pattern: string) => {
|
||||||
|
switch (pattern) {
|
||||||
|
case 'royal':
|
||||||
|
return {
|
||||||
|
primary: 'text-amber-700',
|
||||||
|
secondary: 'text-amber-600',
|
||||||
|
icon: 'text-amber-600',
|
||||||
|
badge: 'bg-amber-50 text-amber-700 border-amber-200'
|
||||||
|
};
|
||||||
|
case 'tech':
|
||||||
|
return {
|
||||||
|
primary: 'text-cyan-700',
|
||||||
|
secondary: 'text-cyan-600',
|
||||||
|
icon: 'text-cyan-600',
|
||||||
|
badge: 'bg-cyan-50 text-cyan-700 border-cyan-200'
|
||||||
|
};
|
||||||
|
case 'nature':
|
||||||
|
return {
|
||||||
|
primary: 'text-green-700',
|
||||||
|
secondary: 'text-green-600',
|
||||||
|
icon: 'text-green-600',
|
||||||
|
badge: 'bg-green-50 text-green-700 border-green-200'
|
||||||
|
};
|
||||||
|
case 'geometric':
|
||||||
|
return {
|
||||||
|
primary: 'text-purple-700',
|
||||||
|
secondary: 'text-purple-600',
|
||||||
|
icon: 'text-purple-600',
|
||||||
|
badge: 'bg-purple-50 text-purple-700 border-purple-200'
|
||||||
|
};
|
||||||
|
case 'vintage':
|
||||||
|
return {
|
||||||
|
primary: 'text-orange-700',
|
||||||
|
secondary: 'text-orange-600',
|
||||||
|
icon: 'text-orange-600',
|
||||||
|
badge: 'bg-orange-50 text-orange-700 border-orange-200'
|
||||||
|
};
|
||||||
|
case 'waves':
|
||||||
|
return {
|
||||||
|
primary: 'text-blue-700',
|
||||||
|
secondary: 'text-blue-600',
|
||||||
|
icon: 'text-blue-600',
|
||||||
|
badge: 'bg-blue-50 text-blue-700 border-blue-200'
|
||||||
|
};
|
||||||
|
case 'minimal':
|
||||||
|
return {
|
||||||
|
primary: 'text-gray-700',
|
||||||
|
secondary: 'text-gray-600',
|
||||||
|
icon: 'text-gray-600',
|
||||||
|
badge: 'bg-gray-50 text-gray-700 border-gray-200'
|
||||||
|
};
|
||||||
|
case 'professional':
|
||||||
|
return {
|
||||||
|
primary: 'text-slate-700',
|
||||||
|
secondary: 'text-slate-600',
|
||||||
|
icon: 'text-slate-600',
|
||||||
|
badge: 'bg-slate-50 text-slate-700 border-slate-200'
|
||||||
|
};
|
||||||
|
case 'academic':
|
||||||
|
return {
|
||||||
|
primary: 'text-indigo-700',
|
||||||
|
secondary: 'text-indigo-600',
|
||||||
|
icon: 'text-indigo-600',
|
||||||
|
badge: 'bg-indigo-50 text-indigo-700 border-indigo-200'
|
||||||
|
};
|
||||||
|
case 'modern':
|
||||||
|
return {
|
||||||
|
primary: 'text-blue-700',
|
||||||
|
secondary: 'text-blue-600',
|
||||||
|
icon: 'text-blue-600',
|
||||||
|
badge: 'bg-blue-50 text-blue-700 border-blue-200'
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
primary: 'text-gray-700',
|
||||||
|
secondary: 'text-gray-600',
|
||||||
|
icon: 'text-gray-600',
|
||||||
|
badge: 'bg-gray-50 text-gray-700 border-gray-200'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to render different certificate patterns
|
||||||
|
const renderCertificatePattern = (pattern: string) => {
|
||||||
|
switch (pattern) {
|
||||||
|
case 'royal':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Royal ornate border with crown elements */}
|
||||||
|
<div className="absolute inset-3 border-4 border-amber-200 rounded-lg opacity-60"></div>
|
||||||
|
<div className="absolute inset-4 border-2 border-amber-300 rounded-md opacity-40"></div>
|
||||||
|
|
||||||
|
{/* Crown-like decorations in corners */}
|
||||||
|
<div className="absolute top-1 left-1/2 transform -translate-x-1/2">
|
||||||
|
<div className="w-8 h-4 bg-amber-200 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0% 100%, 20% 0%, 40% 100%, 60% 0%, 80% 100%, 100% 0%, 100% 100%)'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute bottom-1 left-1/2 transform -translate-x-1/2 rotate-180">
|
||||||
|
<div className="w-8 h-4 bg-amber-200 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0% 100%, 20% 0%, 40% 100%, 60% 0%, 80% 100%, 100% 0%, 100% 100%)'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Royal background pattern */}
|
||||||
|
<div className="absolute inset-0 opacity-3">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `radial-gradient(circle at 25% 25%, #f59e0b 2px, transparent 2px), radial-gradient(circle at 75% 75%, #f59e0b 1px, transparent 1px)`,
|
||||||
|
backgroundSize: '16px 16px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'tech':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Tech circuit board borders */}
|
||||||
|
<div className="absolute inset-3 border-2 border-cyan-200 opacity-50"></div>
|
||||||
|
|
||||||
|
{/* Circuit-like corner elements */}
|
||||||
|
<div className="absolute top-3 left-3 w-6 h-6 border-l-2 border-t-2 border-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute top-3 left-5 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute top-5 left-3 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
|
||||||
|
<div className="absolute top-3 right-3 w-6 h-6 border-r-2 border-t-2 border-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute top-3 right-5 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute top-5 right-3 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-3 left-3 w-6 h-6 border-l-2 border-b-2 border-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-3 left-5 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-5 left-3 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-3 right-3 w-6 h-6 border-r-2 border-b-2 border-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-3 right-5 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-5 right-3 w-2 h-2 bg-cyan-300 opacity-60"></div>
|
||||||
|
|
||||||
|
{/* Tech grid background */}
|
||||||
|
<div className="absolute inset-0 opacity-4">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `linear-gradient(90deg, #06b6d4 1px, transparent 1px), linear-gradient(0deg, #06b6d4 1px, transparent 1px)`,
|
||||||
|
backgroundSize: '8px 8px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'nature':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Nature organic border */}
|
||||||
|
<div className="absolute inset-3 border-2 border-green-200 rounded-2xl opacity-50"></div>
|
||||||
|
|
||||||
|
{/* Leaf-like decorations */}
|
||||||
|
<div className="absolute top-2 left-2 w-4 h-6 bg-green-200 opacity-50 rounded-full transform rotate-45"></div>
|
||||||
|
<div className="absolute top-2 left-4 w-3 h-4 bg-green-300 opacity-40 rounded-full transform rotate-12"></div>
|
||||||
|
|
||||||
|
<div className="absolute top-2 right-2 w-4 h-6 bg-green-200 opacity-50 rounded-full transform -rotate-45"></div>
|
||||||
|
<div className="absolute top-2 right-4 w-3 h-4 bg-green-300 opacity-40 rounded-full transform -rotate-12"></div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-2 left-2 w-4 h-6 bg-green-200 opacity-50 rounded-full transform -rotate-45"></div>
|
||||||
|
<div className="absolute bottom-2 left-4 w-3 h-4 bg-green-300 opacity-40 rounded-full transform -rotate-12"></div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-2 right-2 w-4 h-6 bg-green-200 opacity-50 rounded-full transform rotate-45"></div>
|
||||||
|
<div className="absolute bottom-2 right-4 w-3 h-4 bg-green-300 opacity-40 rounded-full transform rotate-12"></div>
|
||||||
|
|
||||||
|
{/* Organic background pattern */}
|
||||||
|
<div className="absolute inset-0 opacity-3">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `radial-gradient(ellipse at 30% 30%, #10b981 1px, transparent 1px), radial-gradient(ellipse at 70% 70%, #10b981 0.5px, transparent 0.5px)`,
|
||||||
|
backgroundSize: '12px 8px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'geometric':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Geometric angular borders */}
|
||||||
|
<div className="absolute inset-2 border-2 border-purple-200 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 10px, 10px 0, calc(100% - 10px) 0, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0 calc(100% - 10px))'
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
{/* Geometric corner elements */}
|
||||||
|
<div className="absolute top-1 left-1 w-6 h-6 border-2 border-purple-300 opacity-60 transform rotate-45"></div>
|
||||||
|
<div className="absolute top-1 right-1 w-6 h-6 border-2 border-purple-300 opacity-60 transform rotate-45"></div>
|
||||||
|
<div className="absolute bottom-1 left-1 w-6 h-6 border-2 border-purple-300 opacity-60 transform rotate-45"></div>
|
||||||
|
<div className="absolute bottom-1 right-1 w-6 h-6 border-2 border-purple-300 opacity-60 transform rotate-45"></div>
|
||||||
|
|
||||||
|
{/* Abstract geometric shapes */}
|
||||||
|
<div className="absolute top-1/4 left-1 w-2 h-8 bg-purple-200 opacity-30 transform rotate-12"></div>
|
||||||
|
<div className="absolute top-1/4 right-1 w-2 h-8 bg-purple-200 opacity-30 transform -rotate-12"></div>
|
||||||
|
<div className="absolute bottom-1/4 left-1 w-2 h-8 bg-purple-200 opacity-30 transform -rotate-12"></div>
|
||||||
|
<div className="absolute bottom-1/4 right-1 w-2 h-8 bg-purple-200 opacity-30 transform rotate-12"></div>
|
||||||
|
|
||||||
|
{/* Geometric background */}
|
||||||
|
<div className="absolute inset-0 opacity-4">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `linear-gradient(45deg, #8b5cf6 25%, transparent 25%), linear-gradient(-45deg, #8b5cf6 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #8b5cf6 75%), linear-gradient(-45deg, transparent 75%, #8b5cf6 75%)`,
|
||||||
|
backgroundSize: '6px 6px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'vintage':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Art deco style borders */}
|
||||||
|
<div className="absolute inset-2 border-2 border-orange-200 opacity-50"></div>
|
||||||
|
<div className="absolute inset-3 border border-orange-300 opacity-40"></div>
|
||||||
|
|
||||||
|
{/* Art deco corner decorations */}
|
||||||
|
<div className="absolute top-2 left-2 w-8 h-8 border-2 border-orange-300 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 0, 100% 0, 100% 50%, 50% 100%, 0 100%)'
|
||||||
|
}}></div>
|
||||||
|
<div className="absolute top-2 right-2 w-8 h-8 border-2 border-orange-300 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 100%, 0 50%)'
|
||||||
|
}}></div>
|
||||||
|
<div className="absolute bottom-2 left-2 w-8 h-8 border-2 border-orange-300 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 0, 50% 0, 100% 50%, 100% 100%, 0 100%)'
|
||||||
|
}}></div>
|
||||||
|
<div className="absolute bottom-2 right-2 w-8 h-8 border-2 border-orange-300 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 50%, 50% 0, 100% 0, 100% 100%, 0 100%)'
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
{/* Art deco sunburst pattern */}
|
||||||
|
<div className="absolute inset-0 opacity-3">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `repeating-conic-gradient(from 0deg at 50% 50%, #f97316 0deg, #f97316 2deg, transparent 2deg, transparent 8deg)`,
|
||||||
|
backgroundSize: '100% 100%'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'waves':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Flowing wave borders */}
|
||||||
|
<div className="absolute inset-2 border-2 border-blue-200 rounded-3xl opacity-50"></div>
|
||||||
|
|
||||||
|
{/* Wave decorations */}
|
||||||
|
<div className="absolute top-2 left-0 right-0 h-4 opacity-30" style={{
|
||||||
|
background: `radial-gradient(ellipse at center, #3b82f6 30%, transparent 30%)`,
|
||||||
|
backgroundSize: '20px 8px'
|
||||||
|
}}></div>
|
||||||
|
<div className="absolute bottom-2 left-0 right-0 h-4 opacity-30" style={{
|
||||||
|
background: `radial-gradient(ellipse at center, #3b82f6 30%, transparent 30%)`,
|
||||||
|
backgroundSize: '20px 8px'
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
{/* Side wave patterns */}
|
||||||
|
<div className="absolute left-2 top-0 bottom-0 w-4 opacity-30" style={{
|
||||||
|
background: `radial-gradient(ellipse at center, #3b82f6 30%, transparent 30%)`,
|
||||||
|
backgroundSize: '8px 20px'
|
||||||
|
}}></div>
|
||||||
|
<div className="absolute right-2 top-0 bottom-0 w-4 opacity-30" style={{
|
||||||
|
background: `radial-gradient(ellipse at center, #3b82f6 30%, transparent 30%)`,
|
||||||
|
backgroundSize: '8px 20px'
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
{/* Wave background */}
|
||||||
|
<div className="absolute inset-0 opacity-4">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `repeating-linear-gradient(45deg, #3b82f6 0px, #3b82f6 1px, transparent 1px, transparent 8px), repeating-linear-gradient(-45deg, #3b82f6 0px, #3b82f6 1px, transparent 1px, transparent 8px)`,
|
||||||
|
backgroundSize: '12px 12px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'minimal':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Minimal clean border */}
|
||||||
|
<div className="absolute inset-6 border border-gray-300 opacity-60"></div>
|
||||||
|
|
||||||
|
{/* Subtle corner accents */}
|
||||||
|
<div className="absolute top-5 left-5 w-3 h-3 border-l border-t border-gray-400 opacity-40"></div>
|
||||||
|
<div className="absolute top-5 right-5 w-3 h-3 border-r border-t border-gray-400 opacity-40"></div>
|
||||||
|
<div className="absolute bottom-5 left-5 w-3 h-3 border-l border-b border-gray-400 opacity-40"></div>
|
||||||
|
<div className="absolute bottom-5 right-5 w-3 h-3 border-r border-b border-gray-400 opacity-40"></div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'professional':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Professional double border */}
|
||||||
|
<div className="absolute inset-2 border-2 border-slate-300 opacity-50"></div>
|
||||||
|
<div className="absolute inset-3 border border-slate-400 opacity-40"></div>
|
||||||
|
|
||||||
|
{/* Professional corner brackets */}
|
||||||
|
<div className="absolute top-2 left-2 w-6 h-6 border-l-2 border-t-2 border-slate-400 opacity-60"></div>
|
||||||
|
<div className="absolute top-2 right-2 w-6 h-6 border-r-2 border-t-2 border-slate-400 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-2 left-2 w-6 h-6 border-l-2 border-b-2 border-slate-400 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-2 right-2 w-6 h-6 border-r-2 border-b-2 border-slate-400 opacity-60"></div>
|
||||||
|
|
||||||
|
{/* Subtle professional background */}
|
||||||
|
<div className="absolute inset-0 opacity-2">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `linear-gradient(90deg, #64748b 1px, transparent 1px), linear-gradient(0deg, #64748b 1px, transparent 1px)`,
|
||||||
|
backgroundSize: '20px 20px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'academic':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Academic traditional border */}
|
||||||
|
<div className="absolute inset-2 border-3 border-indigo-300 opacity-50"></div>
|
||||||
|
<div className="absolute inset-3 border border-indigo-400 opacity-40"></div>
|
||||||
|
|
||||||
|
{/* Academic shield-like corners */}
|
||||||
|
<div className="absolute top-2 left-2 w-8 h-8 border-2 border-indigo-400 opacity-50 rounded-tl-lg"></div>
|
||||||
|
<div className="absolute top-2 right-2 w-8 h-8 border-2 border-indigo-400 opacity-50 rounded-tr-lg"></div>
|
||||||
|
<div className="absolute bottom-2 left-2 w-8 h-8 border-2 border-indigo-400 opacity-50 rounded-bl-lg"></div>
|
||||||
|
<div className="absolute bottom-2 right-2 w-8 h-8 border-2 border-indigo-400 opacity-50 rounded-br-lg"></div>
|
||||||
|
|
||||||
|
{/* Academic laurel-like decorations */}
|
||||||
|
<div className="absolute top-1/2 left-1 transform -translate-y-1/2">
|
||||||
|
<div className="w-1 h-6 bg-indigo-300 opacity-40 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-1/2 right-1 transform -translate-y-1/2">
|
||||||
|
<div className="w-1 h-6 bg-indigo-300 opacity-40 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Academic background pattern */}
|
||||||
|
<div className="absolute inset-0 opacity-3">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `radial-gradient(circle at 50% 50%, #6366f1 1px, transparent 1px)`,
|
||||||
|
backgroundSize: '15px 15px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'modern':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Modern clean asymmetric border */}
|
||||||
|
<div className="absolute inset-2 border-2 border-gray-300 opacity-50" style={{
|
||||||
|
clipPath: 'polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px))'
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
{/* Modern accent lines */}
|
||||||
|
<div className="absolute top-2 left-2 w-8 h-0.5 bg-blue-400 opacity-60"></div>
|
||||||
|
<div className="absolute top-2 left-2 w-0.5 h-8 bg-blue-400 opacity-60"></div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-2 right-2 w-8 h-0.5 bg-blue-400 opacity-60"></div>
|
||||||
|
<div className="absolute bottom-2 right-2 w-0.5 h-8 bg-blue-400 opacity-60"></div>
|
||||||
|
|
||||||
|
{/* Modern dot accents */}
|
||||||
|
<div className="absolute top-4 right-4 w-2 h-2 bg-blue-400 opacity-50 rounded-full"></div>
|
||||||
|
<div className="absolute bottom-4 left-4 w-2 h-2 bg-blue-400 opacity-50 rounded-full"></div>
|
||||||
|
|
||||||
|
{/* Modern subtle background */}
|
||||||
|
<div className="absolute inset-0 opacity-2">
|
||||||
|
<div className="w-full h-full" style={{
|
||||||
|
backgroundImage: `linear-gradient(135deg, #3b82f6 0%, transparent 1%), linear-gradient(225deg, #3b82f6 0%, transparent 1%)`,
|
||||||
|
backgroundSize: '12px 12px'
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const theme = getPatternTheme(certificatePattern);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 border border-blue-200 rounded-xl p-4 w-full h-full">
|
||||||
|
<div className="bg-white rounded-lg shadow-sm p-6 relative overflow-hidden w-full h-full flex flex-col">
|
||||||
|
{/* Dynamic Certificate Pattern */}
|
||||||
|
{renderCertificatePattern(certificatePattern)}
|
||||||
|
|
||||||
|
{/* Certificate ID - Top Left */}
|
||||||
|
<div className="absolute top-4 left-4 sm:top-6 sm:left-6 z-20">
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<Hash className={`w-3 h-3 sm:w-4 sm:h-4 ${theme.icon}`} />
|
||||||
|
<span className={`text-xs sm:text-sm ${theme.secondary} font-medium`}>ID: LH-2024-001</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* QR Code Box - Top Right */}
|
||||||
|
<div className="absolute top-4 right-4 sm:top-6 sm:right-6 z-20">
|
||||||
|
<div className={`w-16 h-16 sm:w-24 sm:h-24 border-2 ${theme.secondary.replace('text-', 'border-')} rounded-md bg-white/90 backdrop-blur-sm p-1`}>
|
||||||
|
{qrCodeUrl ? (
|
||||||
|
<img
|
||||||
|
src={qrCodeUrl}
|
||||||
|
alt="Certificate QR Code"
|
||||||
|
className="w-full h-full object-contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-full h-full flex items-center justify-center">
|
||||||
|
<QrCode className={`w-8 h-8 sm:w-12 sm:h-12 ${theme.icon}`} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="relative z-10 flex-1 flex flex-col items-center justify-center text-center space-y-3 px-6 py-6">
|
||||||
|
{/* Header with decorative line */}
|
||||||
|
<div className="flex items-center justify-center space-x-2 mb-2">
|
||||||
|
<div className={`w-6 sm:w-8 h-px bg-gradient-to-r from-transparent ${theme.secondary.replace('text-', 'to-')}`}></div>
|
||||||
|
<div className={`text-xs sm:text-sm ${theme.secondary} font-medium uppercase tracking-wider`}>Certificate</div>
|
||||||
|
<div className={`w-6 sm:w-8 h-px bg-gradient-to-l from-transparent ${theme.secondary.replace('text-', 'to-')}`}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Award Icon with decorative elements */}
|
||||||
|
<div className="flex justify-center relative">
|
||||||
|
<div className={`w-12 h-12 sm:w-16 sm:h-16 bg-gradient-to-br ${theme.icon.replace('text-', 'from-')}-100 ${theme.icon.replace('text-', 'to-')}-200 rounded-full flex items-center justify-center relative`}>
|
||||||
|
<Award className={`w-6 h-6 sm:w-8 sm:h-8 ${theme.icon}`} />
|
||||||
|
{/* Decorative rays */}
|
||||||
|
<div className="absolute inset-0 rounded-full">
|
||||||
|
<div className={`absolute top-0 left-1/2 w-px h-2 sm:h-3 ${theme.secondary.replace('text-', 'bg-')} transform -translate-x-1/2 -translate-y-1 opacity-60`}></div>
|
||||||
|
<div className={`absolute bottom-0 left-1/2 w-px h-2 sm:h-3 ${theme.secondary.replace('text-', 'bg-')} transform -translate-x-1/2 translate-y-1 opacity-60`}></div>
|
||||||
|
<div className={`absolute left-0 top-1/2 w-2 sm:w-3 h-px ${theme.secondary.replace('text-', 'bg-')} transform -translate-y-1/2 -translate-x-1 opacity-60`}></div>
|
||||||
|
<div className={`absolute right-0 top-1/2 w-2 sm:w-3 h-px ${theme.secondary.replace('text-', 'bg-')} transform -translate-y-1/2 translate-x-1 opacity-60`}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Certificate Content */}
|
||||||
|
<div className="flex flex-col justify-center items-center flex-1 max-w-full">
|
||||||
|
<h4 className={`font-bold text-sm sm:text-base ${theme.primary} mb-2 text-center`}>
|
||||||
|
{certificationName || 'Certification Name'}
|
||||||
|
</h4>
|
||||||
|
<p className={`text-xs sm:text-sm ${theme.secondary} text-center leading-relaxed max-w-xs sm:max-w-sm`}>
|
||||||
|
{certificationDescription || 'Certification description will appear here...'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Decorative divider */}
|
||||||
|
<div className="flex items-center justify-center space-x-1 py-1">
|
||||||
|
<div className={`w-2 h-px ${theme.secondary.replace('text-', 'bg-')} opacity-50`}></div>
|
||||||
|
<div className={`w-1 h-1 ${theme.primary.replace('text-', 'bg-')} rounded-full opacity-60`}></div>
|
||||||
|
<div className={`w-2 h-px ${theme.secondary.replace('text-', 'bg-')} opacity-50`}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Certification Type Badge */}
|
||||||
|
<div className={`inline-flex items-center space-x-1 text-xs sm:text-sm ${theme.badge} px-3 py-1 rounded-full border`}>
|
||||||
|
<CheckCircle size={12} />
|
||||||
|
<span className="font-medium">
|
||||||
|
{certificationType === 'completion' ? 'Course Completion' :
|
||||||
|
certificationType === 'achievement' ? 'Achievement Based' :
|
||||||
|
certificationType === 'assessment' ? 'Assessment Based' :
|
||||||
|
certificationType === 'participation' ? 'Participation' :
|
||||||
|
certificationType === 'mastery' ? 'Skill Mastery' :
|
||||||
|
certificationType === 'professional' ? 'Professional Development' :
|
||||||
|
certificationType === 'continuing' ? 'Continuing Education' :
|
||||||
|
certificationType === 'workshop' ? 'Workshop Attendance' :
|
||||||
|
certificationType === 'specialization' ? 'Specialization' : 'Course Completion'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section */}
|
||||||
|
<div className="relative z-10 mt-auto p-6 pt-8">
|
||||||
|
<div className="flex items-end justify-between w-full">
|
||||||
|
{/* Left: Teacher/Organization Signature */}
|
||||||
|
<div className="flex flex-col items-start space-y-1 flex-1">
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<User className={`w-2.5 h-2.5 sm:w-3 sm:h-3 ${theme.icon}`} />
|
||||||
|
<span className={`text-xs ${theme.secondary} font-medium`}>Instructor</span>
|
||||||
|
</div>
|
||||||
|
<div className={`text-xs ${theme.primary} font-semibold`}>
|
||||||
|
{certificateInstructor || 'Dr. Jane Smith'}
|
||||||
|
</div>
|
||||||
|
<div className={`h-px w-10 sm:w-12 ${theme.secondary.replace('text-', 'bg-')} opacity-50`}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Center: Logo */}
|
||||||
|
<div className="flex flex-col items-center space-y-1 flex-1">
|
||||||
|
<div className={`w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center`}>
|
||||||
|
{org?.logo_image ? (
|
||||||
|
<img
|
||||||
|
src={`${getOrgLogoMediaDirectory(org.org_uuid, org?.logo_image)}`}
|
||||||
|
alt="Organization Logo"
|
||||||
|
className="w-full h-full object-contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={`w-full h-full ${theme.icon.replace('text-', 'bg-')}-100 rounded-full flex items-center justify-center`}>
|
||||||
|
<Building className={`w-4 h-4 sm:w-5 sm:h-5 ${theme.icon}`} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={`text-xs ${theme.secondary} font-medium`}>
|
||||||
|
{org?.name || 'LearnHouse'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right: Award Date */}
|
||||||
|
<div className="flex flex-col items-end space-y-1 flex-1">
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<Calendar className={`w-2.5 h-2.5 sm:w-3 sm:h-3 ${theme.icon}`} />
|
||||||
|
<span className={`text-xs ${theme.secondary} font-medium`}>Awarded</span>
|
||||||
|
</div>
|
||||||
|
<div className={`text-xs ${theme.primary} font-semibold`}>
|
||||||
|
Dec 15, 2024
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CertificatePreview;
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
import FormLayout, {
|
||||||
|
FormField,
|
||||||
|
FormLabelAndMessage,
|
||||||
|
Input,
|
||||||
|
Textarea,
|
||||||
|
} from '@components/Objects/StyledElements/Form/Form';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import { AlertTriangle, Award, CheckCircle, FileText, Settings } from 'lucide-react';
|
||||||
|
import CertificatePreview from './CertificatePreview';
|
||||||
|
import * as Form from '@radix-ui/react-form';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
|
||||||
|
import {
|
||||||
|
CustomSelect,
|
||||||
|
CustomSelectContent,
|
||||||
|
CustomSelectItem,
|
||||||
|
CustomSelectTrigger,
|
||||||
|
CustomSelectValue,
|
||||||
|
} from "../EditCourseGeneral/CustomSelect";
|
||||||
|
|
||||||
|
type EditCourseCertificationProps = {
|
||||||
|
orgslug: string
|
||||||
|
course_uuid?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = (values: any) => {
|
||||||
|
const errors = {} as any;
|
||||||
|
|
||||||
|
if (values.enable_certification && !values.certification_name) {
|
||||||
|
errors.certification_name = 'Required when certification is enabled';
|
||||||
|
} else if (values.certification_name && values.certification_name.length > 100) {
|
||||||
|
errors.certification_name = 'Must be 100 characters or less';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.enable_certification && !values.certification_description) {
|
||||||
|
errors.certification_description = 'Required when certification is enabled';
|
||||||
|
} else if (values.certification_description && values.certification_description.length > 500) {
|
||||||
|
errors.certification_description = 'Must be 500 characters or less';
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
function EditCourseCertification(props: EditCourseCertificationProps) {
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const course = useCourse();
|
||||||
|
const dispatchCourse = useCourseDispatch() as any;
|
||||||
|
const { isLoading, courseStructure } = course as any;
|
||||||
|
|
||||||
|
// Create initial values object
|
||||||
|
const getInitialValues = () => {
|
||||||
|
// Helper function to get instructor name from authors
|
||||||
|
const getInstructorName = () => {
|
||||||
|
if (courseStructure?.authors && courseStructure.authors.length > 0) {
|
||||||
|
const author = courseStructure.authors[0];
|
||||||
|
const firstName = author.first_name || '';
|
||||||
|
const lastName = author.last_name || '';
|
||||||
|
|
||||||
|
// Only return if at least one name exists
|
||||||
|
if (firstName || lastName) {
|
||||||
|
return `${firstName} ${lastName}`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
enable_certification: courseStructure?.enable_certification || false,
|
||||||
|
certification_name: courseStructure?.certification_name || courseStructure?.name || '',
|
||||||
|
certification_description: courseStructure?.certification_description || courseStructure?.description || '',
|
||||||
|
certification_type: courseStructure?.certification_type || 'completion',
|
||||||
|
certificate_pattern: courseStructure?.certificate_pattern || 'professional',
|
||||||
|
certificate_instructor: courseStructure?.certificate_instructor || getInstructorName(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: getInitialValues(),
|
||||||
|
validate,
|
||||||
|
onSubmit: async values => {
|
||||||
|
try {
|
||||||
|
// Add your submission logic here
|
||||||
|
dispatchCourse({ type: 'setIsSaved' });
|
||||||
|
} catch (e) {
|
||||||
|
setError('Failed to save certification settings.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableReinitialize: true,
|
||||||
|
}) as any;
|
||||||
|
|
||||||
|
// Reset form when courseStructure changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (courseStructure && !isLoading) {
|
||||||
|
const newValues = getInitialValues();
|
||||||
|
formik.resetForm({ values: newValues });
|
||||||
|
}
|
||||||
|
}, [courseStructure, isLoading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoading) {
|
||||||
|
const formikValues = formik.values as any;
|
||||||
|
const initialValues = formik.initialValues as any;
|
||||||
|
const valuesChanged = Object.keys(formikValues).some(
|
||||||
|
key => formikValues[key] !== initialValues[key]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (valuesChanged) {
|
||||||
|
dispatchCourse({ type: 'setIsNotSaved' });
|
||||||
|
const updatedCourse = {
|
||||||
|
...courseStructure,
|
||||||
|
...formikValues,
|
||||||
|
};
|
||||||
|
dispatchCourse({ type: 'setCourseStructure', payload: updatedCourse });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [formik.values, isLoading]);
|
||||||
|
|
||||||
|
if (isLoading || !courseStructure) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{courseStructure && (
|
||||||
|
<div>
|
||||||
|
<div className="h-6"></div>
|
||||||
|
<div className="mx-4 sm:mx-10 bg-white rounded-xl shadow-xs px-4 py-4">
|
||||||
|
{/* Header Section */}
|
||||||
|
<div className="flex items-center justify-between bg-gray-50 px-3 sm:px-5 py-3 rounded-md mb-3">
|
||||||
|
<div className="flex flex-col -space-y-1">
|
||||||
|
<h1 className="font-bold text-lg sm:text-xl text-gray-800">Course Certification</h1>
|
||||||
|
<h2 className="text-gray-500 text-xs sm:text-sm">
|
||||||
|
Enable and configure certificates for students who complete this course
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<label className="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="sr-only peer"
|
||||||
|
checked={formik.values.enable_certification}
|
||||||
|
onChange={(e) => formik.setFieldValue('enable_certification', e.target.checked)}
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="flex justify-center bg-red-200 rounded-md text-red-950 space-x-2 items-center p-4 mb-6 transition-all shadow-xs">
|
||||||
|
<AlertTriangle size={18} />
|
||||||
|
<div className="font-bold text-sm">{error}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Certification Configuration */}
|
||||||
|
{formik.values.enable_certification && (
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
|
||||||
|
{/* Form Section */}
|
||||||
|
<div className="lg:col-span-3">
|
||||||
|
<FormLayout onSubmit={formik.handleSubmit}>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Basic Information Section */}
|
||||||
|
<div className="flex flex-col bg-gray-50 -space-y-1 px-3 sm:px-5 py-3 rounded-md mb-3">
|
||||||
|
<h3 className="font-bold text-md text-gray-800 flex items-center gap-2">
|
||||||
|
<FileText size={16} />
|
||||||
|
Basic Information
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-500 text-xs sm:text-sm">
|
||||||
|
Configure the basic details of your certification
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
{/* Certification Name */}
|
||||||
|
<FormField name="certification_name">
|
||||||
|
<FormLabelAndMessage
|
||||||
|
label="Certification Name"
|
||||||
|
message={formik.errors.certification_name}
|
||||||
|
/>
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Input
|
||||||
|
style={{ backgroundColor: 'white' }}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.certification_name}
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., Advanced JavaScript Certification"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Certification Type */}
|
||||||
|
<FormField name="certification_type">
|
||||||
|
<FormLabelAndMessage label="Certification Type" />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<CustomSelect
|
||||||
|
value={formik.values.certification_type}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
if (!value) return;
|
||||||
|
formik.setFieldValue('certification_type', value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomSelectTrigger className="w-full bg-white">
|
||||||
|
<CustomSelectValue>
|
||||||
|
{formik.values.certification_type === 'completion' ? 'Course Completion' :
|
||||||
|
formik.values.certification_type === 'achievement' ? 'Achievement Based' :
|
||||||
|
formik.values.certification_type === 'assessment' ? 'Assessment Based' :
|
||||||
|
formik.values.certification_type === 'participation' ? 'Participation' :
|
||||||
|
formik.values.certification_type === 'mastery' ? 'Skill Mastery' :
|
||||||
|
formik.values.certification_type === 'professional' ? 'Professional Development' :
|
||||||
|
formik.values.certification_type === 'continuing' ? 'Continuing Education' :
|
||||||
|
formik.values.certification_type === 'workshop' ? 'Workshop Attendance' :
|
||||||
|
formik.values.certification_type === 'specialization' ? 'Specialization' : 'Course Completion'}
|
||||||
|
</CustomSelectValue>
|
||||||
|
</CustomSelectTrigger>
|
||||||
|
<CustomSelectContent>
|
||||||
|
<CustomSelectItem value="completion">Course Completion</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="achievement">Achievement Based</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="assessment">Assessment Based</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="participation">Participation</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="mastery">Skill Mastery</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="professional">Professional Development</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="continuing">Continuing Education</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="workshop">Workshop Attendance</CustomSelectItem>
|
||||||
|
<CustomSelectItem value="specialization">Specialization</CustomSelectItem>
|
||||||
|
</CustomSelectContent>
|
||||||
|
</CustomSelect>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Certification Description */}
|
||||||
|
<FormField name="certification_description">
|
||||||
|
<FormLabelAndMessage
|
||||||
|
label="Certification Description"
|
||||||
|
message={formik.errors.certification_description}
|
||||||
|
/>
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Textarea
|
||||||
|
style={{ backgroundColor: 'white', height: '120px', minHeight: '120px' }}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.certification_description}
|
||||||
|
placeholder="Describe what this certification represents and its value..."
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Certificate Design Section */}
|
||||||
|
<div className="flex flex-col bg-gray-50 -space-y-1 px-3 sm:px-5 py-3 rounded-md mb-3">
|
||||||
|
<h3 className="font-bold text-md text-gray-800 flex items-center gap-2">
|
||||||
|
<Award size={16} />
|
||||||
|
Certificate Design
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-500 text-xs sm:text-sm">
|
||||||
|
Choose a decorative pattern for your certificate
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pattern Selection */}
|
||||||
|
<FormField name="certificate_pattern">
|
||||||
|
<FormLabelAndMessage label="Certificate Pattern" />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-5 gap-3">
|
||||||
|
{[
|
||||||
|
{ value: 'royal', name: 'Royal', description: 'Ornate with crown motifs' },
|
||||||
|
{ value: 'tech', name: 'Tech', description: 'Circuit-inspired patterns' },
|
||||||
|
{ value: 'nature', name: 'Nature', description: 'Organic leaf patterns' },
|
||||||
|
{ value: 'geometric', name: 'Geometric', description: 'Abstract shapes & lines' },
|
||||||
|
{ value: 'vintage', name: 'Vintage', description: 'Art deco styling' },
|
||||||
|
{ value: 'waves', name: 'Waves', description: 'Flowing water patterns' },
|
||||||
|
{ value: 'minimal', name: 'Minimal', description: 'Clean and simple' },
|
||||||
|
{ value: 'professional', name: 'Professional', description: 'Business-ready design' },
|
||||||
|
{ value: 'academic', name: 'Academic', description: 'Traditional university style' },
|
||||||
|
{ value: 'modern', name: 'Modern', description: 'Contemporary clean lines' }
|
||||||
|
].map((pattern) => (
|
||||||
|
<div
|
||||||
|
key={pattern.value}
|
||||||
|
className={`p-3 border-2 rounded-lg cursor-pointer transition-all ${
|
||||||
|
formik.values.certificate_pattern === pattern.value
|
||||||
|
? 'border-blue-500 bg-blue-50'
|
||||||
|
: 'border-gray-200 hover:border-gray-300'
|
||||||
|
}`}
|
||||||
|
onClick={() => formik.setFieldValue('certificate_pattern', pattern.value)}
|
||||||
|
>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-sm font-medium text-gray-900">{pattern.name}</div>
|
||||||
|
<div className="text-xs text-gray-500 mt-1">{pattern.description}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
{/* Custom Instructor */}
|
||||||
|
<FormField name="certificate_instructor">
|
||||||
|
<FormLabelAndMessage label="Instructor Name (Optional)" />
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Input
|
||||||
|
style={{ backgroundColor: 'white' }}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.certificate_instructor}
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., Dr. Jane Smith"
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
</FormLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Preview Section */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<div className="bg-white rounded-xl shadow-xs border border-gray-200 sticky top-6 min-h-[320px]">
|
||||||
|
<div className="flex flex-col bg-gray-50 -space-y-1 px-3 sm:px-5 py-3 rounded-t-xl mb-3">
|
||||||
|
<h3 className="font-bold text-md text-gray-800 flex items-center gap-2">
|
||||||
|
<Award size={16} />
|
||||||
|
Certificate Preview
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-500 text-xs sm:text-sm">
|
||||||
|
Live preview of your certificate
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4">
|
||||||
|
<CertificatePreview
|
||||||
|
certificationName={formik.values.certification_name}
|
||||||
|
certificationDescription={formik.values.certification_description}
|
||||||
|
certificationType={formik.values.certification_type}
|
||||||
|
certificatePattern={formik.values.certificate_pattern}
|
||||||
|
certificateInstructor={formik.values.certificate_instructor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Disabled State */}
|
||||||
|
{!formik.values.enable_certification && (
|
||||||
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-8 text-center">
|
||||||
|
<Award className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||||
|
<h3 className="font-medium text-gray-700 mb-2">No Certification Configured</h3>
|
||||||
|
<p className="text-sm text-gray-500 mb-4">
|
||||||
|
Enable certification to provide students with certificates upon course completion.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => formik.setFieldValue('enable_certification', true)}
|
||||||
|
className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
<Award size={16} />
|
||||||
|
Enable Certification
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditCourseCertification;
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
"nextjs-toploader": "^1.6.12",
|
"nextjs-toploader": "^1.6.12",
|
||||||
"plyr": "^3.7.8",
|
"plyr": "^3.7.8",
|
||||||
"prosemirror-state": "^1.4.3",
|
"prosemirror-state": "^1.4.3",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"randomcolor": "^0.6.2",
|
"randomcolor": "^0.6.2",
|
||||||
"re-resizable": "^6.11.2",
|
"re-resizable": "^6.11.2",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
|
|
@ -94,6 +95,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4.1.3",
|
"@tailwindcss/postcss": "^4.1.3",
|
||||||
"@types/node": "20.12.2",
|
"@types/node": "20.12.2",
|
||||||
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"@types/react-katex": "^3.0.4",
|
"@types/react-katex": "^3.0.4",
|
||||||
|
|
|
||||||
199
apps/web/pnpm-lock.yaml
generated
199
apps/web/pnpm-lock.yaml
generated
|
|
@ -189,6 +189,9 @@ importers:
|
||||||
prosemirror-state:
|
prosemirror-state:
|
||||||
specifier: ^1.4.3
|
specifier: ^1.4.3
|
||||||
version: 1.4.3
|
version: 1.4.3
|
||||||
|
qrcode:
|
||||||
|
specifier: ^1.5.4
|
||||||
|
version: 1.5.4
|
||||||
randomcolor:
|
randomcolor:
|
||||||
specifier: ^0.6.2
|
specifier: ^0.6.2
|
||||||
version: 0.6.2
|
version: 0.6.2
|
||||||
|
|
@ -256,6 +259,9 @@ importers:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 20.12.2
|
specifier: 20.12.2
|
||||||
version: 20.12.2
|
version: 20.12.2
|
||||||
|
'@types/qrcode':
|
||||||
|
specifier: ^1.5.5
|
||||||
|
version: 1.5.5
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: 19.0.10
|
specifier: 19.0.10
|
||||||
version: 19.0.10
|
version: 19.0.10
|
||||||
|
|
@ -1854,6 +1860,9 @@ packages:
|
||||||
'@types/node@20.12.2':
|
'@types/node@20.12.2':
|
||||||
resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==}
|
resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==}
|
||||||
|
|
||||||
|
'@types/qrcode@1.5.5':
|
||||||
|
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
|
||||||
|
|
||||||
'@types/randomcolor@0.5.9':
|
'@types/randomcolor@0.5.9':
|
||||||
resolution: {integrity: sha512-k58cfpkK15AKn1m+oRd9nh5BnuiowhbyvBBdAzcddtARMr3xRzP0VlFaAKovSG6N6Knx08EicjPlOMzDejerrQ==}
|
resolution: {integrity: sha512-k58cfpkK15AKn1m+oRd9nh5BnuiowhbyvBBdAzcddtARMr3xRzP0VlFaAKovSG6N6Knx08EicjPlOMzDejerrQ==}
|
||||||
|
|
||||||
|
|
@ -2026,6 +2035,10 @@ packages:
|
||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
|
||||||
|
ansi-regex@5.0.1:
|
||||||
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -2135,6 +2148,10 @@ packages:
|
||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
camelcase@5.3.1:
|
||||||
|
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
camelize@1.0.1:
|
camelize@1.0.1:
|
||||||
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
|
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
|
||||||
|
|
||||||
|
|
@ -2151,6 +2168,9 @@ packages:
|
||||||
client-only@0.0.1:
|
client-only@0.0.1:
|
||||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||||
|
|
||||||
|
cliui@6.0.0:
|
||||||
|
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
||||||
|
|
||||||
clsx@2.1.1:
|
clsx@2.1.1:
|
||||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -2262,6 +2282,10 @@ packages:
|
||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decamelize@1.2.0:
|
||||||
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
|
|
@ -2291,6 +2315,9 @@ packages:
|
||||||
devlop@1.1.0:
|
devlop@1.1.0:
|
||||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||||
|
|
||||||
|
dijkstrajs@1.0.3:
|
||||||
|
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
@ -2311,6 +2338,9 @@ packages:
|
||||||
emoji-mart@5.6.0:
|
emoji-mart@5.6.0:
|
||||||
resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==}
|
resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==}
|
||||||
|
|
||||||
|
emoji-regex@8.0.0:
|
||||||
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
emoji-regex@9.2.2:
|
emoji-regex@9.2.2:
|
||||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||||
|
|
||||||
|
|
@ -2528,6 +2558,10 @@ packages:
|
||||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -2575,6 +2609,10 @@ packages:
|
||||||
functions-have-names@1.2.3:
|
functions-have-names@1.2.3:
|
||||||
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -2724,6 +2762,10 @@ packages:
|
||||||
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
|
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
is-generator-function@1.1.0:
|
is-generator-function@1.1.0:
|
||||||
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
|
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -2915,6 +2957,10 @@ packages:
|
||||||
loadjs@4.3.0:
|
loadjs@4.3.0:
|
||||||
resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==}
|
resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -3104,14 +3150,26 @@ packages:
|
||||||
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
p-try@2.2.0:
|
||||||
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -3141,6 +3199,10 @@ packages:
|
||||||
plyr@3.7.8:
|
plyr@3.7.8:
|
||||||
resolution: {integrity: sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA==}
|
resolution: {integrity: sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA==}
|
||||||
|
|
||||||
|
pngjs@5.0.0:
|
||||||
|
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0:
|
possible-typed-array-names@1.1.0:
|
||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -3247,6 +3309,11 @@ packages:
|
||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
qrcode@1.5.4:
|
||||||
|
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
|
@ -3399,10 +3466,17 @@ packages:
|
||||||
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
require-directory@2.1.1:
|
||||||
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
require-in-the-middle@7.5.2:
|
require-in-the-middle@7.5.2:
|
||||||
resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==}
|
resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
|
|
||||||
|
require-main-filename@2.0.0:
|
||||||
|
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -3453,6 +3527,9 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
set-blocking@2.0.0:
|
||||||
|
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -3517,6 +3594,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
string.prototype.includes@2.0.1:
|
string.prototype.includes@2.0.1:
|
||||||
resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
|
resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -3540,6 +3621,10 @@ packages:
|
||||||
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
strip-bom@3.0.0:
|
strip-bom@3.0.0:
|
||||||
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
|
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -3752,6 +3837,9 @@ packages:
|
||||||
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
|
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
which-module@2.0.1:
|
||||||
|
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||||
|
|
||||||
which-typed-array@1.1.19:
|
which-typed-array@1.1.19:
|
||||||
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
|
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -3765,9 +3853,24 @@ packages:
|
||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
y18n@4.0.3:
|
||||||
|
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||||
|
|
||||||
yallist@4.0.0:
|
yallist@4.0.0:
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
|
|
||||||
|
yargs-parser@18.1.3:
|
||||||
|
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
yargs@15.4.1:
|
||||||
|
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
yocto-queue@0.1.0:
|
yocto-queue@0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -5268,6 +5371,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 5.26.5
|
undici-types: 5.26.5
|
||||||
|
|
||||||
|
'@types/qrcode@1.5.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.12.2
|
||||||
|
|
||||||
'@types/randomcolor@0.5.9': {}
|
'@types/randomcolor@0.5.9': {}
|
||||||
|
|
||||||
'@types/react-dom@19.0.4(@types/react@19.0.10)':
|
'@types/react-dom@19.0.4(@types/react@19.0.10)':
|
||||||
|
|
@ -5440,6 +5547,8 @@ snapshots:
|
||||||
json-schema-traverse: 0.4.1
|
json-schema-traverse: 0.4.1
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
|
|
||||||
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|
@ -5577,6 +5686,8 @@ snapshots:
|
||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
|
|
||||||
|
camelcase@5.3.1: {}
|
||||||
|
|
||||||
camelize@1.0.1: {}
|
camelize@1.0.1: {}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001712: {}
|
caniuse-lite@1.0.30001712: {}
|
||||||
|
|
@ -5592,6 +5703,12 @@ snapshots:
|
||||||
|
|
||||||
client-only@0.0.1: {}
|
client-only@0.0.1: {}
|
||||||
|
|
||||||
|
cliui@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 6.2.0
|
||||||
|
|
||||||
clsx@2.1.1: {}
|
clsx@2.1.1: {}
|
||||||
|
|
||||||
cmdk@0.2.1(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
cmdk@0.2.1(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
|
@ -5691,6 +5808,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
deepmerge@2.2.1: {}
|
deepmerge@2.2.1: {}
|
||||||
|
|
@ -5717,6 +5836,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
|
|
||||||
|
dijkstrajs@1.0.3: {}
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
|
|
@ -5749,6 +5870,8 @@ snapshots:
|
||||||
|
|
||||||
emoji-mart@5.6.0: {}
|
emoji-mart@5.6.0: {}
|
||||||
|
|
||||||
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
emoji-regex@9.2.2: {}
|
emoji-regex@9.2.2: {}
|
||||||
|
|
||||||
enhanced-resolve@5.18.1:
|
enhanced-resolve@5.18.1:
|
||||||
|
|
@ -6102,6 +6225,11 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range: 5.0.1
|
to-regex-range: 5.0.1
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
locate-path: 5.0.0
|
||||||
|
path-exists: 4.0.0
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path: 6.0.0
|
locate-path: 6.0.0
|
||||||
|
|
@ -6155,6 +6283,8 @@ snapshots:
|
||||||
|
|
||||||
functions-have-names@1.2.3: {}
|
functions-have-names@1.2.3: {}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
|
@ -6307,6 +6437,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0: {}
|
||||||
|
|
||||||
is-generator-function@1.1.0:
|
is-generator-function@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
|
|
@ -6480,6 +6612,10 @@ snapshots:
|
||||||
|
|
||||||
loadjs@4.3.0: {}
|
loadjs@4.3.0: {}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
p-locate: 4.1.0
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
@ -6681,14 +6817,24 @@ snapshots:
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
safe-push-apply: 1.0.0
|
safe-push-apply: 1.0.0
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
p-try: 2.2.0
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 0.1.0
|
yocto-queue: 0.1.0
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
p-limit: 2.3.0
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
|
|
||||||
|
p-try@2.2.0: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|
@ -6713,6 +6859,8 @@ snapshots:
|
||||||
rangetouch: 2.0.1
|
rangetouch: 2.0.1
|
||||||
url-polyfill: 1.1.13
|
url-polyfill: 1.1.13
|
||||||
|
|
||||||
|
pngjs@5.0.0: {}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
postcss-value-parser@4.2.0: {}
|
postcss-value-parser@4.2.0: {}
|
||||||
|
|
@ -6861,6 +7009,12 @@ snapshots:
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
|
qrcode@1.5.4:
|
||||||
|
dependencies:
|
||||||
|
dijkstrajs: 1.0.3
|
||||||
|
pngjs: 5.0.0
|
||||||
|
yargs: 15.4.1
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
raf-schd@4.0.3: {}
|
raf-schd@4.0.3: {}
|
||||||
|
|
@ -7012,6 +7166,8 @@ snapshots:
|
||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
set-function-name: 2.0.2
|
set-function-name: 2.0.2
|
||||||
|
|
||||||
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
require-in-the-middle@7.5.2:
|
require-in-the-middle@7.5.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
|
|
@ -7020,6 +7176,8 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
require-main-filename@2.0.0: {}
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0: {}
|
resolve-pkg-maps@1.0.0: {}
|
||||||
|
|
@ -7069,6 +7227,8 @@ snapshots:
|
||||||
|
|
||||||
semver@7.7.1: {}
|
semver@7.7.1: {}
|
||||||
|
|
||||||
|
set-blocking@2.0.0: {}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-data-property: 1.1.4
|
define-data-property: 1.1.4
|
||||||
|
|
@ -7193,6 +7353,12 @@ snapshots:
|
||||||
|
|
||||||
streamsearch@1.1.0: {}
|
streamsearch@1.1.0: {}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
string.prototype.includes@2.0.1:
|
string.prototype.includes@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.8
|
call-bind: 1.0.8
|
||||||
|
|
@ -7243,6 +7409,10 @@ snapshots:
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.1
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
|
||||||
strip-bom@3.0.0: {}
|
strip-bom@3.0.0: {}
|
||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
@ -7471,6 +7641,8 @@ snapshots:
|
||||||
is-weakmap: 2.0.2
|
is-weakmap: 2.0.2
|
||||||
is-weakset: 2.0.4
|
is-weakset: 2.0.4
|
||||||
|
|
||||||
|
which-module@2.0.1: {}
|
||||||
|
|
||||||
which-typed-array@1.1.19:
|
which-typed-array@1.1.19:
|
||||||
dependencies:
|
dependencies:
|
||||||
available-typed-arrays: 1.0.7
|
available-typed-arrays: 1.0.7
|
||||||
|
|
@ -7487,8 +7659,35 @@ snapshots:
|
||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
y18n@4.0.3: {}
|
||||||
|
|
||||||
yallist@4.0.0: {}
|
yallist@4.0.0: {}
|
||||||
|
|
||||||
|
yargs-parser@18.1.3:
|
||||||
|
dependencies:
|
||||||
|
camelcase: 5.3.1
|
||||||
|
decamelize: 1.2.0
|
||||||
|
|
||||||
|
yargs@15.4.1:
|
||||||
|
dependencies:
|
||||||
|
cliui: 6.0.0
|
||||||
|
decamelize: 1.2.0
|
||||||
|
find-up: 4.1.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
require-directory: 2.1.1
|
||||||
|
require-main-filename: 2.0.0
|
||||||
|
set-blocking: 2.0.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
which-module: 2.0.1
|
||||||
|
y18n: 4.0.3
|
||||||
|
yargs-parser: 18.1.3
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
youtube-player@5.5.2:
|
youtube-player@5.5.2:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue