diff --git a/package.json b/package.json index cc55384..6b20144 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "next build", - "dev": "PORT=3001 next dev --turbo", + "dev": "PORT=3001 next dev", "check": "biome check", "check:fix": "biome check --fix --unsafe", "preview": "next build && next start", @@ -15,6 +15,7 @@ "dependencies": { "@base-ui/react": "^1.3.0", "@better-auth/passkey": "^1.5.5", + "@dnd-kit/react": "^0.4.0", "@hookform/resolvers": "^3.9.1", "@polinetwork/backend": "^0.16.0", "@radix-ui/react-dialog": "^1.1.15", diff --git a/src/app/dashboard/(active)/telegram/users/[id]/page.tsx b/src/app/dashboard/(active)/telegram/users/[id]/page.tsx index bc83ede..3f01a78 100644 --- a/src/app/dashboard/(active)/telegram/users/[id]/page.tsx +++ b/src/app/dashboard/(active)/telegram/users/[id]/page.tsx @@ -11,7 +11,7 @@ import { NewGroupAdmin } from "./new-group-admin" export default async function TgUserDetails({ params }: { params: Promise<{ id: string }> }) { const { id } = await params const parsedInt = parseInt(id, 10) - if (isNaN(parsedInt)) notFound() + if (Number.isNaN(parsedInt)) notFound() const data = await getUserDetails(parsedInt) if (!data) notFound() diff --git a/src/app/dashboard/(active)/web/projects/card-project.tsx b/src/app/dashboard/(active)/web/projects/card-project.tsx new file mode 100644 index 0000000..8090790 --- /dev/null +++ b/src/app/dashboard/(active)/web/projects/card-project.tsx @@ -0,0 +1,243 @@ +"use client" + +import { useSortable } from "@dnd-kit/react/sortable" +import { Languages, Link, LucidePencil, Save, Upload, X } from "lucide-react" +import Image from "next/image" +import type { ChangeEvent } from "react" +import { useEffect, useState } from "react" +import { ProjectCategoryMenu } from "@/app/dashboard/(active)/web/projects/category-menu" +import { DeleteDialog } from "@/components/delete-dialog" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { getInitials } from "@/lib/utils" +import { cn } from "@/lib/utils/shadcn" +import type { CardProjectProps } from "./types" + +export default function CardProject(item: CardProjectProps) { + const { ref } = useSortable({ + id: item.id, + index: item.sortableIndex ?? 0, + group: item.category, + disabled: item.sortableIndex === undefined, + }) + const iconInputId = `project-icon-${item.id}` + const [editActive, setEditActive] = useState(item.initialEditActive ?? false) + const [title, setTitle] = useState(item.title) + const [logoFile, setLogoFile] = useState(null) + const [logoPreview, setLogoPreview] = useState(null) + const [descriptionIt, setDescriptionIt] = useState(item.descriptionIt) + const [descriptionEn, setDescriptionEn] = useState(item.descriptionEn) + const [link, setLinks] = useState(item.link) + const [pending, setPending] = useState(false) + const initials = getInitials(title) + + async function handleIconUpload(event: ChangeEvent) { + const file = event.target.files?.[0] + if (!file) return + setLogoFile(file) + setLogoPreview(URL.createObjectURL(file)) + } + + useEffect(() => { + return () => { + if (logoPreview) URL.revokeObjectURL(logoPreview) + } + }, [logoPreview]) + + // If it's draft, remove the card, otherwise reset the values to the original ones + function handleCancelEdit() { + if (item.isDraft) { + item.onCancelCreate?.() + return + } + + setTitle(item.title) + setLogoFile(null) + setLogoPreview(null) + setDescriptionIt(item.descriptionIt) + setDescriptionEn(item.descriptionEn) + setLinks(item.link) + setEditActive(false) + } + + // TODO: forse spostare la cosa salvata per ultima nella lista? Perche poi ordinata per id finisce li + // se gli id sono crescenti. O tipo la creo direttamente ultima e non in cima? Pero poi devi scorrere per editarla + async function saveChanges() { + if (pending) return + + setPending(true) + try { + const saved = await item.onSave({ + id: item.id, + title, + logo: item.logo, + logoFile, + descriptionIt, + descriptionEn, + link, + category: item.category, + }) + if (saved) { + setLogoFile(null) + setLogoPreview(null) + setEditActive(false) + } + } finally { + setPending(false) + } + } + + function renderIcon() { + const logoSource = logoPreview ?? item.logo + if (logoSource) { + return + } + return ( + + ) + } + + return ( + + + + {editActive ? ( + <> + +
+ setTitle(event.target.value)} + className="text-lg font-medium" + /> +
+ + ) : ( + <> + {renderIcon()} + {title} + + )} +
+ + {editActive ? ( + <> + + + + ) : ( + <> + + + + + + )} + +
+ + +
+ + + Link + + setLinks(event.target.value)} + className={cn( + "border-0 bg-transparent p-3 shadow-none", + !editActive && + "cursor-default resize-none focus-visible:border-transparent focus-visible:ring-0 focus-visible:ring-transparent" + )} + /> +
+
+ + + IT + +