LibreChat/client/src/components/Prompts/PromptVersions.tsx
Danny Avila 63926cb874
🪦 refactor: Remove Legacy Code (#10533)
* 🗑️ chore: Remove unused Legacy Provider clients and related helpers

* Deleted OpenAIClient and GoogleClient files along with their associated tests.
* Removed references to these clients in the clients index file.
* Cleaned up typedefs by removing the OpenAISpecClient export.
* Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes.

* chore/remove-openapi-specs

* 🗑️ chore: Remove unused mergeSort and misc utility functions

* Deleted mergeSort.js and misc.js files as they are no longer needed.
* Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic.
* Updated mongoMeili.ts to eliminate local implementations of removed functions.

* chore: remove legacy endpoints

* chore: remove all plugins endpoint related code

* chore: remove unused prompt handling code and clean up imports

* Deleted handleInputs.js and instructions.js files as they are no longer needed.
* Removed references to these files in the prompts index.js.
* Updated docker-compose.yml to simplify reverse proxy configuration.

* chore: remove unused LightningIcon import from Icons.tsx

* chore: clean up translation.json by removing deprecated and unused keys

* chore: update Jest configuration and remove unused mock file

    * Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock.
    * Deleted the fetchEventSource.js mock file as it is no longer needed.

* fix: simplify endpoint type check in Landing and ConversationStarters components

    * Updated the endpoint type check to use strict equality for better clarity and performance.
    * Ensured consistency in the handling of the azureOpenAI endpoint across both components.

* chore: remove unused dependencies from package.json and package-lock.json

* chore: remove legacy EditController, associated routes and imports

* chore: update banResponse logic to refine request handling for banned users

* chore: remove unused validateEndpoint middleware and its references

* chore: remove unused 'res' parameter from initializeClient in multiple endpoint files

* chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions

* chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import

* chore: remove unused TResPlugin type and related references; clean up imports in types and schemas
2025-11-26 11:47:28 -05:00

192 lines
5.6 KiB
TypeScript

import React from 'react';
import { format } from 'date-fns';
import { Layers3, Crown, Zap } from 'lucide-react';
import { Tag, TooltipAnchor, Label } from '@librechat/client';
import type { TPrompt, TPromptGroup } from 'librechat-data-provider';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
const CombinedStatusIcon = ({ description }: { description: string }) => (
<TooltipAnchor
description={description}
aria-label={description}
render={
<div className="flex items-center justify-center">
<Crown className="h-4 w-4 text-amber-500" />
</div>
}
></TooltipAnchor>
);
const VersionTags = ({ tags }: { tags: string[] }) => {
const localize = useLocalize();
const isLatestAndProduction = tags.includes('latest') && tags.includes('production');
if (isLatestAndProduction) {
return (
<span className="absolute bottom-3 right-3">
<CombinedStatusIcon description={localize('com_ui_latest_production_version')} />
</span>
);
}
return (
<span className="flex gap-1 text-sm">
{tags.map((tag, i) => (
<TooltipAnchor
description={
tag === 'production'
? localize('com_ui_currently_production')
: localize('com_ui_latest_version')
}
key={`${tag}-${i}`}
aria-label={
tag === 'production'
? localize('com_ui_currently_production')
: localize('com_ui_latest_version')
}
render={
<Tag
label={tag}
className={cn(
'w-24 justify-center border border-transparent',
tag === 'production'
? 'bg-green-100 text-green-700 dark:border-green-400 dark:bg-transparent dark:text-green-400'
: 'bg-blue-100 text-blue-700 dark:border-blue-400 dark:bg-transparent dark:text-blue-400',
)}
labelClassName="flex items-center m-0 justify-center gap-1"
LabelNode={(() => {
if (tag === 'production') {
return (
<div className="flex items-center">
<span className="slow-pulse size-2 rounded-full bg-green-400" />
</div>
);
}
if (tag === 'latest') {
return (
<div className="flex items-center">
<Zap className="size-4" />
</div>
);
}
return null;
})()}
/>
}
></TooltipAnchor>
))}
</span>
);
};
const VersionCard = ({
prompt,
index,
isSelected,
totalVersions,
onClick,
authorName,
tags,
}: {
prompt: TPrompt;
index: number;
isSelected: boolean;
totalVersions: number;
onClick: () => void;
authorName?: string;
tags: string[];
}) => {
const localize = useLocalize();
return (
<button
type="button"
className={cn(
'group relative w-full rounded-lg border border-border-light p-4 transition-all duration-300',
isSelected
? 'bg-surface-secondary shadow-xl ring-2 ring-gray-400'
: 'bg-surface-primary shadow-sm hover:bg-surface-secondary',
)}
onClick={onClick}
aria-selected={isSelected}
role="tab"
aria-label={localize('com_ui_version_var', { 0: `${totalVersions - index}` })}
>
<div className="flex flex-col gap-2">
<div className="flex items-start justify-between lg:flex-col xl:flex-row">
<h3 className="font-bold text-text-primary">
{localize('com_ui_version_var', { 0: `${totalVersions - index}` })}
</h3>
<time className="text-xs text-text-secondary" dateTime={prompt.createdAt}>
{format(new Date(prompt.createdAt), 'yyyy-MM-dd HH:mm')}
</time>
</div>
<div className="flex items-center gap-1 lg:flex-col xl:flex-row">
{authorName && (
<Label className="text-left text-xs text-text-secondary">
{localize('com_ui_by_author', { 0: authorName })}
</Label>
)}
{tags.length > 0 && <VersionTags tags={tags} />}
</div>
</div>
</button>
);
};
const PromptVersions = ({
prompts,
group,
selectionIndex,
setSelectionIndex,
}: {
prompts: TPrompt[];
group?: TPromptGroup;
selectionIndex: number;
setSelectionIndex: React.Dispatch<React.SetStateAction<number>>;
}) => {
const localize = useLocalize();
return (
<section className="my-6" aria-label="Prompt Versions">
<header className="mb-6">
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
<Layers3 className="h-5 w-5 text-green-500" />
{localize('com_ui_versions')}
</h2>
</header>
<div className="flex flex-col gap-3" role="tablist" aria-label="Version history">
{prompts.map((prompt: TPrompt, index: number) => {
const tags: string[] = [];
if (index === 0) {
tags.push('latest');
}
if (prompt._id === group?.productionId) {
tags.push('production');
}
return (
<VersionCard
key={prompt._id}
prompt={prompt}
index={index}
isSelected={index === selectionIndex}
totalVersions={prompts.length}
onClick={() => setSelectionIndex(index)}
authorName={group?.authorName}
tags={tags}
/>
);
})}
</div>
</section>
);
};
export default PromptVersions;