diff --git a/resources/js/components/ui/grid-skeleton.jsx b/resources/js/components/ui/grid-skeleton.jsx
index 41ff6ee..d63c782 100644
--- a/resources/js/components/ui/grid-skeleton.jsx
+++ b/resources/js/components/ui/grid-skeleton.jsx
@@ -5,7 +5,7 @@ const GridSkeleton = ({ itemCount = 6 }) => {
{Array.from({ length: itemCount }, (_, index) => (
))}
diff --git a/resources/js/components/ui/media-item-skeleton.jsx b/resources/js/components/ui/media-item-skeleton.jsx
new file mode 100644
index 0000000..15083d4
--- /dev/null
+++ b/resources/js/components/ui/media-item-skeleton.jsx
@@ -0,0 +1,7 @@
+const MediaItemSkeleton = () => {
+ return (
+
+ );
+};
+
+export { MediaItemSkeleton };
\ No newline at end of file
diff --git a/resources/js/components/ui/media-item.jsx b/resources/js/components/ui/media-item.jsx
new file mode 100644
index 0000000..7748d0a
--- /dev/null
+++ b/resources/js/components/ui/media-item.jsx
@@ -0,0 +1,48 @@
+import { useState } from 'react';
+import { MediaItemSkeleton } from './media-item-skeleton';
+
+const MediaItem = ({ src, alt, onClick, isSelected, className = "" }) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [hasError, setHasError] = useState(false);
+
+ const handleImageLoad = () => {
+ setIsLoading(false);
+ };
+
+ const handleImageError = () => {
+ setIsLoading(false);
+ setHasError(true);
+ };
+
+ return (
+
+ {isLoading && (
+
+
+
+ )}
+
+ {hasError ? (
+
+ Failed to load
+
+ ) : (
+

+ )}
+
+ {/* Selection border overlay */}
+ {isSelected && !isLoading && (
+
+ )}
+
+ );
+};
+
+export { MediaItem };
\ No newline at end of file
diff --git a/resources/js/modules/editor/partials/edit-sidebar.jsx b/resources/js/modules/editor/partials/edit-sidebar.jsx
index e421d3d..15034e9 100644
--- a/resources/js/modules/editor/partials/edit-sidebar.jsx
+++ b/resources/js/modules/editor/partials/edit-sidebar.jsx
@@ -1,6 +1,7 @@
import { Button } from '@/components/ui/button';
import { GridSkeleton } from '@/components/ui/grid-skeleton';
import { Input } from '@/components/ui/input';
+import { MediaItem } from '@/components/ui/media-item';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { cn } from '@/lib/utils';
@@ -28,17 +29,17 @@ export default function EditSidebar({ isOpen, onClose }) {
// Track the current active tab
const [activeTab, setActiveTab] = useState('memes');
-
+
// Track search queries
const [searchQueries, setSearchQueries] = useState({
memes: '',
- backgrounds: ''
+ backgrounds: '',
});
-
+
// Track if data has been loaded for each tab (to prevent infinite loading on empty results)
const [dataLoaded, setDataLoaded] = useState({
memes: false,
- backgrounds: false
+ backgrounds: false,
});
// Fetch data when sidebar opens for the current active tab
@@ -48,22 +49,22 @@ export default function EditSidebar({ isOpen, onClose }) {
const query = searchQueries.memes;
if (query) {
searchMemes(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
} else {
fetchMemes().finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
}
} else if (activeTab === 'backgrounds' && !dataLoaded.backgrounds && !isFetchingBackgrounds) {
const query = searchQueries.backgrounds;
if (query) {
searchBackgrounds(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
} else {
fetchBackgrounds().finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
}
}
@@ -76,22 +77,22 @@ export default function EditSidebar({ isOpen, onClose }) {
const query = searchQueries.memes;
if (query) {
searchMemes(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
} else {
fetchMemes().finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
}
} else if (value === 'backgrounds' && !dataLoaded.backgrounds && !isFetchingBackgrounds) {
const query = searchQueries.backgrounds;
if (query) {
searchBackgrounds(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
} else {
fetchBackgrounds().finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
}
}
@@ -99,9 +100,9 @@ export default function EditSidebar({ isOpen, onClose }) {
// Handle search input changes
const handleSearchChange = (value) => {
- setSearchQueries(prev => ({
+ setSearchQueries((prev) => ({
...prev,
- [activeTab]: value
+ [activeTab]: value,
}));
};
@@ -110,30 +111,30 @@ export default function EditSidebar({ isOpen, onClose }) {
const query = searchQueries[activeTab];
if (activeTab === 'memes') {
searchMemes(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
} else if (activeTab === 'backgrounds') {
searchBackgrounds(query).finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
}
};
// Handle clearing search
const handleClearSearch = () => {
- setSearchQueries(prev => ({
+ setSearchQueries((prev) => ({
...prev,
- [activeTab]: ''
+ [activeTab]: '',
}));
-
+
// Reset data loaded state and fetch fresh data without search
if (activeTab === 'memes') {
fetchMemes().finally(() => {
- setDataLoaded(prev => ({ ...prev, memes: true }));
+ setDataLoaded((prev) => ({ ...prev, memes: true }));
});
} else if (activeTab === 'backgrounds') {
fetchBackgrounds().finally(() => {
- setDataLoaded(prev => ({ ...prev, backgrounds: true }));
+ setDataLoaded((prev) => ({ ...prev, backgrounds: true }));
});
}
};
@@ -178,7 +179,7 @@ export default function EditSidebar({ isOpen, onClose }) {
) : (
-
+
No meme
)}
@@ -197,7 +198,7 @@ export default function EditSidebar({ isOpen, onClose }) {
) : (
-
+
No background
)}
@@ -216,56 +217,61 @@ export default function EditSidebar({ isOpen, onClose }) {
{/* Search Bar */}
{isFetchingBackgrounds && }
- {!isFetchingBackgrounds && backgrounds.length === 0 && No backgrounds found.
}
+ {!isFetchingBackgrounds && backgrounds.length === 0 && (
+
+
No backgrounds found.
+
+ {searchQueries.backgrounds && (
+
+ )}
+ {import.meta.env.VITE_DISCORD_LINK && (
+
+ )}
+
+
+ )}
{!isFetchingBackgrounds && backgrounds.length > 0 && (
<>
-
+
{backgrounds.map((item, index) => (
-
+ isSelected={selectedBackground?.ids === item.ids}
+ className="transition-all hover:ring-2 hover:ring-blue-500"
+ />
))}
>
@@ -274,21 +280,37 @@ export default function EditSidebar({ isOpen, onClose }) {
{isFetchingMemes && }
- {!isFetchingMemes && memes.length === 0 && No memes found.
}
+ {!isFetchingMemes && memes.length === 0 && (
+
+
No memes found.
+
+ {searchQueries.memes && (
+
+ )}
+ {import.meta.env.VITE_DISCORD_LINK && (
+
+ )}
+
+
+ )}
{!isFetchingMemes && memes.length > 0 && (
<>
{memes.map((item, index) => (
-
+ isSelected={selectedMeme?.ids === item.ids}
+ className="transition-all hover:ring-2 hover:ring-blue-500"
+ />
))}
>