| "use client"; |
|
|
| import { useTransition, useState } from "react"; |
| import { useRouter } from "next/navigation"; |
| import { languageName, LANGUAGES } from "@/lib/languages"; |
| import { X, Plus } from "lucide-react"; |
| import ReactCountryFlag from "react-country-flag"; |
|
|
| export function LearningLanguagesEditor({ |
| targetLangs, |
| nativeLang, |
| level, |
| }: { |
| targetLangs: string[]; |
| nativeLang: string; |
| level: string; |
| }) { |
| const router = useRouter(); |
| const [isPending, startTransition] = useTransition(); |
| const [isAdding, setIsAdding] = useState(false); |
|
|
| function updateLangs(newList: string[]) { |
| if (isPending) return; |
| startTransition(async () => { |
| try { |
| await fetch("/api/me/profile", { |
| method: "PUT", |
| headers: { "content-type": "application/json" }, |
| body: JSON.stringify({ |
| nativeLang, |
| targetLang: newList[0] || "en", |
| targetLangs: newList, |
| level |
| }), |
| }); |
| router.refresh(); |
| } finally { |
| setIsAdding(false); |
| } |
| }); |
| } |
|
|
| const availableLangs = LANGUAGES.filter( |
| (l) => l.code !== nativeLang && !targetLangs.includes(l.code) |
| ); |
|
|
| return ( |
| <div className="space-y-4"> |
| <div className="flex flex-wrap gap-3"> |
| {targetLangs.map((lang) => { |
| const label = languageName(lang); |
| |
| return ( |
| <div |
| key={lang} |
| className="group relative flex items-center gap-2 rounded-xl border-2 border-black bg-[#FFD21E] px-4 py-2 text-sm font-black text-black shadow-[3px_3px_0px_rgba(0,0,0,1)]" |
| > |
| {label} |
| <button |
| type="button" |
| aria-label={`Remove ${label} from learning languages`} |
| onClick={() => updateLangs(targetLangs.filter((l) => l !== lang))} |
| disabled={isPending || targetLangs.length <= 1} |
| className="ml-1 p-0.5 rounded-md hover:bg-black/10 transition-colors disabled:opacity-0 disabled:pointer-events-none" |
| > |
| <X size={14} strokeWidth={3} /> |
| </button> |
| </div> |
| ); |
| })} |
| |
| <button |
| type="button" |
| aria-controls="learning-language-options" |
| aria-expanded={isAdding} |
| aria-label={isAdding ? "Close language picker" : "Add a learning language"} |
| onClick={() => setIsAdding(!isAdding)} |
| disabled={isPending} |
| className="flex h-10 w-10 items-center justify-center rounded-xl border-2 border-black bg-white text-black shadow-[3px_3px_0px_rgba(0,0,0,1)] transition-all hover:translate-x-[-2px] hover:translate-y-[-2px] hover:shadow-[5px_5px_0px_rgba(0,0,0,1)] active:translate-x-[1px] active:translate-y-[1px] active:shadow-[1px_1px_0px_rgba(0,0,0,1)] disabled:opacity-50" |
| > |
| <Plus size={20} strokeWidth={3} className={`transition-transform duration-200 ${isAdding ? "rotate-45" : ""}`} /> |
| </button> |
| </div> |
| |
| {isAdding && ( |
| <div |
| id="learning-language-options" |
| className="max-h-64 w-full overflow-y-auto rounded-xl border-2 border-black bg-white p-1 shadow-[6px_6px_0px_rgba(0,0,0,1)]" |
| > |
| {availableLangs.length > 0 ? ( |
| availableLangs.map((lang) => ( |
| <button |
| key={lang.code} |
| type="button" |
| aria-label={`Add ${lang.name} to learning languages`} |
| onClick={() => updateLangs([...targetLangs, lang.code])} |
| className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm font-bold text-black transition-colors hover:bg-[#F3F4F6]" |
| > |
| <ReactCountryFlag countryCode={lang.flag} svg style={{ width: "1.1rem", height: "1.1rem" }} /> |
| <span>{lang.name}</span> |
| </button> |
| )) |
| ) : ( |
| <div className="px-3 py-2 text-xs font-bold italic text-gray-500">No more languages</div> |
| )} |
| </div> |
| )} |
| |
| {isPending && ( |
| <div className="text-[10px] font-black uppercase tracking-widest text-[#0EA5A4] animate-pulse"> |
| Updating languages... |
| </div> |
| )} |
| </div> |
| ); |
| } |
|
|