mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-31 19:15:17 +02:00
Merge pull request #269 from rowboatlabs/dev
Hide community cards behind feature flag
This commit is contained in:
commit
109997ca2e
3 changed files with 219 additions and 204 deletions
|
|
@ -18,3 +18,6 @@ export const SHOW_COPILOT_MARQUEE = false;
|
|||
export const SHOW_PROMPTS_SECTION = true;
|
||||
export const SHOW_DARK_MODE_TOGGLE = false;
|
||||
export const SHOW_VISUALIZATION = false;
|
||||
|
||||
// Client-safe flags
|
||||
export const SHOW_COMMUNITY_PUBLISH = false;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useParams, useRouter } from "next/navigation";
|
|||
import { ProgressBar, ProgressStep } from "@/components/ui/progress-bar";
|
||||
import { useUser } from '@auth0/nextjs-auth0';
|
||||
import { useState, useEffect } from "react";
|
||||
import { SHOW_COMMUNITY_PUBLISH } from "@/app/lib/feature_flags";
|
||||
|
||||
interface TopBarProps {
|
||||
localProjectName: string;
|
||||
|
|
@ -708,126 +709,130 @@ export function TopBar({
|
|||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-200 dark:border-gray-700"></div>
|
||||
{SHOW_COMMUNITY_PUBLISH && (
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
<div className="relative flex justify-center">
|
||||
<span className="px-4 bg-white dark:bg-gray-900 text-xs font-medium text-gray-400 dark:text-gray-500 uppercase tracking-wider">or</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex justify-center">
|
||||
<span className="px-4 bg-white dark:bg-gray-900 text-xs font-medium text-gray-400 dark:text-gray-500 uppercase tracking-wider">or</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Community Publishing Section */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center">
|
||||
<MessageCircleIcon size={16} className="text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 dark:text-gray-100">Publish to Community</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Make it discoverable by others</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
{/* Assistant Name */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Assistant Name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
placeholder="Enter assistant name"
|
||||
value={communityData.name}
|
||||
onChange={(e) => setCommunityData({ ...communityData, name: e.target.value })}
|
||||
classNames={{
|
||||
input: "text-sm focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0",
|
||||
inputWrapper: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0 !ring-0 !ring-offset-0"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Description <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Textarea
|
||||
placeholder="Describe what this assistant does..."
|
||||
value={communityData.description}
|
||||
onChange={(e) => setCommunityData({ ...communityData, description: e.target.value })}
|
||||
minRows={3}
|
||||
classNames={{
|
||||
input: "text-sm focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0",
|
||||
inputWrapper: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0 !ring-0 !ring-offset-0"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Category <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Select
|
||||
placeholder="Select a category"
|
||||
selectedKeys={communityData.category ? [communityData.category] : []}
|
||||
onSelectionChange={(keys) => {
|
||||
const selected = Array.from(keys)[0] as string;
|
||||
setCommunityData({ ...communityData, category: selected });
|
||||
}}
|
||||
classNames={{
|
||||
trigger: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0",
|
||||
value: "text-sm"
|
||||
}}
|
||||
>
|
||||
<SelectItem key="Work Productivity">Work Productivity</SelectItem>
|
||||
<SelectItem key="Developer Productivity">Developer Productivity</SelectItem>
|
||||
<SelectItem key="News & Social">News & Social</SelectItem>
|
||||
<SelectItem key="Customer Support">Customer Support</SelectItem>
|
||||
<SelectItem key="Education">Education</SelectItem>
|
||||
<SelectItem key="Entertainment">Entertainment</SelectItem>
|
||||
<SelectItem key="Other">Other</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Privacy Toggle */}
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800/30 rounded-xl border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-1">
|
||||
{communityData.isAnonymous ? 'Publish anonymously' : `Publish as ${getUserDisplayName()}`}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{communityData.isAnonymous ? 'Your name will be hidden from the community' : 'Your name will be visible to the community'}
|
||||
</div>
|
||||
{SHOW_COMMUNITY_PUBLISH && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center">
|
||||
<MessageCircleIcon size={16} className="text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCommunityData({ ...communityData, isAnonymous: !communityData.isAnonymous })}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
|
||||
communityData.isAnonymous ? 'bg-gray-300 dark:bg-gray-600' : 'bg-blue-600'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
communityData.isAnonymous ? 'translate-x-1' : 'translate-x-6'
|
||||
}`}
|
||||
<div>
|
||||
<h3 className="text-base font-medium text-gray-900 dark:text-gray-100">Publish to Community</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Make it discoverable by others</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
{/* Assistant Name */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Assistant Name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
placeholder="Enter assistant name"
|
||||
value={communityData.name}
|
||||
onChange={(e) => setCommunityData({ ...communityData, name: e.target.value })}
|
||||
classNames={{
|
||||
input: "text-sm focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0",
|
||||
inputWrapper: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0 !ring-0 !ring-offset-0"
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Success Message */}
|
||||
{communityPublishSuccess && (
|
||||
<div className="flex items-center gap-3 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-xl">
|
||||
<div className="w-5 h-5 rounded-full bg-green-100 dark:bg-green-900/40 flex items-center justify-center">
|
||||
<span className="text-green-600 dark:text-green-400 text-xs">✓</span>
|
||||
</div>
|
||||
<p className="text-green-700 dark:text-green-300 text-sm font-medium">
|
||||
Successfully published to community!
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Description <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Textarea
|
||||
placeholder="Describe what this assistant does..."
|
||||
value={communityData.description}
|
||||
onChange={(e) => setCommunityData({ ...communityData, description: e.target.value })}
|
||||
minRows={3}
|
||||
classNames={{
|
||||
input: "text-sm focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0",
|
||||
inputWrapper: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0 !ring-0 !ring-offset-0"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Category <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Select
|
||||
placeholder="Select a category"
|
||||
selectedKeys={communityData.category ? [communityData.category] : []}
|
||||
onSelectionChange={(keys) => {
|
||||
const selected = Array.from(keys)[0] as string;
|
||||
setCommunityData({ ...communityData, category: selected });
|
||||
}}
|
||||
classNames={{
|
||||
trigger: "border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 focus:outline-none !focus:ring-0 !focus:ring-offset-0 !ring-0 !ring-offset-0 focus-within:border-gray-300 dark:focus-within:border-gray-500 !focus-within:ring-0 !focus-within:ring-offset-0",
|
||||
value: "text-sm"
|
||||
}}
|
||||
>
|
||||
<SelectItem key="Work Productivity">Work Productivity</SelectItem>
|
||||
<SelectItem key="Developer Productivity">Developer Productivity</SelectItem>
|
||||
<SelectItem key="News & Social">News & Social</SelectItem>
|
||||
<SelectItem key="Customer Support">Customer Support</SelectItem>
|
||||
<SelectItem key="Education">Education</SelectItem>
|
||||
<SelectItem key="Entertainment">Entertainment</SelectItem>
|
||||
<SelectItem key="Other">Other</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Privacy Toggle */}
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800/30 rounded-xl border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-1">
|
||||
{communityData.isAnonymous ? 'Publish anonymously' : `Publish as ${getUserDisplayName()}`}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{communityData.isAnonymous ? 'Your name will be hidden from the community' : 'Your name will be visible to the community'}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCommunityData({ ...communityData, isAnonymous: !communityData.isAnonymous })}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
|
||||
communityData.isAnonymous ? 'bg-gray-300 dark:bg-gray-600' : 'bg-blue-600'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
communityData.isAnonymous ? 'translate-x-1' : 'translate-x-6'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Success Message */}
|
||||
{communityPublishSuccess && (
|
||||
<div className="flex items-center gap-3 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-xl">
|
||||
<div className="w-5 h-5 rounded-full bg-green-100 dark:bg-green-900/40 flex items-center justify-center">
|
||||
<span className="text-green-600 dark:text-green-400 text-xs">✓</span>
|
||||
</div>
|
||||
<p className="text-green-700 dark:text-green-300 text-sm font-medium">
|
||||
Successfully published to community!
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter className="gap-3">
|
||||
|
|
@ -836,76 +841,80 @@ export function TopBar({
|
|||
onPress={onShareModalClose}
|
||||
className="px-6 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
color={communityPublishSuccess ? "success" : "primary"}
|
||||
onPress={() => {
|
||||
// Open confirmation first
|
||||
onConfirmOpen();
|
||||
}}
|
||||
isLoading={communityPublishing}
|
||||
isDisabled={communityPublishSuccess || !communityData.name.trim() || !communityData.description.trim() || !communityData.category}
|
||||
className={`${communityPublishSuccess ? 'bg-green-600 hover:bg-green-700' : 'bg-blue-600 hover:bg-blue-700'} px-6 py-2 text-white font-medium`}
|
||||
>
|
||||
{communityPublishSuccess ? 'Published' : (communityPublishing ? 'Publishing...' : 'Publish to Community')}
|
||||
Close
|
||||
</Button>
|
||||
{SHOW_COMMUNITY_PUBLISH && (
|
||||
<Button
|
||||
color={communityPublishSuccess ? "success" : "primary"}
|
||||
onPress={() => {
|
||||
// Open confirmation first
|
||||
onConfirmOpen();
|
||||
}}
|
||||
isLoading={communityPublishing}
|
||||
isDisabled={communityPublishSuccess || !communityData.name.trim() || !communityData.description.trim() || !communityData.category}
|
||||
className={`${communityPublishSuccess ? 'bg-green-600 hover:bg-green-700' : 'bg-blue-600 hover:bg-blue-700'} px-6 py-2 text-white font-medium`}
|
||||
>
|
||||
{communityPublishSuccess ? 'Published' : (communityPublishing ? 'Publishing...' : 'Publish to Community')}
|
||||
</Button>
|
||||
)}
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
{/* Confirmation Modal for Community Publish */}
|
||||
<Modal
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={() => { setAcknowledged(false); onConfirmClose(); }}
|
||||
size="md"
|
||||
classNames={{
|
||||
base: "bg-white dark:bg-gray-900",
|
||||
header: "border-b border-gray-200 dark:border-gray-700 pb-3",
|
||||
body: "py-5",
|
||||
footer: "border-t border-gray-200 dark:border-gray-700 pt-3"
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Confirm publish to community</h3>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="space-y-3 text-sm text-gray-700 dark:text-gray-300">
|
||||
<p>Publishing to community will make this assistant and its description publicly visible to other users.</p>
|
||||
<ul className="list-disc pl-5 space-y-1">
|
||||
<li>Your assistant may appear in the community templates library.</li>
|
||||
<li>Others can import and use this assistant in their own projects.</li>
|
||||
<li>Do not include secrets or private data in the description or workflow.</li>
|
||||
</ul>
|
||||
<div className="mt-3 flex items-start gap-2">
|
||||
<input
|
||||
id="ack-publish"
|
||||
type="checkbox"
|
||||
checked={acknowledged}
|
||||
onChange={(e) => setAcknowledged(e.target.checked)}
|
||||
className="mt-1 h-4 w-4"
|
||||
/>
|
||||
<label htmlFor="ack-publish" className="text-sm">I understand this will be publicly available.</label>
|
||||
{SHOW_COMMUNITY_PUBLISH && (
|
||||
<Modal
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={() => { setAcknowledged(false); onConfirmClose(); }}
|
||||
size="md"
|
||||
classNames={{
|
||||
base: "bg-white dark:bg-gray-900",
|
||||
header: "border-b border-gray-200 dark:border-gray-700 pb-3",
|
||||
body: "py-5",
|
||||
footer: "border-t border-gray-200 dark:border-gray-700 pt-3"
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Confirm publish to community</h3>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="space-y-3 text-sm text-gray-700 dark:text-gray-300">
|
||||
<p>Publishing to community will make this assistant and its description publicly visible to other users.</p>
|
||||
<ul className="list-disc pl-5 space-y-1">
|
||||
<li>Your assistant may appear in the community templates library.</li>
|
||||
<li>Others can import and use this assistant in their own projects.</li>
|
||||
<li>Do not include secrets or private data in the description or workflow.</li>
|
||||
</ul>
|
||||
<div className="mt-3 flex items-start gap-2">
|
||||
<input
|
||||
id="ack-publish"
|
||||
type="checkbox"
|
||||
checked={acknowledged}
|
||||
onChange={(e) => setAcknowledged(e.target.checked)}
|
||||
className="mt-1 h-4 w-4"
|
||||
/>
|
||||
<label htmlFor="ack-publish" className="text-sm">I understand this will be publicly available.</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="light" onPress={() => { setAcknowledged(false); onConfirmClose(); }}>Cancel</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
isDisabled={!acknowledged}
|
||||
onPress={() => {
|
||||
onConfirmClose();
|
||||
setAcknowledged(false);
|
||||
onCommunityPublish();
|
||||
}}
|
||||
>
|
||||
Confirm & Publish
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="light" onPress={() => { setAcknowledged(false); onConfirmClose(); }}>Cancel</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
isDisabled={!acknowledged}
|
||||
onPress={() => {
|
||||
onConfirmClose();
|
||||
setAcknowledged(false);
|
||||
onCommunityPublish();
|
||||
}}
|
||||
>
|
||||
Confirm & Publish
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Search, Filter } from 'lucide-react';
|
|||
import { AssistantCard } from './AssistantCard';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getCurrentUser } from '@/app/actions/assistant-templates.actions';
|
||||
import { SHOW_COMMUNITY_PUBLISH } from '@/app/lib/feature_flags';
|
||||
|
||||
interface TemplateItem {
|
||||
id: string;
|
||||
|
|
@ -60,7 +61,7 @@ export function UnifiedTemplatesSection({
|
|||
onTypeChange,
|
||||
}: UnifiedTemplatesSectionProps) {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedType, setSelectedType] = useState<'prebuilt' | 'community'>('prebuilt');
|
||||
const [selectedType, setSelectedType] = useState<'prebuilt' | 'community'>(SHOW_COMMUNITY_PUBLISH ? 'prebuilt' : 'prebuilt');
|
||||
const [selectedCategories, setSelectedCategories] = useState<Set<string>>(new Set());
|
||||
const [sortBy, setSortBy] = useState<'popular' | 'newest' | 'alphabetical'>('alphabetical');
|
||||
const [currentUserId, setCurrentUserId] = useState<string | null>(null);
|
||||
|
|
@ -105,7 +106,7 @@ export function UnifiedTemplatesSection({
|
|||
const allTemplates = useMemo(() => {
|
||||
const combined = [
|
||||
...prebuiltTemplates.map(t => ({ ...t, type: 'prebuilt' as const })),
|
||||
...communityTemplates.map(t => ({ ...t, type: 'community' as const }))
|
||||
...(SHOW_COMMUNITY_PUBLISH ? communityTemplates.map(t => ({ ...t, type: 'community' as const })) : [])
|
||||
];
|
||||
return combined;
|
||||
}, [prebuiltTemplates, communityTemplates]);
|
||||
|
|
@ -300,32 +301,34 @@ export function UnifiedTemplatesSection({
|
|||
|
||||
<div className="flex gap-2">
|
||||
{/* Type Filter Segmented Control (Library | Community) */}
|
||||
<div className="flex gap-0.5 items-center h-8 rounded-full border border-gray-200 dark:border-gray-700 p-0 bg-white dark:bg-gray-800 shadow-sm overflow-hidden">
|
||||
{[
|
||||
{ key: 'prebuilt', label: 'Library' },
|
||||
{ key: 'community', label: 'Community' }
|
||||
].map(({ key, label }) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={async () => {
|
||||
const newType = key as 'prebuilt' | 'community';
|
||||
const target = rowsShown * itemsPerRow;
|
||||
if (onTypeChange) {
|
||||
await onTypeChange(newType, target);
|
||||
}
|
||||
setSelectedType(newType);
|
||||
}}
|
||||
aria-pressed={selectedType === key}
|
||||
className={`inline-flex items-center h-8 px-2.5 rounded-full text-[13px] font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 ${
|
||||
selectedType === key
|
||||
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
|
||||
: 'bg-transparent text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{SHOW_COMMUNITY_PUBLISH && (
|
||||
<div className="flex gap-0.5 items-center h-8 rounded-full border border-gray-200 dark:border-gray-700 p-0 bg-white dark:bg-gray-800 shadow-sm overflow-hidden">
|
||||
{[
|
||||
{ key: 'prebuilt', label: 'Library' },
|
||||
{ key: 'community', label: 'Community' }
|
||||
].map(({ key, label }) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={async () => {
|
||||
const newType = key as 'prebuilt' | 'community';
|
||||
const target = rowsShown * itemsPerRow;
|
||||
if (onTypeChange) {
|
||||
await onTypeChange(newType, target);
|
||||
}
|
||||
setSelectedType(newType);
|
||||
}}
|
||||
aria-pressed={selectedType === key}
|
||||
className={`inline-flex items-center h-8 px-2.5 rounded-full text-[13px] font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 ${
|
||||
selectedType === key
|
||||
? 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
|
||||
: 'bg-transparent text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sort Dropdown (Popularity only for Community) */}
|
||||
<div className="relative">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue