mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-10-03 18:08:58 +02:00
Minor search UX improvements + Critical connector fixes (#2259)
This commit is contained in:
@@ -170,7 +170,7 @@ def associate_credential_to_connector(
|
|||||||
connector_id=connector_id,
|
connector_id=connector_id,
|
||||||
credential_id=credential_id,
|
credential_id=credential_id,
|
||||||
cc_pair_name=metadata.name,
|
cc_pair_name=metadata.name,
|
||||||
is_public=metadata.is_public or True,
|
is_public=True if metadata.is_public is None else metadata.is_public,
|
||||||
groups=metadata.groups,
|
groups=metadata.groups,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -552,6 +552,7 @@ def create_connector_from_model(
|
|||||||
try:
|
try:
|
||||||
_validate_connector_allowed(connector_data.source)
|
_validate_connector_allowed(connector_data.source)
|
||||||
connector_base = _check_connector_permissions(connector_data, user)
|
connector_base = _check_connector_permissions(connector_data, user)
|
||||||
|
|
||||||
return create_connector(
|
return create_connector(
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
connector_data=connector_base,
|
connector_data=connector_base,
|
||||||
|
@@ -112,7 +112,7 @@ export default function Page() {
|
|||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
onKeyDown={handleKeyPress}
|
onKeyDown={handleKeyPress}
|
||||||
className="flex mt-2 max-w-sm h-9 w-full rounded-md border-2 border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="ml-1 w-96 h-9 flex-none rounded-md border border-border bg-background-50 px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{Object.entries(categorizedSources)
|
{Object.entries(categorizedSources)
|
||||||
|
@@ -44,7 +44,6 @@ import {
|
|||||||
IsPublicGroupSelectorFormType,
|
IsPublicGroupSelectorFormType,
|
||||||
} from "@/components/IsPublicGroupSelector";
|
} from "@/components/IsPublicGroupSelector";
|
||||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||||
import { AdminBooleanFormField } from "@/components/credentials/CredentialFields";
|
|
||||||
|
|
||||||
export type AdvancedConfigFinal = {
|
export type AdvancedConfigFinal = {
|
||||||
pruneFreq: number | null;
|
pruneFreq: number | null;
|
||||||
@@ -489,6 +488,7 @@ export default function AddConnector({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(formikProps) => {
|
{(formikProps) => {
|
||||||
|
console.log(formikProps.values);
|
||||||
setFormValues(formikProps.values);
|
setFormValues(formikProps.values);
|
||||||
handleFormStatusChange(
|
handleFormStatusChange(
|
||||||
formikProps.isValid && isFormSubmittable(formikProps.values)
|
formikProps.isValid && isFormSubmittable(formikProps.values)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { SubLabel } from "@/components/admin/connectors/Field";
|
import { SubLabel } from "@/components/admin/connectors/Field";
|
||||||
import { Field } from "formik";
|
import { Field, useFormikContext } from "formik";
|
||||||
|
|
||||||
export default function NumberInput({
|
export default function NumberInput({
|
||||||
label,
|
label,
|
||||||
@@ -8,6 +8,7 @@ export default function NumberInput({
|
|||||||
description,
|
description,
|
||||||
name,
|
name,
|
||||||
showNeverIfZero,
|
showNeverIfZero,
|
||||||
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
value?: number;
|
value?: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -15,7 +16,10 @@ export default function NumberInput({
|
|||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
description?: string;
|
description?: string;
|
||||||
showNeverIfZero?: boolean;
|
showNeverIfZero?: boolean;
|
||||||
|
onChange?: (value: number) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col">
|
<div className="w-full flex flex-col">
|
||||||
<label className="block text-base font-medium text-text-700 mb-1">
|
<label className="block text-base font-medium text-text-700 mb-1">
|
||||||
@@ -29,6 +33,14 @@ export default function NumberInput({
|
|||||||
name={name}
|
name={name}
|
||||||
min="-1"
|
min="-1"
|
||||||
value={value === 0 && showNeverIfZero ? "Never" : value}
|
value={value === 0 && showNeverIfZero ? "Never" : value}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newValue =
|
||||||
|
e.target.value === "Never" ? 0 : Number(e.target.value);
|
||||||
|
setFieldValue(name, newValue);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(newValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
className={`mt-2 block w-full px-3 py-2
|
className={`mt-2 block w-full px-3 py-2
|
||||||
bg-white border border-gray-300 rounded-md
|
bg-white border border-gray-300 rounded-md
|
||||||
text-sm shadow-sm placeholder-gray-400
|
text-sm shadow-sm placeholder-gray-400
|
||||||
|
@@ -92,7 +92,7 @@ const RerankingDetailsForm = forwardRef<
|
|||||||
.nullable()
|
.nullable()
|
||||||
.oneOf(Object.values(RerankerProvider))
|
.oneOf(Object.values(RerankerProvider))
|
||||||
.optional(),
|
.optional(),
|
||||||
api_key: Yup.string().nullable(),
|
rerank_api_key: Yup.string().nullable(),
|
||||||
num_rerank: Yup.number().min(1, "Must be at least 1"),
|
num_rerank: Yup.number().min(1, "Must be at least 1"),
|
||||||
})}
|
})}
|
||||||
onSubmit={async (_, { setSubmitting }) => {
|
onSubmit={async (_, { setSubmitting }) => {
|
||||||
@@ -132,6 +132,7 @@ const RerankingDetailsForm = forwardRef<
|
|||||||
...values,
|
...values,
|
||||||
rerank_provider_type: card.rerank_provider_type!,
|
rerank_provider_type: card.rerank_provider_type!,
|
||||||
rerank_model_name: card.modelName,
|
rerank_model_name: card.modelName,
|
||||||
|
rerank_api_key: null,
|
||||||
});
|
});
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
"rerank_provider_type",
|
"rerank_provider_type",
|
||||||
@@ -192,15 +193,18 @@ const RerankingDetailsForm = forwardRef<
|
|||||||
>
|
>
|
||||||
<div className="w-full px-4">
|
<div className="w-full px-4">
|
||||||
<TextFormField
|
<TextFormField
|
||||||
placeholder={values.api_key || undefined}
|
placeholder={values.rerank_api_key || undefined}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setRerankingDetails({ ...values, api_key: value });
|
setRerankingDetails({
|
||||||
setFieldValue("api_key", value);
|
...values,
|
||||||
|
rerank_api_key: value,
|
||||||
|
});
|
||||||
|
setFieldValue("rerank_api_key", value);
|
||||||
}}
|
}}
|
||||||
type="password"
|
type="password"
|
||||||
label="Cohere API Key"
|
label="Cohere API Key"
|
||||||
name="api_key"
|
name="rerank_api_key"
|
||||||
/>
|
/>
|
||||||
<div className="mt-4 flex justify-between">
|
<div className="mt-4 flex justify-between">
|
||||||
<Button
|
<Button
|
||||||
|
@@ -4,7 +4,7 @@ import { NonNullChain } from "typescript";
|
|||||||
export interface RerankingDetails {
|
export interface RerankingDetails {
|
||||||
rerank_model_name: string | null;
|
rerank_model_name: string | null;
|
||||||
rerank_provider_type: RerankerProvider | null;
|
rerank_provider_type: RerankerProvider | null;
|
||||||
api_key: string | null;
|
rerank_api_key: string | null;
|
||||||
num_rerank: number;
|
num_rerank: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ interface AdvancedEmbeddingFormPageProps {
|
|||||||
) => void;
|
) => void;
|
||||||
advancedEmbeddingDetails: AdvancedSearchConfiguration;
|
advancedEmbeddingDetails: AdvancedSearchConfiguration;
|
||||||
numRerank: number;
|
numRerank: number;
|
||||||
|
updateNumRerank: (value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AdvancedEmbeddingFormPage = forwardRef<
|
const AdvancedEmbeddingFormPage = forwardRef<
|
||||||
@@ -22,7 +23,12 @@ const AdvancedEmbeddingFormPage = forwardRef<
|
|||||||
AdvancedEmbeddingFormPageProps
|
AdvancedEmbeddingFormPageProps
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ updateAdvancedEmbeddingDetails, advancedEmbeddingDetails, numRerank },
|
{
|
||||||
|
updateAdvancedEmbeddingDetails,
|
||||||
|
advancedEmbeddingDetails,
|
||||||
|
numRerank,
|
||||||
|
updateNumRerank,
|
||||||
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
@@ -154,6 +160,10 @@ const AdvancedEmbeddingFormPage = forwardRef<
|
|||||||
name="disableRerankForStreaming"
|
name="disableRerankForStreaming"
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
|
onChange={(value: number) => {
|
||||||
|
updateNumRerank(value);
|
||||||
|
setFieldValue("num_rerank", value);
|
||||||
|
}}
|
||||||
description="Number of results to rerank"
|
description="Number of results to rerank"
|
||||||
optional={false}
|
optional={false}
|
||||||
value={values.num_rerank}
|
value={values.num_rerank}
|
||||||
|
@@ -44,7 +44,7 @@ export default function EmbeddingForm() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [rerankingDetails, setRerankingDetails] = useState<RerankingDetails>({
|
const [rerankingDetails, setRerankingDetails] = useState<RerankingDetails>({
|
||||||
api_key: "",
|
rerank_api_key: "",
|
||||||
num_rerank: 0,
|
num_rerank: 0,
|
||||||
rerank_provider_type: null,
|
rerank_provider_type: null,
|
||||||
rerank_model_name: "",
|
rerank_model_name: "",
|
||||||
@@ -118,7 +118,7 @@ export default function EmbeddingForm() {
|
|||||||
searchSettings.disable_rerank_for_streaming,
|
searchSettings.disable_rerank_for_streaming,
|
||||||
});
|
});
|
||||||
setRerankingDetails({
|
setRerankingDetails({
|
||||||
api_key: searchSettings.api_key,
|
rerank_api_key: searchSettings.rerank_api_key,
|
||||||
num_rerank: searchSettings.num_rerank,
|
num_rerank: searchSettings.num_rerank,
|
||||||
rerank_provider_type: searchSettings.rerank_provider_type,
|
rerank_provider_type: searchSettings.rerank_provider_type,
|
||||||
rerank_model_name: searchSettings.rerank_model_name,
|
rerank_model_name: searchSettings.rerank_model_name,
|
||||||
@@ -128,13 +128,13 @@ export default function EmbeddingForm() {
|
|||||||
|
|
||||||
const originalRerankingDetails: RerankingDetails = searchSettings
|
const originalRerankingDetails: RerankingDetails = searchSettings
|
||||||
? {
|
? {
|
||||||
api_key: searchSettings.api_key,
|
rerank_api_key: searchSettings.rerank_api_key,
|
||||||
num_rerank: searchSettings.num_rerank,
|
num_rerank: searchSettings.num_rerank,
|
||||||
rerank_provider_type: searchSettings.rerank_provider_type,
|
rerank_provider_type: searchSettings.rerank_provider_type,
|
||||||
rerank_model_name: searchSettings.rerank_model_name,
|
rerank_model_name: searchSettings.rerank_model_name,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
api_key: "",
|
rerank_api_key: "",
|
||||||
num_rerank: 0,
|
num_rerank: 0,
|
||||||
rerank_provider_type: null,
|
rerank_provider_type: null,
|
||||||
rerank_model_name: "",
|
rerank_model_name: "",
|
||||||
@@ -415,6 +415,12 @@ export default function EmbeddingForm() {
|
|||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<AdvancedEmbeddingFormPage
|
<AdvancedEmbeddingFormPage
|
||||||
|
updateNumRerank={(value: number) =>
|
||||||
|
setRerankingDetails({
|
||||||
|
...rerankingDetails,
|
||||||
|
num_rerank: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
numRerank={rerankingDetails.num_rerank}
|
numRerank={rerankingDetails.num_rerank}
|
||||||
advancedEmbeddingDetails={advancedEmbeddingDetails}
|
advancedEmbeddingDetails={advancedEmbeddingDetails}
|
||||||
updateAdvancedEmbeddingDetails={updateAdvancedEmbeddingDetails}
|
updateAdvancedEmbeddingDetails={updateAdvancedEmbeddingDetails}
|
||||||
|
@@ -457,7 +457,7 @@ export function CCPairIndexingStatusTable({
|
|||||||
placeholder="Search connectors..."
|
placeholder="Search connectors..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="ml-2 w-96 h-9 flex-none rounded-md border border-border bg-background-50 px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="ml-1 w-96 h-9 flex-none rounded-md border border-border bg-background-50 px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button className="h-9" onClick={() => toggleSources()}>
|
<Button className="h-9" onClick={() => toggleSources()}>
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||||
import { useContext, useState, useRef, useLayoutEffect } from "react";
|
import { useContext, useState, useRef, useLayoutEffect } from "react";
|
||||||
import remarkGfm from "remark-gfm";
|
|
||||||
import { Popover } from "@/components/popover/Popover";
|
import { Popover } from "@/components/popover/Popover";
|
||||||
import { ChevronDownIcon } from "@/components/icons/icons";
|
import { ChevronDownIcon } from "@/components/icons/icons";
|
||||||
import { Divider } from "@tremor/react";
|
|
||||||
import { MinimalMarkdown } from "@/components/chat_search/MinimalMarkdown";
|
import { MinimalMarkdown } from "@/components/chat_search/MinimalMarkdown";
|
||||||
|
|
||||||
export function ChatBanner() {
|
export function ChatBanner() {
|
||||||
@@ -34,27 +31,6 @@ export function ChatBanner() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderMarkdown = (className: string) => (
|
|
||||||
<ReactMarkdown
|
|
||||||
className={`w-full text-wrap break-word ${className}`}
|
|
||||||
components={{
|
|
||||||
a: ({ node, ...props }) => (
|
|
||||||
<a
|
|
||||||
{...props}
|
|
||||||
className="text-sm text-link hover:text-link-hover"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
p: ({ node, ...props }) => (
|
|
||||||
<p {...props} className="text-wrap break-word text-sm m-0 w-full" />
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
remarkPlugins={[remarkGfm]}
|
|
||||||
>
|
|
||||||
{settings.enterpriseSettings?.custom_header_content}
|
|
||||||
</ReactMarkdown>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
@@ -65,7 +41,6 @@ export function ChatBanner() {
|
|||||||
w-full
|
w-full
|
||||||
mx-auto
|
mx-auto
|
||||||
relative
|
relative
|
||||||
bg-background-100
|
|
||||||
shadow-sm
|
shadow-sm
|
||||||
rounded
|
rounded
|
||||||
border-l-8 border-l-400
|
border-l-8 border-l-400
|
||||||
@@ -81,7 +56,6 @@ export function ChatBanner() {
|
|||||||
className="line-clamp-2 text-center w-full overflow-hidden pr-8"
|
className="line-clamp-2 text-center w-full overflow-hidden pr-8"
|
||||||
>
|
>
|
||||||
<MinimalMarkdown
|
<MinimalMarkdown
|
||||||
className=""
|
|
||||||
content={settings.enterpriseSettings.custom_header_content}
|
content={settings.enterpriseSettings.custom_header_content}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,7 +64,6 @@ export function ChatBanner() {
|
|||||||
className="absolute top-0 left-0 invisible w-full"
|
className="absolute top-0 left-0 invisible w-full"
|
||||||
>
|
>
|
||||||
<MinimalMarkdown
|
<MinimalMarkdown
|
||||||
className=""
|
|
||||||
content={settings.enterpriseSettings.custom_header_content}
|
content={settings.enterpriseSettings.custom_header_content}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -22,7 +22,7 @@ export const IsPublicGroupSelector = <T extends IsPublicGroupSelectorFormType>({
|
|||||||
enforceGroupSelection?: boolean;
|
enforceGroupSelection?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { data: userGroups, isLoading: userGroupsIsLoading } = useUserGroups();
|
const { data: userGroups, isLoading: userGroupsIsLoading } = useUserGroups();
|
||||||
const { isAdmin, user, isLoadingUser } = useUser();
|
const { isAdmin, user, isLoadingUser, isCurator } = useUser();
|
||||||
const [shouldHideContent, setShouldHideContent] = useState(false);
|
const [shouldHideContent, setShouldHideContent] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -87,42 +87,45 @@ export const IsPublicGroupSelector = <T extends IsPublicGroupSelectorFormType>({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{(!formikProps.values.is_public ||
|
{(!formikProps.values.is_public ||
|
||||||
!isAdmin ||
|
isCurator ||
|
||||||
formikProps.values.groups.length > 0) && (
|
formikProps.values.groups.length > 0) &&
|
||||||
<>
|
(userGroupsIsLoading ? (
|
||||||
<div className="flex mt-4 gap-x-2 items-center">
|
<div className="animate-pulse bg-gray-200 h-8 w-32 rounded"></div>
|
||||||
<div className="block font-medium text-base">
|
) : (
|
||||||
Assign group access for this {objectName}
|
userGroups &&
|
||||||
</div>
|
userGroups.length > 0 && (
|
||||||
</div>
|
<>
|
||||||
<Text className="mb-3">
|
<div className="flex mt-4 gap-x-2 items-center">
|
||||||
{isAdmin || !enforceGroupSelection ? (
|
<div className="block font-medium text-base">
|
||||||
<>
|
Assign group access for this {objectName}
|
||||||
This {objectName} will be visible/accessible by the groups
|
</div>
|
||||||
selected below
|
</div>
|
||||||
</>
|
<Text className="mb-3">
|
||||||
) : (
|
{isAdmin || !enforceGroupSelection ? (
|
||||||
<>
|
<>
|
||||||
Curators must select one or more groups to give access to this{" "}
|
This {objectName} will be visible/accessible by the groups
|
||||||
{objectName}
|
selected below
|
||||||
</>
|
</>
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
<FieldArray
|
|
||||||
name="groups"
|
|
||||||
render={(arrayHelpers: ArrayHelpers) => (
|
|
||||||
<div className="flex gap-2 flex-wrap mb-4">
|
|
||||||
{userGroupsIsLoading ? (
|
|
||||||
<div className="animate-pulse bg-gray-200 h-8 w-32 rounded"></div>
|
|
||||||
) : (
|
) : (
|
||||||
userGroups &&
|
<>
|
||||||
userGroups.map((userGroup: UserGroup) => {
|
Curators must select one or more groups to give access to
|
||||||
const ind = formikProps.values.groups.indexOf(userGroup.id);
|
this {objectName}
|
||||||
let isSelected = ind !== -1;
|
</>
|
||||||
return (
|
)}
|
||||||
<div
|
</Text>
|
||||||
key={userGroup.id}
|
<FieldArray
|
||||||
className={`
|
name="groups"
|
||||||
|
render={(arrayHelpers: ArrayHelpers) => (
|
||||||
|
<div className="flex gap-2 flex-wrap mb-4">
|
||||||
|
{userGroups.map((userGroup: UserGroup) => {
|
||||||
|
const ind = formikProps.values.groups.indexOf(
|
||||||
|
userGroup.id
|
||||||
|
);
|
||||||
|
let isSelected = ind !== -1;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={userGroup.id}
|
||||||
|
className={`
|
||||||
px-3
|
px-3
|
||||||
py-1
|
py-1
|
||||||
rounded-lg
|
rounded-lg
|
||||||
@@ -132,32 +135,33 @@ export const IsPublicGroupSelector = <T extends IsPublicGroupSelectorFormType>({
|
|||||||
flex
|
flex
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
${isSelected ? "bg-background-strong" : "hover:bg-hover"}
|
${isSelected ? "bg-background-strong" : "hover:bg-hover"}
|
||||||
`}
|
`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
arrayHelpers.remove(ind);
|
arrayHelpers.remove(ind);
|
||||||
} else {
|
} else {
|
||||||
arrayHelpers.push(userGroup.id);
|
arrayHelpers.push(userGroup.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="my-auto flex">
|
<div className="my-auto flex">
|
||||||
<FiUsers className="my-auto mr-2" /> {userGroup.name}
|
<FiUsers className="my-auto mr-2" />{" "}
|
||||||
|
{userGroup.name}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
})}
|
||||||
})
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
/>
|
||||||
)}
|
<ErrorMessage
|
||||||
/>
|
name="groups"
|
||||||
<ErrorMessage
|
component="div"
|
||||||
name="groups"
|
className="text-error text-sm mt-1"
|
||||||
component="div"
|
/>
|
||||||
className="text-error text-sm mt-1"
|
</>
|
||||||
/>
|
)
|
||||||
</>
|
))}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -149,6 +149,17 @@ export function TextFormField({
|
|||||||
if (isTextArea && !heightString) {
|
if (isTextArea && !heightString) {
|
||||||
heightString = "h-28";
|
heightString = "h-28";
|
||||||
}
|
}
|
||||||
|
const [field, , helpers] = useField(name);
|
||||||
|
const { setValue } = helpers;
|
||||||
|
|
||||||
|
const handleChange = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
|
) => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(e as React.ChangeEvent<HTMLInputElement>);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
@@ -200,7 +211,7 @@ export function TextFormField({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
||||||
// onChange={onChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
{includeRevert && (
|
{includeRevert && (
|
||||||
<div className="flex-none mt-auto">
|
<div className="flex-none mt-auto">
|
||||||
@@ -396,17 +407,27 @@ export const BooleanFormField = ({
|
|||||||
alignTop,
|
alignTop,
|
||||||
checked,
|
checked,
|
||||||
}: BooleanFormFieldProps) => {
|
}: BooleanFormFieldProps) => {
|
||||||
|
const [field, meta, helpers] = useField<boolean>(name);
|
||||||
|
const { setValue } = helpers;
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue(e.target.checked);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label className="flex text-sm">
|
<label className="flex text-sm">
|
||||||
<Field
|
<Field
|
||||||
disabled={disabled}
|
|
||||||
name={name}
|
|
||||||
checked={checked}
|
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
{...field}
|
||||||
|
checked={checked !== undefined ? checked : field.value}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={handleChange}
|
||||||
className={`${removeIndent ? "mr-2" : "mx-3"}
|
className={`${removeIndent ? "mr-2" : "mx-3"}
|
||||||
px-5 w-3.5 h-3.5 ${alignTop ? "mt-1" : "my-auto"}`}
|
px-5 w-3.5 h-3.5 ${alignTop ? "mt-1" : "my-auto"}`}
|
||||||
{...(onChange ? { onChange } : {})}
|
|
||||||
/>
|
/>
|
||||||
{!noLabel && (
|
{!noLabel && (
|
||||||
<div>
|
<div>
|
||||||
|
@@ -7,7 +7,6 @@ import { NewChatIcon } from "../icons/icons";
|
|||||||
import { NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA } from "@/lib/constants";
|
import { NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA } from "@/lib/constants";
|
||||||
import { ChatSession } from "@/app/chat/interfaces";
|
import { ChatSession } from "@/app/chat/interfaces";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { SettingsContext } from "../settings/SettingsProvider";
|
|
||||||
import { pageType } from "@/app/chat/sessionSidebar/types";
|
import { pageType } from "@/app/chat/sessionSidebar/types";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { ChatBanner } from "@/app/chat/ChatBanner";
|
import { ChatBanner } from "@/app/chat/ChatBanner";
|
||||||
@@ -65,7 +64,7 @@ export default function FunctionalHeader({
|
|||||||
router.push(newChatUrl);
|
router.push(newChatUrl);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="pb-6 left-0 sticky top-0 z-20 w-full relative flex">
|
<div className="left-0 sticky top-0 z-20 w-full relative flex">
|
||||||
<div className="mt-2 mx-2.5 cursor-pointer text-text-700 relative flex w-full">
|
<div className="mt-2 mx-2.5 cursor-pointer text-text-700 relative flex w-full">
|
||||||
<LogoType
|
<LogoType
|
||||||
assistantId={currentChatSession?.persona_id}
|
assistantId={currentChatSession?.persona_id}
|
||||||
@@ -130,7 +129,7 @@ export default function FunctionalHeader({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{page != "assistants" && (
|
{page != "assistants" && (
|
||||||
<div className="h-24 left-0 absolute top-0 z-10 w-full bg-gradient-to-b via-50% z-[-1] from-background via-background to-background/10 flex" />
|
<div className="h-20 left-0 absolute top-0 z-10 w-full bg-gradient-to-b via-50% z-[-1] from-background via-background to-background/10 flex" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -111,6 +111,19 @@ export function SourceSelector({
|
|||||||
<DateRangeSelector value={timeRange} onValueChange={setTimeRange} />
|
<DateRangeSelector value={timeRange} onValueChange={setTimeRange} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{availableTags.length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="mt-4 mb-2">
|
||||||
|
<SectionTitle>Tags</SectionTitle>
|
||||||
|
</div>
|
||||||
|
<TagFilter
|
||||||
|
tags={availableTags}
|
||||||
|
selectedTags={selectedTags}
|
||||||
|
setSelectedTags={setSelectedTags}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{existingSources.length > 0 && (
|
{existingSources.length > 0 && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<div className="flex w-full gap-x-2 items-center">
|
<div className="flex w-full gap-x-2 items-center">
|
||||||
@@ -191,19 +204,6 @@ export function SourceSelector({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{availableTags.length > 0 && (
|
|
||||||
<>
|
|
||||||
<div className="mt-4 mb-2">
|
|
||||||
<SectionTitle>Tags</SectionTitle>
|
|
||||||
</div>
|
|
||||||
<TagFilter
|
|
||||||
tags={availableTags}
|
|
||||||
selectedTags={selectedTags}
|
|
||||||
setSelectedTags={setSelectedTags}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -115,7 +115,7 @@ export function TagFilter({
|
|||||||
<FiTag className="mr-1 my-auto" />
|
<FiTag className="mr-1 my-auto" />
|
||||||
Tags
|
Tags
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-x-1 gap-y-1">
|
<div className="flex overflow-y-scroll max-h-96 flex-wrap gap-x-1 gap-y-1">
|
||||||
{filteredTags.length > 0 ? (
|
{filteredTags.length > 0 ? (
|
||||||
filteredTags.map((tag) => (
|
filteredTags.map((tag) => (
|
||||||
<div
|
<div
|
||||||
|
@@ -8,6 +8,7 @@ interface UserContextType {
|
|||||||
user: User | null;
|
user: User | null;
|
||||||
isLoadingUser: boolean;
|
isLoadingUser: boolean;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
isCurator: boolean;
|
||||||
refreshUser: () => Promise<void>;
|
refreshUser: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,12 +18,16 @@ export function UserProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const [user, setUser] = useState<User | null>(null);
|
const [user, setUser] = useState<User | null>(null);
|
||||||
const [isLoadingUser, setIsLoadingUser] = useState(true);
|
const [isLoadingUser, setIsLoadingUser] = useState(true);
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
|
const [isCurator, setIsCurator] = useState(false);
|
||||||
|
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
try {
|
try {
|
||||||
const user = await getCurrentUser();
|
const user = await getCurrentUser();
|
||||||
setUser(user);
|
setUser(user);
|
||||||
setIsAdmin(user?.role === UserRole.ADMIN);
|
setIsAdmin(user?.role === UserRole.ADMIN);
|
||||||
|
setIsCurator(
|
||||||
|
user?.role === UserRole.CURATOR || user?.role == UserRole.GLOBAL_CURATOR
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching current user:", error);
|
console.error("Error fetching current user:", error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -40,7 +45,9 @@ export function UserProvider({ children }: { children: React.ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserContext.Provider value={{ user, isLoadingUser, isAdmin, refreshUser }}>
|
<UserContext.Provider
|
||||||
|
value={{ user, isLoadingUser, isAdmin, refreshUser, isCurator }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</UserContext.Provider>
|
</UserContext.Provider>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user