mirror of
https://github.com/aykhans/slash-e.git
synced 2026-08-04 22:52:41 +00:00
* feat: add shadcn/ui base dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: configure shadcn/ui setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: add cn utility for class merging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: configure tailwind with shadcn theme and CSS variables 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: add shadcn button component * feat: add shadcn input component * feat: add shadcn label component * feat: add shadcn card component * feat: add shadcn badge component * feat: add shadcn separator component * chore: update dependencies for separator component * feat: add shadcn dialog component * feat: add shadcn sheet component (drawer replacement) * feat: add shadcn dropdown-menu component * feat: add shadcn tooltip component * feat: add shadcn checkbox component * feat: add shadcn textarea component * feat: add shadcn avatar component * feat: add shadcn sonner component (toast replacement) * refactor: migrate Dropdown to shadcn dropdown-menu * refactor: migrate Alert to shadcn alert-dialog * refactor: migrate ShortcutCard to shadcn components * refactor: migrate CreateShortcutDrawer to shadcn sheet * fix: correct import path in alert-dialog * feat: add framer-motion for animations * feat: add animated card with framer motion * feat: improve typography with consistent styles * chore: migrate MUI Joy components to shadcn/ui (partial) - Migrated core app components (main.tsx, App.tsx, Root.tsx) - Replaced CssVarsProvider with next-themes ThemeProvider - Replaced useColorScheme with useTheme from next-themes - Migrated all page components using Button, Input, Divider, Tooltip, Separator - Migrated component files using simple components - Migrated setting sections with Select, Switch, Textarea - Added shadcn/ui components: alert, switch, select, radio-group - Build tested and passing Remaining: Dialog/Modal, Drawer/Sheet, RadioGroup components in 10 files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: complete Task 26 - migrate remaining components from MUI Joy to shadcn/ui Migrated 11 remaining files using MUI Joy components to shadcn/ui: Dialog migrations (6 files): - AboutDialog.tsx: Modal/ModalDialog → Dialog - ChangePasswordDialog.tsx: Modal/ModalDialog → Dialog - CreateAccessTokenDialog.tsx: Modal/ModalDialog → Dialog with RadioGroup - CreateUserDialog.tsx: Modal/ModalDialog → Dialog with RadioGroup - EditUserinfoDialog.tsx: Modal/ModalDialog → Dialog - GenerateQRCodeDialog.tsx: Modal/ModalDialog → Dialog Drawer migrations (2 files): - CreateCollectionDrawer.tsx: Drawer → Sheet - CreateIdentityProviderDrawer.tsx: Drawer → Sheet Page migrations (2 files): - SubscriptionSetting.tsx: Alert, Button, Divider, Link, Textarea → shadcn equivalents - WorkspaceSetting.tsx: Alert, Button, Divider → shadcn equivalents Component migrations (1 file): - SubscriptionFAQ.tsx: Accordion/AccordionGroup → shadcn Accordion All components now use shadcn/ui. No MUI Joy imports remain. Build verified successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * chore: remove MUI Joy and react-hot-toast dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: add UI components guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: complete migration from MUI Joy to shadcn/ui - Migrated all UI components to shadcn/ui - Implemented clean & minimal design aesthetic - Added Framer Motion for subtle animations - Improved typography and spacing consistency - Enhanced dark mode with proper color palette - Replaced react-hot-toast with sonner - Removed MUI Joy dependencies - Updated all forms, modals, and interactive components - Added component documentation This migration improves maintainability, reduces bundle size, and provides a more modern, polished UI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * chore: update * chore: update * chore: update * refactor: complete shadcn/ui theme migration with semantic tokens --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: Johnny <yourselfhosted@gmail.com>
163 lines
6.6 KiB
TypeScript
163 lines
6.6 KiB
TypeScript
import classNames from "classnames";
|
|
import copy from "copy-to-clipboard";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Link } from "react-router-dom";
|
|
import { toast } from "sonner";
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
|
import { absolutifyLink } from "@/helpers/utils";
|
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
|
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
|
import { useCollectionStore, useShortcutStore, useUserStore } from "@/stores";
|
|
import { Collection } from "@/types/proto/api/v1/collection_service";
|
|
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
|
|
import { showCommonDialog } from "./Alert";
|
|
import CreateCollectionDialog from "./CreateCollectionDrawer";
|
|
import Icon from "./Icon";
|
|
import ShortcutView from "./ShortcutView";
|
|
import Dropdown from "./common/Dropdown";
|
|
|
|
interface Props {
|
|
collection: Collection;
|
|
}
|
|
|
|
const CollectionView = (props: Props) => {
|
|
const { collection } = props;
|
|
const { t } = useTranslation();
|
|
const { sm } = useResponsiveWidth();
|
|
const navigateTo = useNavigateTo();
|
|
const userStore = useUserStore();
|
|
const currentUser = userStore.getCurrentUser();
|
|
const collectionStore = useCollectionStore();
|
|
const shortcutList = useShortcutStore().getShortcutList();
|
|
const [showEditDialog, setShowEditDialog] = useState<boolean>(false);
|
|
const shortcuts = collection.shortcutIds
|
|
.map((shortcutId) => shortcutList.find((shortcut) => shortcut?.id === shortcutId))
|
|
.filter(Boolean) as any as Shortcut[];
|
|
const showAdminActions = currentUser.id === collection.creatorId;
|
|
|
|
const handleCopyCollectionLink = () => {
|
|
copy(absolutifyLink(`/c/${collection.name}`));
|
|
toast.success("Collection link copied to clipboard.");
|
|
};
|
|
|
|
const handleDeleteCollectionButtonClick = () => {
|
|
showCommonDialog({
|
|
title: "Delete Collection",
|
|
content: `Are you sure to delete collection \`${collection.name}\`? You cannot undo this action.`,
|
|
style: "danger",
|
|
onConfirm: async () => {
|
|
await collectionStore.deleteCollection(collection.id);
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleShortcutClick = (shortcut: Shortcut) => {
|
|
navigateTo(`/shortcut/${shortcut.id}`);
|
|
};
|
|
|
|
const handleOpenAllShortcutsButtonClick = () => {
|
|
shortcuts.forEach((shortcut: Shortcut) => window.open(`/s/${shortcut.name}`));
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className={classNames("w-full flex flex-col justify-start items-start border border-border rounded-lg hover:shadow")}>
|
|
<div className="bg-muted px-3 py-2 w-full flex flex-row justify-between items-center rounded-t-lg">
|
|
<div className="w-auto flex flex-col justify-start items-start mr-2">
|
|
<div className="w-full truncate">
|
|
<Link className="leading-6 font-medium text-foreground" to={`/c/${collection.name}`} viewTransition>
|
|
{collection.title}
|
|
</Link>
|
|
<span className="ml-1 leading-6 text-muted-foreground" onClick={handleCopyCollectionLink}>
|
|
(c/{collection.name})
|
|
</span>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">{collection.description}</p>
|
|
</div>
|
|
<div className="flex flex-row justify-end items-center shrink-0 gap-2">
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Link
|
|
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
|
|
to={`/c/${collection.name}`}
|
|
target="_blank"
|
|
>
|
|
<Icon.Share className="w-4 h-auto" />
|
|
</Link>
|
|
</TooltipTrigger>
|
|
<TooltipContent>Share</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<button
|
|
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
|
|
onClick={() => handleOpenAllShortcutsButtonClick()}
|
|
>
|
|
<Icon.ArrowUpRight className="w-5 h-auto" />
|
|
</button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>Open all</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
{showAdminActions && (
|
|
<Dropdown
|
|
trigger={
|
|
<button className="flex flex-row justify-center items-center rounded text-muted-foreground cursor-pointer hover:text-foreground">
|
|
<Icon.MoreVertical className="w-4 h-auto" />
|
|
</button>
|
|
}
|
|
actionsClassName="!w-28 text-sm"
|
|
actions={
|
|
<>
|
|
<button
|
|
className="w-full px-2 flex flex-row justify-start items-center text-left text-foreground leading-8 cursor-pointer rounded hover:bg-accent disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-60"
|
|
onClick={() => setShowEditDialog(true)}
|
|
>
|
|
<Icon.Edit className="w-4 h-auto mr-2" /> {t("common.edit")}
|
|
</button>
|
|
<button
|
|
className="w-full px-2 flex flex-row justify-start items-center text-left text-destructive leading-8 cursor-pointer rounded hover:bg-accent disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-60"
|
|
onClick={() => {
|
|
handleDeleteCollectionButtonClick();
|
|
}}
|
|
>
|
|
<Icon.Trash className="w-4 h-auto mr-2" /> {t("common.delete")}
|
|
</button>
|
|
</>
|
|
}
|
|
></Dropdown>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="w-full p-3 flex flex-row justify-start items-start flex-wrap gap-3">
|
|
{shortcuts.map((shortcut) => {
|
|
return (
|
|
<ShortcutView
|
|
key={shortcut.id}
|
|
className="!w-auto"
|
|
shortcut={shortcut}
|
|
alwaysShowLink={!sm}
|
|
onClick={() => handleShortcutClick(shortcut)}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{showEditDialog && (
|
|
<CreateCollectionDialog
|
|
collectionId={collection.id}
|
|
onClose={() => setShowEditDialog(false)}
|
|
onConfirm={() => setShowEditDialog(false)}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default CollectionView;
|