fix: language selector mutual exclusion with preset buttons
- When "Other" is selected, preset language buttons are deselected - Only one option can be selected at a time - Refactor dictionary page with zustand store - Add custom language input option to dictionary - Fix multiple issues in dictionary bigmodel pipeline
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
Folder as Fd,
|
||||
Heart,
|
||||
Search,
|
||||
ArrowUpDown,
|
||||
} from "lucide-react";
|
||||
import { CircleButton } from "@/design-system/base/button";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -61,40 +62,38 @@ const PublicFolderCard = ({ folder, currentUserId, onUpdateFavorite }: PublicFol
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group bg-white border-2 border-gray-200 rounded-lg p-5 hover:border-primary-300 hover:shadow-md cursor-pointer transition-all"
|
||||
className="group bg-white border border-gray-200 sm:border-2 rounded-lg p-3 sm:p-5 hover:border-primary-300 hover:shadow-md cursor-pointer transition-all overflow-hidden"
|
||||
onClick={() => {
|
||||
router.push(`/explore/${folder.id}`);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="shrink-0 w-10 h-10 rounded-lg bg-primary-50 flex items-center justify-center text-primary-500">
|
||||
<Fd size={22} />
|
||||
<div className="flex items-start justify-between mb-2 sm:mb-3">
|
||||
<div className="shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-primary-50 flex items-center justify-center text-primary-500">
|
||||
<Fd size={18} className="sm:hidden" />
|
||||
<Fd size={22} className="hidden sm:block" />
|
||||
</div>
|
||||
<CircleButton
|
||||
onClick={handleToggleFavorite}
|
||||
title={isFavorited ? t("unfavorite") : t("favorite")}
|
||||
>
|
||||
<Heart
|
||||
size={18}
|
||||
className={isFavorited ? "fill-red-500 text-red-500" : ""}
|
||||
size={16}
|
||||
className={`sm:w-[18px] sm:h-[18px] sm:text-[18px] ${isFavorited ? "fill-red-500 text-red-500" : ""}`}
|
||||
/>
|
||||
</CircleButton>
|
||||
</div>
|
||||
|
||||
<h3 className="font-semibold text-gray-900 truncate mb-2">{folder.name}</h3>
|
||||
<h3 className="font-semibold text-gray-900 truncate text-sm sm:text-base mb-1 sm:mb-2">{folder.name}</h3>
|
||||
|
||||
<p className="text-sm text-gray-500 mb-3">
|
||||
<p className="text-xs sm:text-sm text-gray-500 mb-2 sm:mb-3 line-clamp-2">
|
||||
{t("folderInfo", {
|
||||
userName: folder.userName ?? folder.userUsername ?? t("unknownUser"),
|
||||
totalPairs: folder.totalPairs,
|
||||
})}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-1 text-sm text-gray-400">
|
||||
<Heart
|
||||
size={14}
|
||||
className={isFavorited ? "fill-red-500 text-red-500" : ""}
|
||||
/>
|
||||
<div className="flex items-center gap-1 text-xs sm:text-sm text-gray-400">
|
||||
<Heart size={12} className="sm:w-3.5 sm:h-3.5" />
|
||||
<span>{favoriteCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,6 +110,7 @@ export function ExploreClient({ initialPublicFolders }: ExploreClientProps) {
|
||||
const [publicFolders, setPublicFolders] = useState<TPublicFolder[]>(initialPublicFolders);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [sortByFavorites, setSortByFavorites] = useState(false);
|
||||
|
||||
const { data: session } = authClient.useSession();
|
||||
const currentUserId = session?.user?.id;
|
||||
@@ -128,6 +128,14 @@ export function ExploreClient({ initialPublicFolders }: ExploreClientProps) {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleToggleSort = () => {
|
||||
setSortByFavorites((prev) => !prev);
|
||||
};
|
||||
|
||||
const sortedFolders = sortByFavorites
|
||||
? [...publicFolders].sort((a, b) => b.favoriteCount - a.favoriteCount)
|
||||
: publicFolders;
|
||||
|
||||
const handleUpdateFavorite = (folderId: number, _isFavorited: boolean, favoriteCount: number) => {
|
||||
setPublicFolders((prev) =>
|
||||
prev.map((f) =>
|
||||
@@ -152,6 +160,13 @@ export function ExploreClient({ initialPublicFolders }: ExploreClientProps) {
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
/>
|
||||
</div>
|
||||
<CircleButton
|
||||
onClick={handleToggleSort}
|
||||
title={sortByFavorites ? t("sortByFavoritesActive") : t("sortByFavorites")}
|
||||
className={sortByFavorites ? "bg-primary-100 text-primary-600 hover:bg-primary-200" : ""}
|
||||
>
|
||||
<ArrowUpDown size={18} />
|
||||
</CircleButton>
|
||||
<CircleButton onClick={handleSearch}>
|
||||
<Search size={18} />
|
||||
</CircleButton>
|
||||
@@ -162,7 +177,7 @@ export function ExploreClient({ initialPublicFolders }: ExploreClientProps) {
|
||||
<div className="w-8 h-8 border-2 border-gray-200 border-t-gray-400 rounded-full animate-spin mx-auto mb-3"></div>
|
||||
<p className="text-sm text-gray-500">{t("loading")}</p>
|
||||
</div>
|
||||
) : publicFolders.length === 0 ? (
|
||||
) : sortedFolders.length === 0 ? (
|
||||
<div className="text-center py-12 text-gray-400">
|
||||
<div className="w-16 h-16 mx-auto mb-3 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<Fd size={24} className="text-gray-400" />
|
||||
@@ -171,7 +186,7 @@ export function ExploreClient({ initialPublicFolders }: ExploreClientProps) {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
{publicFolders.map((folder) => (
|
||||
{sortedFolders.map((folder) => (
|
||||
<PublicFolderCard
|
||||
key={folder.id}
|
||||
folder={folder}
|
||||
|
||||
Reference in New Issue
Block a user