Shortcodes
This commit is contained in:
@@ -5,19 +5,18 @@ import Link from "next/link";
|
||||
import { API_URL } from "@/lib/constants";
|
||||
import Image from "next/image";
|
||||
|
||||
const Hero = () => {
|
||||
const background = `${API_URL}/media/591ab183-c72d-46ff-905c-ec04fed1bb34/file`;
|
||||
const Hero: React.FC<{ background: string }> = ({ background }) => {
|
||||
return (
|
||||
<section className="relative flex h-[calc(100vh-68px)] items-center justify-center overflow-hidden py-32">
|
||||
<div className="">
|
||||
<Image
|
||||
src={background}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
// objectFit="cover"
|
||||
priority
|
||||
alt="Hero image"
|
||||
unoptimized
|
||||
className="grayscale"
|
||||
className="grayscale object-cover "
|
||||
/>
|
||||
{/* Gradient and Blur Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-transparent bg-opacity-30 backdrop-blur-sm"></div>
|
||||
@@ -29,10 +28,12 @@ const Hero = () => {
|
||||
className="h-16"
|
||||
/>
|
||||
<div>
|
||||
<h1 className="mb-6 text-pretty text-2xl font-bold text-primary lg:text-5xl">
|
||||
Trouvez votre équilibre avec
|
||||
<h1 className="mb-6 text-pretty text-2xl font-bold text-primary lg:text-5xl font-times">
|
||||
Trouvez votre <em>équilibre</em> avec
|
||||
<br />
|
||||
Latosa-Escrima
|
||||
<span className="font-extrabold text-3xl lg:text-6xl">
|
||||
Latosa Escrima
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-muted-foreground lg:text-xl">
|
||||
Une évolution des arts martiaux Philippins
|
||||
|
||||
@@ -23,7 +23,7 @@ interface PhotoDialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onDelete?: (id: Media["id"]) => void;
|
||||
onSave: (photo: Omit<Media, "id">, file: File) => void;
|
||||
onSave: (photo: Omit<Media, "id"> | Media, file: File) => void;
|
||||
}
|
||||
|
||||
export function PhotoDialog({
|
||||
@@ -34,7 +34,7 @@ export function PhotoDialog({
|
||||
onSave,
|
||||
}: PhotoDialogProps) {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [newPhoto, setNewPhoto] = useState<Omit<Media, "id">>({
|
||||
const [newPhoto, setNewPhoto] = useState<Omit<Media, "id"> | Media>({
|
||||
url: "",
|
||||
alt: "",
|
||||
path: "",
|
||||
|
||||
@@ -14,6 +14,18 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import type IShortcode from "@/interfaces/IShortcode";
|
||||
import Image from "next/image";
|
||||
import Media from "@/interfaces/Media";
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "./ui/pagination";
|
||||
import useMedia from "@/hooks/use-media";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
interface ShortcodeDialogProps {
|
||||
onSave: (shortcode: IShortcode) => void;
|
||||
@@ -33,6 +45,8 @@ export default function ShortcodeDialog({
|
||||
);
|
||||
|
||||
const handleSave = () => {
|
||||
if (!(_shortcode?.code && (_shortcode.media_id || _shortcode.value)))
|
||||
return;
|
||||
onSave(_shortcode);
|
||||
setOpen();
|
||||
resetForm();
|
||||
@@ -92,6 +106,7 @@ export default function ShortcodeDialog({
|
||||
setShortcode((p) => ({
|
||||
...p,
|
||||
value: e.target.value,
|
||||
media_id: undefined,
|
||||
}))
|
||||
}
|
||||
className="col-span-3"
|
||||
@@ -99,27 +114,29 @@ export default function ShortcodeDialog({
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="media">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="mediaId" className="text-right">
|
||||
Media ID
|
||||
</Label>
|
||||
<Input
|
||||
id="mediaId"
|
||||
value={_shortcode.media_id}
|
||||
onChange={(e) =>
|
||||
setShortcode((p) => ({
|
||||
...p,
|
||||
media_id: e.target.value,
|
||||
}))
|
||||
}
|
||||
className="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<PhotoGrid
|
||||
onSelect={(photo) => {
|
||||
setShortcode((p) => ({
|
||||
...p,
|
||||
media_id: photo.id,
|
||||
value: undefined,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="submit" onClick={handleSave}>
|
||||
<Button
|
||||
disabled={
|
||||
!(
|
||||
_shortcode?.code &&
|
||||
(_shortcode.media_id || _shortcode.value)
|
||||
)
|
||||
}
|
||||
type="submit"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Enregistrer
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -127,3 +144,144 @@ export default function ShortcodeDialog({
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
const LazyImage = ({
|
||||
photo,
|
||||
onClick,
|
||||
isSelected,
|
||||
}: {
|
||||
photo: Media;
|
||||
onClick: () => void;
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`aspect-square ${isSelected ? "ring-4 ring-primary" : ""}`}
|
||||
>
|
||||
<Image
|
||||
src={photo.url || "/placeholder.svg"}
|
||||
alt={photo.alt}
|
||||
width={300}
|
||||
height={300}
|
||||
className="object-cover w-full h-full cursor-pointer transition-transform hover:scale-105"
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PhotoGrid: React.FC<{ onSelect: (photo: Media) => void }> = ({
|
||||
onSelect,
|
||||
}) => {
|
||||
const { data, error, isLoading, success, setPage, setLimit, mutate } =
|
||||
useMedia(5);
|
||||
const [selectedPhoto, setSelectedPhoto] = useState<Media | null>(null);
|
||||
|
||||
const handlePhotoClick = (photo: Media) => {
|
||||
setSelectedPhoto(photo);
|
||||
onSelect(photo);
|
||||
};
|
||||
|
||||
const handleChangeSelection = () => {
|
||||
setSelectedPhoto(null);
|
||||
// if (!photos) return;
|
||||
// const currentIndex = photos.findIndex(
|
||||
// (photo) => photo.id === selectedPhoto?.id,
|
||||
// );
|
||||
// const nextIndex = (currentIndex + 1) % photos.length;
|
||||
// setSelectedPhoto(photos[nextIndex]);
|
||||
};
|
||||
|
||||
if (isLoading) return <Loader2 className="animate-spin" />;
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">Gallerie Photo</h1>
|
||||
{selectedPhoto ? (
|
||||
<div className="flex flex-col items-center">
|
||||
<Image
|
||||
src={selectedPhoto.url || "/placeholder.svg"}
|
||||
alt={selectedPhoto.alt}
|
||||
width={600}
|
||||
height={600}
|
||||
className="w-full max-w-2xl h-auto mb-4 rounded-lg"
|
||||
/>
|
||||
<div className="flex gap-4 mt-4">
|
||||
<Button onClick={handleChangeSelection}>
|
||||
Changer de sélection
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
||||
{data?.items?.map((photo) => {
|
||||
return (
|
||||
<LazyImage
|
||||
photo={photo}
|
||||
key={photo.id}
|
||||
onClick={() => handlePhotoClick(photo)}
|
||||
isSelected={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Pagination className="mt-8">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setPage((prev) =>
|
||||
Math.max(prev - 1, 1),
|
||||
);
|
||||
}}
|
||||
className={
|
||||
data?.page === 1
|
||||
? "pointer-events-none opacity-50"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{[...Array(data?.totalPages)].map((_, i) => (
|
||||
<PaginationItem key={i + 1}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setPage(i + 1);
|
||||
}}
|
||||
isActive={data?.page === i + 1}
|
||||
>
|
||||
{i + 1}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setPage((prev) =>
|
||||
Math.min(
|
||||
prev + 1,
|
||||
data?.totalPages ?? 1,
|
||||
),
|
||||
);
|
||||
}}
|
||||
className={
|
||||
data?.page === data?.totalPages
|
||||
? "pointer-events-none opacity-50"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user