mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add unsplash image chooser for courses thumbnails
This commit is contained in:
parent
3f1e9ecb7f
commit
cc68ea2e94
5 changed files with 222 additions and 25 deletions
|
|
@ -3,10 +3,11 @@ import { useOrg } from '@components/Contexts/OrgContext'
|
||||||
import { getAPIUrl } from '@services/config/config'
|
import { getAPIUrl } from '@services/config/config'
|
||||||
import { updateCourseThumbnail } from '@services/courses/courses'
|
import { updateCourseThumbnail } from '@services/courses/courses'
|
||||||
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
|
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
|
||||||
import { ArrowBigUpDash, UploadCloud } from 'lucide-react'
|
import { ArrowBigUpDash, UploadCloud, Image as ImageIcon } from 'lucide-react'
|
||||||
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
import { useLHSession } from '@components/Contexts/LHSessionContext'
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { mutate } from 'swr'
|
import { mutate } from 'swr'
|
||||||
|
import UnsplashImagePicker from './UnsplashImagePicker'
|
||||||
|
|
||||||
function ThumbnailUpdate() {
|
function ThumbnailUpdate() {
|
||||||
const course = useCourse() as any
|
const course = useCourse() as any
|
||||||
|
|
@ -15,10 +16,24 @@ function ThumbnailUpdate() {
|
||||||
const [localThumbnail, setLocalThumbnail] = React.useState(null) as any
|
const [localThumbnail, setLocalThumbnail] = React.useState(null) as any
|
||||||
const [isLoading, setIsLoading] = React.useState(false) as any
|
const [isLoading, setIsLoading] = React.useState(false) as any
|
||||||
const [error, setError] = React.useState('') as any
|
const [error, setError] = React.useState('') as any
|
||||||
|
const [showUnsplashPicker, setShowUnsplashPicker] = useState(false)
|
||||||
|
|
||||||
const handleFileChange = async (event: any) => {
|
const handleFileChange = async (event: any) => {
|
||||||
const file = event.target.files[0]
|
const file = event.target.files[0]
|
||||||
setLocalThumbnail(file)
|
setLocalThumbnail(file)
|
||||||
|
await updateThumbnail(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUnsplashSelect = async (imageUrl: string) => {
|
||||||
|
setIsLoading(true)
|
||||||
|
const response = await fetch(imageUrl)
|
||||||
|
const blob = await response.blob()
|
||||||
|
const file = new File([blob], 'unsplash_image.jpg', { type: 'image/jpeg' })
|
||||||
|
setLocalThumbnail(file)
|
||||||
|
await updateThumbnail(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateThumbnail = async (file: File) => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const res = await updateCourseThumbnail(
|
const res = await updateCourseThumbnail(
|
||||||
course.courseStructure.course_uuid,
|
course.courseStructure.course_uuid,
|
||||||
|
|
@ -49,8 +64,7 @@ function ThumbnailUpdate() {
|
||||||
{localThumbnail ? (
|
{localThumbnail ? (
|
||||||
<img
|
<img
|
||||||
src={URL.createObjectURL(localThumbnail)}
|
src={URL.createObjectURL(localThumbnail)}
|
||||||
className={`${isLoading ? 'animate-pulse' : ''
|
className={`${isLoading ? 'animate-pulse' : ''} shadow w-[200px] h-[100px] rounded-md`}
|
||||||
} shadow w-[200px] h-[100px] rounded-md`}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
|
|
@ -65,19 +79,13 @@ function ThumbnailUpdate() {
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex justify-center items-center">
|
<div className="flex justify-center items-center">
|
||||||
<input
|
<div className="font-bold animate-pulse antialiased items-center bg-green-200 text-gray text-sm rounded-md px-4 py-2 mt-4 flex">
|
||||||
type="file"
|
|
||||||
id="fileInput"
|
|
||||||
style={{ display: 'none' }}
|
|
||||||
onChange={handleFileChange}
|
|
||||||
/>
|
|
||||||
<div className="font-bold animate-pulse antialiased items-center bg-green-200 text-gray text-sm rounded-md px-4 py-2 mt-4 flex">
|
|
||||||
<ArrowBigUpDash size={16} className="mr-2" />
|
<ArrowBigUpDash size={16} className="mr-2" />
|
||||||
<span>Uploading</span>
|
<span>Uploading</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center">
|
<div className="flex justify-center items-center space-x-2">
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
|
|
@ -85,18 +93,31 @@ function ThumbnailUpdate() {
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="font-bold antialiased items-center text-gray text-sm rounded-md px-4 mt-6 flex"
|
className="font-bold antialiased items-center text-gray text-sm rounded-md px-4 mt-6 flex"
|
||||||
onClick={() => document.getElementById('fileInput')?.click()}
|
onClick={() => document.getElementById('fileInput')?.click()}
|
||||||
>
|
>
|
||||||
<UploadCloud size={16} className="mr-2" />
|
<UploadCloud size={16} className="mr-2" />
|
||||||
<span>Change Thumbnail</span>
|
<span>Upload Image</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="font-bold antialiased items-center text-gray text-sm rounded-md px-4 mt-6 flex"
|
||||||
|
onClick={() => setShowUnsplashPicker(true)}
|
||||||
|
>
|
||||||
|
<ImageIcon size={16} className="mr-2" />
|
||||||
|
<span>Choose from Gallery</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{showUnsplashPicker && (
|
||||||
|
<UnsplashImagePicker
|
||||||
|
onSelect={handleUnsplashSelect}
|
||||||
|
onClose={() => setShowUnsplashPicker(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ThumbnailUpdate
|
export default ThumbnailUpdate
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createApi } from 'unsplash-js';
|
||||||
|
import { Search, X, Cpu, Briefcase, GraduationCap, Heart, Palette, Plane, Utensils,
|
||||||
|
Dumbbell, Music, Shirt, Book, Building, Bike, Camera, Microscope, Coins, Coffee, Gamepad,
|
||||||
|
Flower} from 'lucide-react';
|
||||||
|
|
||||||
|
const unsplash = createApi({
|
||||||
|
accessKey: process.env.NEXT_PUBLIC_UNSPLASH_ACCESS_KEY as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
const IMAGES_PER_PAGE = 20;
|
||||||
|
|
||||||
|
const predefinedLabels = [
|
||||||
|
{ name: 'Nature', icon: Flower },
|
||||||
|
{ name: 'Technology', icon: Cpu },
|
||||||
|
{ name: 'Business', icon: Briefcase },
|
||||||
|
{ name: 'Education', icon: GraduationCap },
|
||||||
|
{ name: 'Health', icon: Heart },
|
||||||
|
{ name: 'Art', icon: Palette },
|
||||||
|
{ name: 'Science', icon: Microscope },
|
||||||
|
{ name: 'Travel', icon: Plane },
|
||||||
|
{ name: 'Food', icon: Utensils },
|
||||||
|
{ name: 'Sports', icon: Dumbbell },
|
||||||
|
{ name: 'Music', icon: Music },
|
||||||
|
{ name: 'Fashion', icon: Shirt },
|
||||||
|
{ name: 'History', icon: Book },
|
||||||
|
{ name: 'Architecture', icon: Building },
|
||||||
|
{ name: 'Fitness', icon: Bike },
|
||||||
|
{ name: 'Photography', icon: Camera },
|
||||||
|
{ name: 'Biology', icon: Microscope },
|
||||||
|
{ name: 'Finance', icon: Coins },
|
||||||
|
{ name: 'Lifestyle', icon: Coffee },
|
||||||
|
{ name: 'Gaming', icon: Gamepad },
|
||||||
|
];
|
||||||
|
|
||||||
|
interface UnsplashImagePickerProps {
|
||||||
|
onSelect: (imageUrl: string) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsplashImagePicker: React.FC<UnsplashImagePickerProps> = ({ onSelect, onClose }) => {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [images, setImages] = useState<any[]>([]);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const fetchImages = useCallback(async (searchQuery: string, pageNum: number) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await unsplash.search.getPhotos({
|
||||||
|
query: searchQuery,
|
||||||
|
page: pageNum,
|
||||||
|
perPage: IMAGES_PER_PAGE,
|
||||||
|
});
|
||||||
|
if (result && result.response) {
|
||||||
|
setImages(prevImages => pageNum === 1 ? result.response.results : [...prevImages, ...result.response.results]);
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected response structure:', result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching images:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const debouncedFetchImages = useCallback(
|
||||||
|
debounce((searchQuery: string) => {
|
||||||
|
setPage(1);
|
||||||
|
fetchImages(searchQuery, 1);
|
||||||
|
}, 300),
|
||||||
|
[fetchImages]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (query) {
|
||||||
|
debouncedFetchImages(query);
|
||||||
|
}
|
||||||
|
}, [query, debouncedFetchImages]);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setQuery(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLabelClick = (label: string) => {
|
||||||
|
setQuery(label);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLoadMore = () => {
|
||||||
|
const nextPage = page + 1;
|
||||||
|
setPage(nextPage);
|
||||||
|
fetchImages(query, nextPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageSelect = (imageUrl: string) => {
|
||||||
|
onSelect(imageUrl);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
|
<div className="bg-white rounded-lg p-6 w-3/4 max-w-4xl max-h-[80vh] overflow-y-auto">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-2xl font-bold">Choose an image from Unsplash</h2>
|
||||||
|
<button onClick={onClose} className="text-gray-500 hover:text-gray-700">
|
||||||
|
<X size={24} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="relative mb-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={query}
|
||||||
|
onChange={handleSearch}
|
||||||
|
placeholder="Search for images..."
|
||||||
|
className="w-full p-2 pl-10 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 mb-4">
|
||||||
|
{predefinedLabels.map(label => (
|
||||||
|
<button
|
||||||
|
key={label.name}
|
||||||
|
onClick={() => handleLabelClick(label.name)}
|
||||||
|
className="px-3 py-1 bg-neutral-100 rounded-lg hover:bg-neutral-200 nice-shadow transition-colors flex items-center gap-1 space-x-1"
|
||||||
|
>
|
||||||
|
<label.icon size={16} />
|
||||||
|
<span>{label.name}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{images.map(image => (
|
||||||
|
<div key={image.id} className="relative w-full pb-[56.25%]">
|
||||||
|
<img
|
||||||
|
src={image.urls.small}
|
||||||
|
alt={image.alt_description}
|
||||||
|
className="absolute inset-0 w-full h-full object-cover rounded-lg cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
onClick={() => handleImageSelect(image.urls.full)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{loading && <p className="text-center mt-4">Loading...</p>}
|
||||||
|
{!loading && images.length > 0 && (
|
||||||
|
<button
|
||||||
|
onClick={handleLoadMore}
|
||||||
|
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
|
||||||
|
>
|
||||||
|
Load More
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom debounce function
|
||||||
|
const debounce = (func: Function, delay: number) => {
|
||||||
|
let timeoutId: NodeJS.Timeout;
|
||||||
|
return (...args: any[]) => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(() => func(...args), delay);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnsplashImagePicker;
|
||||||
|
|
@ -175,7 +175,7 @@ function UserEditGeneral() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<UploadCloud size={16} className="mr-2" />
|
<UploadCloud size={16} className="mr-2" />
|
||||||
<span>Change Thumbnail</span>
|
<span>Change Avatar</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@
|
||||||
"tailwind-merge": "^2.5.3",
|
"tailwind-merge": "^2.5.3",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"unsplash-js": "^7.0.19",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"y-indexeddb": "^9.0.12",
|
"y-indexeddb": "^9.0.12",
|
||||||
"y-prosemirror": "^1.2.12",
|
"y-prosemirror": "^1.2.12",
|
||||||
|
|
|
||||||
27
apps/web/pnpm-lock.yaml
generated
27
apps/web/pnpm-lock.yaml
generated
|
|
@ -176,6 +176,9 @@ importers:
|
||||||
tailwindcss-animate:
|
tailwindcss-animate:
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(tailwindcss@3.4.13)
|
version: 1.0.7(tailwindcss@3.4.13)
|
||||||
|
unsplash-js:
|
||||||
|
specifier: ^7.0.19
|
||||||
|
version: 7.0.19
|
||||||
uuid:
|
uuid:
|
||||||
specifier: ^9.0.1
|
specifier: ^9.0.1
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
|
|
@ -3818,6 +3821,10 @@ packages:
|
||||||
unplugin@1.0.1:
|
unplugin@1.0.1:
|
||||||
resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==}
|
resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==}
|
||||||
|
|
||||||
|
unsplash-js@7.0.19:
|
||||||
|
resolution: {integrity: sha512-j6qT2floy5Q2g2d939FJpwey1yw/GpQecFiSouyJtsHQPj3oqmqq3K4rI+GF8vU1zwGCT7ZwIGQd2dtCQLjYJw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
update-browserslist-db@1.1.1:
|
update-browserslist-db@1.1.1:
|
||||||
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
|
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -6271,8 +6278,8 @@ snapshots:
|
||||||
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
|
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
|
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
|
||||||
eslint-plugin-react: 7.37.1(eslint@8.57.1)
|
eslint-plugin-react: 7.37.1(eslint@8.57.1)
|
||||||
eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
|
eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
|
||||||
|
|
@ -6291,37 +6298,37 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
debug: 4.3.7
|
debug: 4.3.7
|
||||||
enhanced-resolve: 5.17.1
|
enhanced-resolve: 5.17.1
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
get-tsconfig: 4.8.1
|
get-tsconfig: 4.8.1
|
||||||
is-bun-module: 1.2.1
|
is-bun-module: 1.2.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@typescript-eslint/parser'
|
- '@typescript-eslint/parser'
|
||||||
- eslint-import-resolver-node
|
- eslint-import-resolver-node
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
|
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
|
|
@ -6332,7 +6339,7 @@ snapshots:
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.15.1
|
is-core-module: 2.15.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
|
@ -8034,6 +8041,8 @@ snapshots:
|
||||||
webpack-sources: 3.2.3
|
webpack-sources: 3.2.3
|
||||||
webpack-virtual-modules: 0.5.0
|
webpack-virtual-modules: 0.5.0
|
||||||
|
|
||||||
|
unsplash-js@7.0.19: {}
|
||||||
|
|
||||||
update-browserslist-db@1.1.1(browserslist@4.24.0):
|
update-browserslist-db@1.1.1(browserslist@4.24.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.24.0
|
browserslist: 4.24.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue