mirror of
https://github.com/Canner/WrenAI.git
synced 2026-09-24 23:29:49 +08:00
feat(wren-ui): support recommendation questions for home and follow-up (#910)
This commit is contained in:
@@ -7,6 +7,8 @@ export type CommonErrorFragment = { __typename?: 'Error', code?: string | null,
|
||||
|
||||
export type CommonResponseFragment = { __typename?: 'ThreadResponse', id: number, question: string, status: Types.AskingTaskStatus, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null };
|
||||
|
||||
export type CommonRecommendedQuestionsTaskFragment = { __typename?: 'RecommendedQuestionsTask', status: Types.RecommendedQuestionsTaskStatus, questions: Array<{ __typename?: 'ResultQuestion', question: string, category: string, sql: string }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null };
|
||||
|
||||
export type SuggestedQuestionsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -110,14 +112,30 @@ export type InstantRecommendedQuestionsQueryVariables = Types.Exact<{
|
||||
|
||||
export type InstantRecommendedQuestionsQuery = { __typename?: 'Query', instantRecommendedQuestions: { __typename?: 'RecommendedQuestionsTask', status: Types.RecommendedQuestionsTaskStatus, questions: Array<{ __typename?: 'ResultQuestion', question: string, category: string, sql: string }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null } };
|
||||
|
||||
export const CommonErrorFragmentDoc = gql`
|
||||
fragment CommonError on Error {
|
||||
code
|
||||
shortMessage
|
||||
message
|
||||
stacktrace
|
||||
}
|
||||
`;
|
||||
export type GetThreadRecommendationQuestionsQueryVariables = Types.Exact<{
|
||||
threadId: Types.Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetThreadRecommendationQuestionsQuery = { __typename?: 'Query', getThreadRecommendationQuestions: { __typename?: 'RecommendedQuestionsTask', status: Types.RecommendedQuestionsTaskStatus, questions: Array<{ __typename?: 'ResultQuestion', question: string, category: string, sql: string }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null } };
|
||||
|
||||
export type GetProjectRecommendationQuestionsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetProjectRecommendationQuestionsQuery = { __typename?: 'Query', getProjectRecommendationQuestions: { __typename?: 'RecommendedQuestionsTask', status: Types.RecommendedQuestionsTaskStatus, questions: Array<{ __typename?: 'ResultQuestion', question: string, category: string, sql: string }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null } };
|
||||
|
||||
export type GenerateProjectRecommendationQuestionsMutationVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GenerateProjectRecommendationQuestionsMutation = { __typename?: 'Mutation', generateProjectRecommendationQuestions: boolean };
|
||||
|
||||
export type GenerateThreadRecommendationQuestionsMutationVariables = Types.Exact<{
|
||||
threadId: Types.Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GenerateThreadRecommendationQuestionsMutation = { __typename?: 'Mutation', generateThreadRecommendationQuestions: boolean };
|
||||
|
||||
export const CommonResponseFragmentDoc = gql`
|
||||
fragment CommonResponse on ThreadResponse {
|
||||
id
|
||||
@@ -140,6 +158,27 @@ export const CommonResponseFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const CommonErrorFragmentDoc = gql`
|
||||
fragment CommonError on Error {
|
||||
code
|
||||
shortMessage
|
||||
message
|
||||
stacktrace
|
||||
}
|
||||
`;
|
||||
export const CommonRecommendedQuestionsTaskFragmentDoc = gql`
|
||||
fragment CommonRecommendedQuestionsTask on RecommendedQuestionsTask {
|
||||
status
|
||||
questions {
|
||||
question
|
||||
category
|
||||
sql
|
||||
}
|
||||
error {
|
||||
...CommonError
|
||||
}
|
||||
}
|
||||
${CommonErrorFragmentDoc}`;
|
||||
export const SuggestedQuestionsDocument = gql`
|
||||
query SuggestedQuestions {
|
||||
suggestedQuestions {
|
||||
@@ -648,18 +687,10 @@ export type CreateInstantRecommendedQuestionsMutationOptions = Apollo.BaseMutati
|
||||
export const InstantRecommendedQuestionsDocument = gql`
|
||||
query InstantRecommendedQuestions($taskId: String!) {
|
||||
instantRecommendedQuestions(taskId: $taskId) {
|
||||
status
|
||||
questions {
|
||||
question
|
||||
category
|
||||
sql
|
||||
}
|
||||
error {
|
||||
...CommonError
|
||||
}
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
${CommonErrorFragmentDoc}`;
|
||||
${CommonRecommendedQuestionsTaskFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useInstantRecommendedQuestionsQuery__
|
||||
@@ -687,4 +718,134 @@ export function useInstantRecommendedQuestionsLazyQuery(baseOptions?: Apollo.Laz
|
||||
}
|
||||
export type InstantRecommendedQuestionsQueryHookResult = ReturnType<typeof useInstantRecommendedQuestionsQuery>;
|
||||
export type InstantRecommendedQuestionsLazyQueryHookResult = ReturnType<typeof useInstantRecommendedQuestionsLazyQuery>;
|
||||
export type InstantRecommendedQuestionsQueryResult = Apollo.QueryResult<InstantRecommendedQuestionsQuery, InstantRecommendedQuestionsQueryVariables>;
|
||||
export type InstantRecommendedQuestionsQueryResult = Apollo.QueryResult<InstantRecommendedQuestionsQuery, InstantRecommendedQuestionsQueryVariables>;
|
||||
export const GetThreadRecommendationQuestionsDocument = gql`
|
||||
query GetThreadRecommendationQuestions($threadId: Int!) {
|
||||
getThreadRecommendationQuestions(threadId: $threadId) {
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
${CommonRecommendedQuestionsTaskFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetThreadRecommendationQuestionsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetThreadRecommendationQuestionsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetThreadRecommendationQuestionsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetThreadRecommendationQuestionsQuery({
|
||||
* variables: {
|
||||
* threadId: // value for 'threadId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetThreadRecommendationQuestionsQuery(baseOptions: Apollo.QueryHookOptions<GetThreadRecommendationQuestionsQuery, GetThreadRecommendationQuestionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetThreadRecommendationQuestionsQuery, GetThreadRecommendationQuestionsQueryVariables>(GetThreadRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export function useGetThreadRecommendationQuestionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetThreadRecommendationQuestionsQuery, GetThreadRecommendationQuestionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetThreadRecommendationQuestionsQuery, GetThreadRecommendationQuestionsQueryVariables>(GetThreadRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export type GetThreadRecommendationQuestionsQueryHookResult = ReturnType<typeof useGetThreadRecommendationQuestionsQuery>;
|
||||
export type GetThreadRecommendationQuestionsLazyQueryHookResult = ReturnType<typeof useGetThreadRecommendationQuestionsLazyQuery>;
|
||||
export type GetThreadRecommendationQuestionsQueryResult = Apollo.QueryResult<GetThreadRecommendationQuestionsQuery, GetThreadRecommendationQuestionsQueryVariables>;
|
||||
export const GetProjectRecommendationQuestionsDocument = gql`
|
||||
query GetProjectRecommendationQuestions {
|
||||
getProjectRecommendationQuestions {
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
${CommonRecommendedQuestionsTaskFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetProjectRecommendationQuestionsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetProjectRecommendationQuestionsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetProjectRecommendationQuestionsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetProjectRecommendationQuestionsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetProjectRecommendationQuestionsQuery(baseOptions?: Apollo.QueryHookOptions<GetProjectRecommendationQuestionsQuery, GetProjectRecommendationQuestionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetProjectRecommendationQuestionsQuery, GetProjectRecommendationQuestionsQueryVariables>(GetProjectRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export function useGetProjectRecommendationQuestionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetProjectRecommendationQuestionsQuery, GetProjectRecommendationQuestionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetProjectRecommendationQuestionsQuery, GetProjectRecommendationQuestionsQueryVariables>(GetProjectRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export type GetProjectRecommendationQuestionsQueryHookResult = ReturnType<typeof useGetProjectRecommendationQuestionsQuery>;
|
||||
export type GetProjectRecommendationQuestionsLazyQueryHookResult = ReturnType<typeof useGetProjectRecommendationQuestionsLazyQuery>;
|
||||
export type GetProjectRecommendationQuestionsQueryResult = Apollo.QueryResult<GetProjectRecommendationQuestionsQuery, GetProjectRecommendationQuestionsQueryVariables>;
|
||||
export const GenerateProjectRecommendationQuestionsDocument = gql`
|
||||
mutation GenerateProjectRecommendationQuestions {
|
||||
generateProjectRecommendationQuestions
|
||||
}
|
||||
`;
|
||||
export type GenerateProjectRecommendationQuestionsMutationFn = Apollo.MutationFunction<GenerateProjectRecommendationQuestionsMutation, GenerateProjectRecommendationQuestionsMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useGenerateProjectRecommendationQuestionsMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useGenerateProjectRecommendationQuestionsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useGenerateProjectRecommendationQuestionsMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [generateProjectRecommendationQuestionsMutation, { data, loading, error }] = useGenerateProjectRecommendationQuestionsMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGenerateProjectRecommendationQuestionsMutation(baseOptions?: Apollo.MutationHookOptions<GenerateProjectRecommendationQuestionsMutation, GenerateProjectRecommendationQuestionsMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<GenerateProjectRecommendationQuestionsMutation, GenerateProjectRecommendationQuestionsMutationVariables>(GenerateProjectRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export type GenerateProjectRecommendationQuestionsMutationHookResult = ReturnType<typeof useGenerateProjectRecommendationQuestionsMutation>;
|
||||
export type GenerateProjectRecommendationQuestionsMutationResult = Apollo.MutationResult<GenerateProjectRecommendationQuestionsMutation>;
|
||||
export type GenerateProjectRecommendationQuestionsMutationOptions = Apollo.BaseMutationOptions<GenerateProjectRecommendationQuestionsMutation, GenerateProjectRecommendationQuestionsMutationVariables>;
|
||||
export const GenerateThreadRecommendationQuestionsDocument = gql`
|
||||
mutation GenerateThreadRecommendationQuestions($threadId: Int!) {
|
||||
generateThreadRecommendationQuestions(threadId: $threadId)
|
||||
}
|
||||
`;
|
||||
export type GenerateThreadRecommendationQuestionsMutationFn = Apollo.MutationFunction<GenerateThreadRecommendationQuestionsMutation, GenerateThreadRecommendationQuestionsMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useGenerateThreadRecommendationQuestionsMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useGenerateThreadRecommendationQuestionsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useGenerateThreadRecommendationQuestionsMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [generateThreadRecommendationQuestionsMutation, { data, loading, error }] = useGenerateThreadRecommendationQuestionsMutation({
|
||||
* variables: {
|
||||
* threadId: // value for 'threadId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGenerateThreadRecommendationQuestionsMutation(baseOptions?: Apollo.MutationHookOptions<GenerateThreadRecommendationQuestionsMutation, GenerateThreadRecommendationQuestionsMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<GenerateThreadRecommendationQuestionsMutation, GenerateThreadRecommendationQuestionsMutationVariables>(GenerateThreadRecommendationQuestionsDocument, options);
|
||||
}
|
||||
export type GenerateThreadRecommendationQuestionsMutationHookResult = ReturnType<typeof useGenerateThreadRecommendationQuestionsMutation>;
|
||||
export type GenerateThreadRecommendationQuestionsMutationResult = Apollo.MutationResult<GenerateThreadRecommendationQuestionsMutation>;
|
||||
export type GenerateThreadRecommendationQuestionsMutationOptions = Apollo.BaseMutationOptions<GenerateThreadRecommendationQuestionsMutation, GenerateThreadRecommendationQuestionsMutationVariables>;
|
||||
@@ -32,6 +32,22 @@ const COMMON_RESPONSE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
const COMMON_RECOMMENDED_QUESTIONS_TASK = gql`
|
||||
fragment CommonRecommendedQuestionsTask on RecommendedQuestionsTask {
|
||||
status
|
||||
questions {
|
||||
question
|
||||
category
|
||||
sql
|
||||
}
|
||||
error {
|
||||
...CommonError
|
||||
}
|
||||
}
|
||||
|
||||
${COMMON_ERROR}
|
||||
`;
|
||||
|
||||
export const SUGGESTED_QUESTIONS = gql`
|
||||
query SuggestedQuestions {
|
||||
suggestedQuestions {
|
||||
@@ -191,16 +207,40 @@ export const CREATE_INSTANT_RECOMMENDED_QUESTIONS = gql`
|
||||
export const INSTANT_RECOMMENDED_QUESTIONS = gql`
|
||||
query InstantRecommendedQuestions($taskId: String!) {
|
||||
instantRecommendedQuestions(taskId: $taskId) {
|
||||
status
|
||||
questions {
|
||||
question
|
||||
category
|
||||
sql
|
||||
}
|
||||
error {
|
||||
...CommonError
|
||||
}
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
${COMMON_ERROR}
|
||||
${COMMON_RECOMMENDED_QUESTIONS_TASK}
|
||||
`;
|
||||
|
||||
export const GET_THREAD_RECOMMENDATION_QUESTIONS = gql`
|
||||
query GetThreadRecommendationQuestions($threadId: Int!) {
|
||||
getThreadRecommendationQuestions(threadId: $threadId) {
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
|
||||
${COMMON_RECOMMENDED_QUESTIONS_TASK}
|
||||
`;
|
||||
|
||||
export const GET_PROJECT_RECOMMENDATION_QUESTIONS = gql`
|
||||
query GetProjectRecommendationQuestions {
|
||||
getProjectRecommendationQuestions {
|
||||
...CommonRecommendedQuestionsTask
|
||||
}
|
||||
}
|
||||
|
||||
${COMMON_RECOMMENDED_QUESTIONS_TASK}
|
||||
`;
|
||||
|
||||
export const GENERATE_PROJECT_RECOMMENDATION_QUESTIONS = gql`
|
||||
mutation GenerateProjectRecommendationQuestions {
|
||||
generateProjectRecommendationQuestions
|
||||
}
|
||||
`;
|
||||
|
||||
export const GENERATE_THREAD_RECOMMENDATION_QUESTIONS = gql`
|
||||
mutation GenerateThreadRecommendationQuestions($threadId: Int!) {
|
||||
generateThreadRecommendationQuestions(threadId: $threadId)
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -4,9 +4,13 @@ import { useMemo } from 'react';
|
||||
import { Skeleton } from 'antd';
|
||||
import BulbOutlined from '@ant-design/icons/BulbOutlined';
|
||||
import { makeIterable } from '@/utils/iteration';
|
||||
import {
|
||||
RecommendedQuestionsTask,
|
||||
RecommendedQuestionsTaskStatus,
|
||||
} from '@/apollo/client/graphql/__types__';
|
||||
|
||||
interface Props {
|
||||
items: string[];
|
||||
items: { question: string; sql: string }[];
|
||||
loading?: boolean;
|
||||
error?: {
|
||||
shortMessage?: string;
|
||||
@@ -15,7 +19,7 @@ interface Props {
|
||||
stacktrace?: string[];
|
||||
};
|
||||
className?: string;
|
||||
onSelect?: (question: string) => void;
|
||||
onSelect?: ({ question, sql }: { question: string; sql: string }) => void;
|
||||
}
|
||||
|
||||
const StyledSkeleton = styled(Skeleton)`
|
||||
@@ -31,17 +35,38 @@ const StyledSkeleton = styled(Skeleton)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const getRecommendedQuestionProps = (
|
||||
data: RecommendedQuestionsTask,
|
||||
show = true,
|
||||
) => {
|
||||
if (!data || !show) return { show: false };
|
||||
const questions = (data?.questions || []).slice(0, 3).map((item) => ({
|
||||
question: item.question,
|
||||
sql: item.sql,
|
||||
}));
|
||||
const loading = data?.status === RecommendedQuestionsTaskStatus.GENERATING;
|
||||
return {
|
||||
show: loading || questions.length > 0,
|
||||
state: {
|
||||
items: questions,
|
||||
loading,
|
||||
error: data?.error,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const QuestionItem = (props: {
|
||||
index: number;
|
||||
question: string;
|
||||
onSelect?: (question: string) => void;
|
||||
sql: string;
|
||||
onSelect?: ({ question, sql }: { question: string; sql: string }) => void;
|
||||
}) => {
|
||||
const { index, question, onSelect } = props;
|
||||
const { index, question, sql, onSelect } = props;
|
||||
return (
|
||||
<div className={clsx(index > 0 && 'mt-1')}>
|
||||
<span
|
||||
className="cursor-pointer hover:text"
|
||||
onClick={() => onSelect(question)}
|
||||
onClick={() => onSelect({ question, sql })}
|
||||
>
|
||||
{question}
|
||||
</span>
|
||||
@@ -53,7 +78,10 @@ const QuestionList = makeIterable(QuestionItem);
|
||||
export default function RecommendedQuestions(props: Props) {
|
||||
const { items, loading, className, onSelect } = props;
|
||||
|
||||
const data = useMemo(() => items.map((question) => ({ question })), [items]);
|
||||
const data = useMemo(
|
||||
() => items.map(({ question, sql }) => ({ question, sql })),
|
||||
[items],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={clsx('bg-gray-2 rounded p-3', className)}>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Space, Button, Row, Col } from 'antd';
|
||||
import ColumnHeightOutlined from '@ant-design/icons/ColumnHeightOutlined';
|
||||
import MinusOutlined from '@ant-design/icons/MinusOutlined';
|
||||
import EllipsisWrapper from '@/components/EllipsisWrapper';
|
||||
import { Logo } from '@/components/Logo';
|
||||
import { makeIterable } from '@/utils/iteration';
|
||||
|
||||
const CategorySectionBlock = styled.div`
|
||||
background: var(--gray-1);
|
||||
border: 1px solid var(--gray-4);
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
`;
|
||||
|
||||
const QuestionBlock = styled.div`
|
||||
background: var(--gray-1);
|
||||
user-select: none;
|
||||
height: 150px;
|
||||
transition: border-color ease 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--geekblue-6) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const MAX_EXPANDED_QUESTIONS = 9;
|
||||
|
||||
const QuestionTemplate = ({ category, sql, question, onSelect }) => {
|
||||
return (
|
||||
<Col span={8}>
|
||||
<QuestionBlock
|
||||
className="border border-gray-5 rounded px-3 pt-3 pb-4 cursor-pointer"
|
||||
onClick={() => onSelect({ sql, question })}
|
||||
>
|
||||
<div className="d-flex justify-space-between align-center text-sm mb-3">
|
||||
<div className="border border-gray-5 px-2 rounded-pill">
|
||||
{category}
|
||||
</div>
|
||||
</div>
|
||||
<EllipsisWrapper multipleLine={4} text={question} />
|
||||
</QuestionBlock>
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
||||
const QuestionColumnIterator = makeIterable(QuestionTemplate);
|
||||
|
||||
export default function RecommendedQuestionsPrompt(props) {
|
||||
const { onSelect, recommendedQuestions, buttonProps } = props;
|
||||
|
||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
||||
|
||||
const questionList = useMemo(() => {
|
||||
return recommendedQuestions.slice(
|
||||
0,
|
||||
isExpanded ? undefined : MAX_EXPANDED_QUESTIONS,
|
||||
);
|
||||
}, [recommendedQuestions, isExpanded]);
|
||||
|
||||
const onHandleToggle = () => setIsExpanded((prev) => !prev);
|
||||
|
||||
const showExpandButton = recommendedQuestions.length > MAX_EXPANDED_QUESTIONS;
|
||||
|
||||
return (
|
||||
<div className="bg-gray-2 px-10 py-6">
|
||||
<div className="d-flex align-center mb-3 justify-space-between">
|
||||
<Logo size={24} color="var(--gray-8)" />
|
||||
<div className="text-md text-medium gray-8 mx-3">
|
||||
Know more about your data.
|
||||
</div>
|
||||
<div className="text-medium gray-7">
|
||||
Try asking some of the following questions
|
||||
</div>
|
||||
<Button className="ml-3" {...buttonProps} />
|
||||
</div>
|
||||
<Space
|
||||
style={{ width: 680 }}
|
||||
className="gray-8"
|
||||
direction="vertical"
|
||||
size={[0, 16]}
|
||||
>
|
||||
<CategorySectionBlock>
|
||||
<Row gutter={[16, 16]} className="mt-3">
|
||||
<QuestionColumnIterator data={questionList} onSelect={onSelect} />
|
||||
</Row>
|
||||
{showExpandButton && (
|
||||
<div className="text-right">
|
||||
<Button
|
||||
onClick={() => onHandleToggle()}
|
||||
className="gray-6 mt-3"
|
||||
type="text"
|
||||
size="small"
|
||||
icon={isExpanded ? <MinusOutlined /> : <ColumnHeightOutlined />}
|
||||
>
|
||||
{isExpanded ? 'Collapse' : 'Expand all'}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CategorySectionBlock>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -11,13 +11,14 @@ import MessageOutlined from '@ant-design/icons/MessageOutlined';
|
||||
import ErrorCollapse from '@/components/ErrorCollapse';
|
||||
import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined';
|
||||
import useAskProcessState from '@/hooks/useAskProcessState';
|
||||
import RecommendedQuestions from '@/components/pages/home/RecommendedQuestions';
|
||||
import RecommendedQuestions, {
|
||||
getRecommendedQuestionProps,
|
||||
} from '@/components/pages/home/RecommendedQuestions';
|
||||
import MarkdownBlock from '@/components/editor/MarkdownBlock';
|
||||
import {
|
||||
AskingTask,
|
||||
AskingTaskType,
|
||||
RecommendedQuestionsTask,
|
||||
RecommendedQuestionsTaskStatus,
|
||||
} from '@/apollo/client/graphql/__types__';
|
||||
|
||||
const StyledResult = styled.div`
|
||||
@@ -42,7 +43,13 @@ interface Props {
|
||||
};
|
||||
error?: any;
|
||||
onSelectResult: (payload: { sql: string }) => void;
|
||||
onSelectQuestion: (question: string) => void;
|
||||
onSelectQuestion: ({
|
||||
question,
|
||||
sql,
|
||||
}: {
|
||||
question: string;
|
||||
sql: string;
|
||||
}) => void;
|
||||
onClose: () => void;
|
||||
onStop: () => void;
|
||||
loading?: boolean;
|
||||
@@ -56,20 +63,6 @@ const Wrapper = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const getRecommendedQuestionProps = (data: RecommendedQuestionsTask) => {
|
||||
if (!data) return { show: false };
|
||||
const questions = (data?.questions || []).map((item) => item.question);
|
||||
const loading = data?.status === RecommendedQuestionsTaskStatus.GENERATING;
|
||||
return {
|
||||
show: loading || questions.length > 0,
|
||||
state: {
|
||||
items: questions,
|
||||
loading,
|
||||
error: data?.error,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const makeProcessing = (text: string) => (props: Props) => {
|
||||
const { onStop } = props;
|
||||
return (
|
||||
|
||||
@@ -132,11 +132,10 @@ export default forwardRef<Attributes, Props>(function Prompt(props, ref) {
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
const selectQuestion = async (value: string) => {
|
||||
setInputValue(value);
|
||||
onStopStreaming && onStopStreaming();
|
||||
const selectQuestion = async (payload) => {
|
||||
onSelect && (await onSelect(payload));
|
||||
closeResult();
|
||||
askProcessState.resetState();
|
||||
onSubmit && (await onSubmit(value));
|
||||
};
|
||||
|
||||
const selectResult = async (payload) => {
|
||||
|
||||
@@ -9,6 +9,10 @@ import SaveOutlined from '@ant-design/icons/SaveOutlined';
|
||||
import FileDoneOutlined from '@ant-design/icons/FileDoneOutlined';
|
||||
import StepContent from '@/components/pages/home/promptThread/StepContent';
|
||||
import { getIsFinished } from '@/hooks/useAskPrompt';
|
||||
import { RecommendedQuestionsProps } from '@/components/pages/home/promptThread';
|
||||
import RecommendedQuestions, {
|
||||
getRecommendedQuestionProps,
|
||||
} from '@/components/pages/home/RecommendedQuestions';
|
||||
import { ThreadResponse } from '@/apollo/client/graphql/__types__';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
@@ -39,6 +43,9 @@ interface Props {
|
||||
isLastThreadResponse: boolean;
|
||||
onOpenSaveAsViewModal: (data: { sql: string; responseId: number }) => void;
|
||||
onInitPreviewDone: () => void;
|
||||
|
||||
// recommended questions
|
||||
recommendedQuestionsProps: RecommendedQuestionsProps;
|
||||
}
|
||||
|
||||
const QuestionTitle = (props) => {
|
||||
@@ -61,6 +68,7 @@ export default function AnswerResult(props: Props) {
|
||||
isLastThreadResponse,
|
||||
onOpenSaveAsViewModal,
|
||||
onInitPreviewDone,
|
||||
recommendedQuestionsProps,
|
||||
} = props;
|
||||
|
||||
const { question, status, error } = threadResponse;
|
||||
@@ -75,8 +83,13 @@ export default function AnswerResult(props: Props) {
|
||||
? { minHeight: 'calc(100vh - (194px))' }
|
||||
: null;
|
||||
|
||||
const recommendedQuestionProps = getRecommendedQuestionProps(
|
||||
recommendedQuestionsProps.data,
|
||||
recommendedQuestionsProps.show,
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={resultStyle}>
|
||||
<div style={resultStyle} className="adm-answer-result">
|
||||
<QuestionTitle className="mb-9" question={question} />
|
||||
{error ? (
|
||||
<Alert
|
||||
@@ -137,6 +150,13 @@ export default function AnswerResult(props: Props) {
|
||||
Save as View
|
||||
</Button>
|
||||
)}
|
||||
{isLastThreadResponse && recommendedQuestionProps.show && (
|
||||
<RecommendedQuestions
|
||||
className="mt-5 mb-4"
|
||||
{...recommendedQuestionProps.state}
|
||||
onSelect={recommendedQuestionsProps.onSelect}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</StyledSkeleton>
|
||||
)}
|
||||
|
||||
@@ -4,15 +4,32 @@ import { Divider } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import AnswerResult from './AnswerResult';
|
||||
import { makeIterable, IterableComponent } from '@/utils/iteration';
|
||||
import { getIsFinished } from '@/hooks/useAskPrompt';
|
||||
import {
|
||||
DetailedThread,
|
||||
RecommendedQuestionsTask,
|
||||
ThreadResponse,
|
||||
} from '@/apollo/client/graphql/__types__';
|
||||
import { getIsFinished } from '@/hooks/useAskPrompt';
|
||||
|
||||
interface SelectQuestionProps {
|
||||
question: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
export interface RecommendedQuestionsProps {
|
||||
data: RecommendedQuestionsTask;
|
||||
show: boolean;
|
||||
onSelect: ({ question, sql }: SelectQuestionProps) => void;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: DetailedThread;
|
||||
data: {
|
||||
thread: DetailedThread;
|
||||
recommendedQuestions: RecommendedQuestionsTask;
|
||||
showRecommendedQuestions: boolean;
|
||||
};
|
||||
onOpenSaveAsViewModal: (data: { sql: string; responseId: number }) => void;
|
||||
onSelect: ({ question, sql }: SelectQuestionProps) => void;
|
||||
}
|
||||
|
||||
const StyledPromptThread = styled.div`
|
||||
@@ -44,11 +61,17 @@ const AnswerResultTemplate: React.FC<
|
||||
motion: boolean;
|
||||
onOpenSaveAsViewModal: (data: { sql: string; responseId: number }) => void;
|
||||
onInitPreviewDone: () => void;
|
||||
recommendedQuestionsProps: {
|
||||
data: RecommendedQuestionsTask;
|
||||
show: boolean;
|
||||
onSelect: ({ question, sql }: SelectQuestionProps) => void;
|
||||
};
|
||||
}
|
||||
> = ({
|
||||
data,
|
||||
index,
|
||||
motion,
|
||||
recommendedQuestionsProps,
|
||||
onOpenSaveAsViewModal,
|
||||
onInitPreviewDone,
|
||||
...threadResponse
|
||||
@@ -66,6 +89,7 @@ const AnswerResultTemplate: React.FC<
|
||||
onOpenSaveAsViewModal={onOpenSaveAsViewModal}
|
||||
onInitPreviewDone={onInitPreviewDone}
|
||||
threadResponse={threadResponse}
|
||||
recommendedQuestionsProps={recommendedQuestionsProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -77,26 +101,29 @@ export default function PromptThread(props: Props) {
|
||||
const router = useRouter();
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
const motionResponsesRef = useRef<Record<number, boolean>>({});
|
||||
const { data, onOpenSaveAsViewModal } = props;
|
||||
const { data, onOpenSaveAsViewModal, onSelect } = props;
|
||||
|
||||
const responses = useMemo(
|
||||
() =>
|
||||
(data?.responses || []).map((response) => ({
|
||||
(data.thread?.responses || []).map((response) => ({
|
||||
...response,
|
||||
motion: motionResponsesRef.current[response.id],
|
||||
})),
|
||||
[data?.responses],
|
||||
[data.thread?.responses],
|
||||
);
|
||||
|
||||
const triggerScrollToBottom = (behavior?: ScrollBehavior) => {
|
||||
if ((data?.responses || []).length <= 1) return;
|
||||
if ((data.thread?.responses || []).length <= 1) return;
|
||||
const contentLayout = divRef.current?.parentElement;
|
||||
const lastChild = divRef.current?.lastElementChild as HTMLElement;
|
||||
const lastChildElement = lastChild?.lastElementChild as HTMLElement;
|
||||
const allElements = (divRef.current?.querySelectorAll(
|
||||
'.adm-answer-result',
|
||||
) || []) as HTMLElement[];
|
||||
const lastAnswerResult = allElements[allElements.length - 1];
|
||||
|
||||
const dividerSpace = 48;
|
||||
if (contentLayout && lastChildElement) {
|
||||
if (contentLayout && lastAnswerResult) {
|
||||
contentLayout.scrollTo({
|
||||
top: lastChildElement.offsetTop - dividerSpace,
|
||||
top: lastAnswerResult.offsetTop - dividerSpace,
|
||||
behavior,
|
||||
});
|
||||
}
|
||||
@@ -109,7 +136,7 @@ export default function PromptThread(props: Props) {
|
||||
}, [router.query]);
|
||||
|
||||
useEffect(() => {
|
||||
motionResponsesRef.current = (data?.responses || []).reduce(
|
||||
motionResponsesRef.current = (data.thread?.responses || []).reduce(
|
||||
(result, item) => {
|
||||
result[item.id] = !getIsFinished(item?.status);
|
||||
return result;
|
||||
@@ -118,7 +145,7 @@ export default function PromptThread(props: Props) {
|
||||
);
|
||||
const lastResponseMotion = Object.values(motionResponsesRef.current).pop();
|
||||
triggerScrollToBottom(lastResponseMotion ? 'smooth' : 'auto');
|
||||
}, [data?.responses]);
|
||||
}, [data.thread?.responses]);
|
||||
|
||||
const onInitPreviewDone = () => {
|
||||
triggerScrollToBottom();
|
||||
@@ -130,6 +157,11 @@ export default function PromptThread(props: Props) {
|
||||
data={responses}
|
||||
onOpenSaveAsViewModal={onOpenSaveAsViewModal}
|
||||
onInitPreviewDone={onInitPreviewDone}
|
||||
recommendedQuestionsProps={{
|
||||
data: data.recommendedQuestions,
|
||||
show: data.showRecommendedQuestions,
|
||||
onSelect,
|
||||
}}
|
||||
/>
|
||||
</StyledPromptThread>
|
||||
);
|
||||
|
||||
@@ -30,10 +30,13 @@ export const getIsFinished = (status: AskingTaskStatus) =>
|
||||
AskingTaskStatus.STOPPED,
|
||||
].includes(status);
|
||||
|
||||
const isRecommendedFinished = (status: RecommendedQuestionsTaskStatus) =>
|
||||
export const isRecommendedFinished = (status: RecommendedQuestionsTaskStatus) =>
|
||||
[
|
||||
RecommendedQuestionsTaskStatus.FINISHED,
|
||||
RecommendedQuestionsTaskStatus.FAILED,
|
||||
|
||||
// for existing thread response & existing projects that are not executed to generate recommendation questions
|
||||
RecommendedQuestionsTaskStatus.NOT_STARTED,
|
||||
].includes(status);
|
||||
|
||||
const isNeedRecommendedQuestions = (askingTask: AskingTask) => {
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { groupBy, orderBy, flatMap } from 'lodash';
|
||||
import { message } from 'antd';
|
||||
import Icon from '@ant-design/icons';
|
||||
import ReloadOutlined from '@ant-design/icons/ReloadOutlined';
|
||||
import { CopilotSVG } from '@/utils/svgs';
|
||||
import { isRecommendedFinished } from '@/hooks/useAskPrompt';
|
||||
import {
|
||||
ResultQuestion,
|
||||
RecommendedQuestionsTaskStatus,
|
||||
} from '@/apollo/client/graphql/__types__';
|
||||
import {
|
||||
useGetProjectRecommendationQuestionsLazyQuery,
|
||||
useGenerateProjectRecommendationQuestionsMutation,
|
||||
} from '@/apollo/client/graphql/home.generated';
|
||||
|
||||
interface GroupedQuestion {
|
||||
category: string;
|
||||
question: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
const getGroupedQuestions = (
|
||||
questions: ResultQuestion[],
|
||||
): GroupedQuestion[] => {
|
||||
const groupedData = groupBy(questions, 'category');
|
||||
return orderBy(
|
||||
flatMap(groupedData),
|
||||
(item) => groupedData[item.category].length, // Sort by number of questions in each category
|
||||
'desc',
|
||||
);
|
||||
};
|
||||
|
||||
export default function useRecommendedQuestionsInstruction() {
|
||||
const [showRetry, setShowRetry] = useState<boolean>(false);
|
||||
const [generating, setGenerating] = useState<boolean>(false);
|
||||
const [isRegenerate, setIsRegenerate] = useState<boolean>(false);
|
||||
const [
|
||||
showRecommendedQuestionsPromptMode,
|
||||
setShowRecommendedQuestionsPromptMode,
|
||||
] = useState<boolean>(false);
|
||||
const [recommendedQuestions, setRecommendedQuestions] = useState<
|
||||
GroupedQuestion[]
|
||||
>([]);
|
||||
|
||||
const [fetchRecommendationQuestions, recommendationQuestionsResult] =
|
||||
useGetProjectRecommendationQuestionsLazyQuery({
|
||||
pollInterval: 2000,
|
||||
});
|
||||
|
||||
const [generateProjectRecommendationQuestions] =
|
||||
useGenerateProjectRecommendationQuestionsMutation();
|
||||
|
||||
const recommendedQuestionsTask = useMemo(
|
||||
() =>
|
||||
recommendationQuestionsResult.data?.getProjectRecommendationQuestions ||
|
||||
null,
|
||||
[recommendationQuestionsResult.data],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRecommendationQuestionsData = async () => {
|
||||
const result = await fetchRecommendationQuestions();
|
||||
const data = result.data?.getProjectRecommendationQuestions;
|
||||
|
||||
// for existing projects that do not have to generate recommended questions yet
|
||||
if (isRecommendedFinished(data.status)) {
|
||||
if (data.questions.length > 0) {
|
||||
// for regenerate then leave and go back to the home page
|
||||
setRecommendedQuestions(getGroupedQuestions(data.questions));
|
||||
|
||||
setShowRecommendedQuestionsPromptMode(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchRecommendationQuestionsData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isRecommendedFinished(recommendedQuestionsTask?.status)) {
|
||||
recommendationQuestionsResult.stopPolling();
|
||||
|
||||
if (recommendedQuestionsTask.questions.length === 0) {
|
||||
isRegenerate && setShowRetry(true);
|
||||
|
||||
if (
|
||||
showRecommendedQuestionsPromptMode &&
|
||||
recommendedQuestionsTask.status ===
|
||||
RecommendedQuestionsTaskStatus.FAILED
|
||||
) {
|
||||
message.error(
|
||||
`We couldn't regenerate questions right now. Let's try again later.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
setIsRegenerate(true);
|
||||
|
||||
// update to recommendedQuestions
|
||||
setRecommendedQuestions(
|
||||
getGroupedQuestions(recommendedQuestionsTask.questions),
|
||||
);
|
||||
setShowRecommendedQuestionsPromptMode(true);
|
||||
}
|
||||
|
||||
setGenerating(false);
|
||||
}
|
||||
}, [recommendedQuestionsTask]);
|
||||
|
||||
const onGetRecommendationQuestions = async () => {
|
||||
setGenerating(true);
|
||||
setIsRegenerate(true);
|
||||
try {
|
||||
await generateProjectRecommendationQuestions();
|
||||
fetchRecommendationQuestions();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonProps = useMemo(() => {
|
||||
const baseProps = {
|
||||
loading: generating,
|
||||
onClick: onGetRecommendationQuestions,
|
||||
};
|
||||
|
||||
if (showRecommendedQuestionsPromptMode && isRegenerate) {
|
||||
return {
|
||||
...baseProps,
|
||||
icon: <ReloadOutlined />,
|
||||
children: 'Regenerate',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...baseProps,
|
||||
icon: showRetry ? (
|
||||
<ReloadOutlined />
|
||||
) : (
|
||||
<Icon component={CopilotSVG} className="geekblue-6" />
|
||||
),
|
||||
children: generating
|
||||
? 'Generating questions'
|
||||
: showRetry
|
||||
? 'Retry'
|
||||
: 'What could I ask?',
|
||||
};
|
||||
}, [generating, isRegenerate, showRetry, showRecommendedQuestionsPromptMode]);
|
||||
|
||||
return {
|
||||
recommendedQuestions,
|
||||
generating,
|
||||
showRetry,
|
||||
showRecommendedQuestionsPromptMode,
|
||||
buttonProps,
|
||||
};
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ComponentRef, useEffect, useMemo, useRef } from 'react';
|
||||
import { ComponentRef, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { message } from 'antd';
|
||||
import { Path } from '@/utils/enum';
|
||||
import useHomeSidebar from '@/hooks/useHomeSidebar';
|
||||
import SiderLayout from '@/components/layouts/SiderLayout';
|
||||
import Prompt from '@/components/pages/home/prompt';
|
||||
import useAskPrompt, {
|
||||
getIsFinished,
|
||||
isRecommendedFinished,
|
||||
} from '@/hooks/useAskPrompt';
|
||||
import useModalAction from '@/hooks/useModalAction';
|
||||
import PromptThread from '@/components/pages/home/promptThread';
|
||||
import SaveAsViewModal from '@/components/modals/SaveAsViewModal';
|
||||
import {
|
||||
useCreateThreadResponseMutation,
|
||||
useThreadQuery,
|
||||
useThreadResponseLazyQuery,
|
||||
useGenerateThreadRecommendationQuestionsMutation,
|
||||
useGetThreadRecommendationQuestionsLazyQuery,
|
||||
} from '@/apollo/client/graphql/home.generated';
|
||||
import useAskPrompt, { getIsFinished } from '@/hooks/useAskPrompt';
|
||||
import useModalAction from '@/hooks/useModalAction';
|
||||
import PromptThread from '@/components/pages/home/promptThread';
|
||||
import SaveAsViewModal from '@/components/modals/SaveAsViewModal';
|
||||
import { useCreateViewMutation } from '@/apollo/client/graphql/view.generated';
|
||||
|
||||
export default function HomeThread() {
|
||||
@@ -25,6 +30,10 @@ export default function HomeThread() {
|
||||
const threadId = useMemo(() => Number(params?.id) || null, [params]);
|
||||
const askPrompt = useAskPrompt(threadId);
|
||||
const saveAsViewModal = useModalAction();
|
||||
|
||||
const [showRecommendedQuestions, setShowRecommendedQuestions] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [createViewMutation, { loading: creating }] = useCreateViewMutation({
|
||||
onError: (error) => console.error(error),
|
||||
onCompleted: () => message.success('Successfully created view.'),
|
||||
@@ -67,6 +76,16 @@ export default function HomeThread() {
|
||||
},
|
||||
});
|
||||
|
||||
const [generateThreadRecommendationQuestions] =
|
||||
useGenerateThreadRecommendationQuestionsMutation();
|
||||
|
||||
const [
|
||||
fetchThreadRecommendationQuestions,
|
||||
threadRecommendationQuestionsResult,
|
||||
] = useGetThreadRecommendationQuestionsLazyQuery({
|
||||
pollInterval: 1000,
|
||||
});
|
||||
|
||||
const thread = useMemo(() => data?.thread || null, [data]);
|
||||
const threadResponse = useMemo(
|
||||
() => threadResponseResult.data?.threadResponse || null,
|
||||
@@ -81,7 +100,13 @@ export default function HomeThread() {
|
||||
useEffect(() => {
|
||||
askPrompt.onStopPolling();
|
||||
threadResponseResult.stopPolling();
|
||||
threadRecommendationQuestionsResult.stopPolling();
|
||||
$prompt.current?.close();
|
||||
|
||||
if (threadId !== null) {
|
||||
fetchThreadRecommendationQuestions({ variables: { threadId } });
|
||||
setShowRecommendedQuestions(true);
|
||||
}
|
||||
}, [threadId]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -99,18 +124,50 @@ export default function HomeThread() {
|
||||
}, [thread]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFinished) threadResponseResult.stopPolling();
|
||||
if (isFinished) {
|
||||
threadResponseResult.stopPolling();
|
||||
setShowRecommendedQuestions(true);
|
||||
}
|
||||
}, [isFinished]);
|
||||
|
||||
const recommendedQuestions = useMemo(
|
||||
() =>
|
||||
threadRecommendationQuestionsResult.data
|
||||
?.getThreadRecommendationQuestions || null,
|
||||
[threadRecommendationQuestionsResult.data],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isRecommendedFinished(recommendedQuestions?.status)) {
|
||||
threadRecommendationQuestionsResult.stopPolling();
|
||||
}
|
||||
}, [recommendedQuestions]);
|
||||
|
||||
const result = useMemo(
|
||||
() => ({
|
||||
thread,
|
||||
recommendedQuestions,
|
||||
showRecommendedQuestions,
|
||||
}),
|
||||
[thread, recommendedQuestions, showRecommendedQuestions],
|
||||
);
|
||||
|
||||
const onSelect = async (payload) => {
|
||||
try {
|
||||
askPrompt.onStopPolling();
|
||||
const response = await createThreadResponse({
|
||||
variables: { threadId: thread.id, data: payload },
|
||||
});
|
||||
generateThreadRecommendationQuestions({
|
||||
variables: { threadId: thread.id },
|
||||
});
|
||||
setShowRecommendedQuestions(false);
|
||||
await fetchThreadResponse({
|
||||
variables: { responseId: response.data.createThreadResponse.id },
|
||||
});
|
||||
fetchThreadRecommendationQuestions({
|
||||
variables: { threadId: thread.id },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -119,8 +176,9 @@ export default function HomeThread() {
|
||||
return (
|
||||
<SiderLayout loading={false} sidebar={homeSidebar}>
|
||||
<PromptThread
|
||||
data={thread}
|
||||
data={result}
|
||||
onOpenSaveAsViewModal={saveAsViewModal.openModal}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
<div className="py-12" />
|
||||
<Prompt ref={$prompt} {...askPrompt} onSelect={onSelect} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ComponentRef, useMemo, useRef } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Button, Typography } from 'antd';
|
||||
import { Logo } from '@/components/Logo';
|
||||
import { Path } from '@/utils/enum';
|
||||
import { nextTick } from '@/utils/time';
|
||||
@@ -8,10 +9,83 @@ import Prompt from '@/components/pages/home/prompt';
|
||||
import DemoPrompt from '@/components/pages/home/prompt/DemoPrompt';
|
||||
import useHomeSidebar from '@/hooks/useHomeSidebar';
|
||||
import useAskPrompt from '@/hooks/useAskPrompt';
|
||||
import useRecommendedQuestionsInstruction from '@/hooks/useRecommendedQuestionsInstruction';
|
||||
import RecommendedQuestionsPrompt from '@/components/pages/home/prompt/RecommendedQuestionsPrompt';
|
||||
import {
|
||||
useSuggestedQuestionsQuery,
|
||||
useCreateThreadMutation,
|
||||
useGenerateThreadRecommendationQuestionsMutation,
|
||||
} from '@/apollo/client/graphql/home.generated';
|
||||
import { useGetSettingsQuery } from '@/apollo/client/graphql/settings.generated';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const Wrapper = ({ children }) => {
|
||||
return (
|
||||
<div
|
||||
className="d-flex align-center justify-center flex-column"
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<Logo size={48} color="var(--gray-8)" />
|
||||
<div className="text-md text-medium gray-8 mt-3">
|
||||
Know more about your data
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SampleQuestionsInstruction = (props) => {
|
||||
const { sampleQuestions, onSelect } = props;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<DemoPrompt demo={sampleQuestions} onSelect={onSelect} />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
function RecommendedQuestionsInstruction(props) {
|
||||
const { onSelect } = props;
|
||||
|
||||
const {
|
||||
buttonProps,
|
||||
generating,
|
||||
recommendedQuestions,
|
||||
showRetry,
|
||||
showRecommendedQuestionsPromptMode,
|
||||
} = useRecommendedQuestionsInstruction();
|
||||
|
||||
return showRecommendedQuestionsPromptMode ? (
|
||||
<div
|
||||
className="d-flex align-center flex-column pt-10"
|
||||
style={{ margin: 'auto' }}
|
||||
>
|
||||
<RecommendedQuestionsPrompt
|
||||
recommendedQuestions={recommendedQuestions}
|
||||
onSelect={onSelect}
|
||||
buttonProps={buttonProps}
|
||||
/>
|
||||
<div className="py-12" />
|
||||
</div>
|
||||
) : (
|
||||
<Wrapper>
|
||||
<Button className="mt-6" {...buttonProps} />
|
||||
{generating && (
|
||||
<Text className="mt-3 text-sm gray-6">
|
||||
Thinking of good questions for you... (about 1 minute)
|
||||
</Text>
|
||||
)}
|
||||
{!generating && showRetry && (
|
||||
<Text className="mt-3 text-sm gray-6 text-center">
|
||||
We couldn't think of questions right now.
|
||||
<br />
|
||||
Let's try again later.
|
||||
</Text>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const $prompt = useRef<ComponentRef<typeof Prompt>>(null);
|
||||
@@ -26,14 +100,22 @@ export default function Home() {
|
||||
onCompleted: () => homeSidebar.refetch(),
|
||||
});
|
||||
|
||||
const [generateThreadRecommendationQuestions] =
|
||||
useGenerateThreadRecommendationQuestionsMutation();
|
||||
|
||||
const { data: settingsResult } = useGetSettingsQuery();
|
||||
const settings = settingsResult?.settings;
|
||||
const isSampleDataset = useMemo(
|
||||
() => Boolean(settings?.dataSource?.sampleDataset),
|
||||
[settings],
|
||||
);
|
||||
|
||||
const sampleQuestions = useMemo(
|
||||
() => suggestedQuestionsData?.suggestedQuestions.questions || [],
|
||||
[suggestedQuestionsData],
|
||||
);
|
||||
|
||||
const isSampleDataset = sampleQuestions.length > 0;
|
||||
|
||||
const onDemoSelect = async ({ question }) => {
|
||||
const onSelectQuestion = async ({ question }) => {
|
||||
$prompt.current.setValue(question);
|
||||
await nextTick();
|
||||
$prompt.current.submit();
|
||||
@@ -43,6 +125,9 @@ export default function Home() {
|
||||
try {
|
||||
askPrompt.onStopPolling();
|
||||
const response = await createThread({ variables: { data: payload } });
|
||||
generateThreadRecommendationQuestions({
|
||||
variables: { threadId: response.data.createThread.id },
|
||||
});
|
||||
router.push(Path.Home + `/${response.data.createThread.id}`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -51,19 +136,16 @@ export default function Home() {
|
||||
|
||||
return (
|
||||
<SiderLayout loading={false} sidebar={homeSidebar}>
|
||||
<div
|
||||
className="d-flex align-center justify-center flex-column"
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<Logo size={48} color="var(--gray-8)" />
|
||||
<div className="text-md text-medium gray-8 mt-3">
|
||||
Know more about your data
|
||||
</div>
|
||||
{isSampleDataset && (
|
||||
<SampleQuestionsInstruction
|
||||
sampleQuestions={sampleQuestions}
|
||||
onSelect={onSelectQuestion}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isSampleDataset && (
|
||||
<DemoPrompt demo={sampleQuestions} onSelect={onDemoSelect} />
|
||||
)}
|
||||
</div>
|
||||
{!isSampleDataset && (
|
||||
<RecommendedQuestionsInstruction onSelect={onSelectQuestion} />
|
||||
)}
|
||||
<Prompt ref={$prompt} {...askPrompt} onSelect={onSelect} />
|
||||
</SiderLayout>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export const CopilotSVG = ({
|
||||
fillCurrentColor = true,
|
||||
className,
|
||||
}: {
|
||||
fillCurrentColor?: boolean;
|
||||
className?: string;
|
||||
}) => (
|
||||
<svg
|
||||
className={className}
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18.6562 11.7478L13.9791 10.0247L12.2522 5.34375C12.1201 4.98542 11.8813 4.67621 11.5679 4.45781C11.2546 4.2394 10.8819 4.12231 10.5 4.12231C10.1181 4.12231 9.74535 4.2394 9.43205 4.45781C9.11875 4.67621 8.87993 4.98542 8.74781 5.34375L7.02469 10.0247L2.34375 11.7478C1.98542 11.8799 1.67621 12.1187 1.45781 12.432C1.2394 12.7454 1.12231 13.1181 1.12231 13.5C1.12231 13.8819 1.2394 14.2546 1.45781 14.568C1.67621 14.8813 1.98542 15.1201 2.34375 15.2522L7.02094 16.9753L8.74781 21.6562C8.87993 22.0146 9.11875 22.3238 9.43205 22.5422C9.74535 22.7606 10.1181 22.8777 10.5 22.8777C10.8819 22.8777 11.2546 22.7606 11.5679 22.5422C11.8813 22.3238 12.1201 22.0146 12.2522 21.6562L13.9753 16.9791L18.6562 15.2522C19.0146 15.1201 19.3238 14.8813 19.5422 14.568C19.7606 14.2546 19.8777 13.8819 19.8777 13.5C19.8777 13.1181 19.7606 12.7454 19.5422 12.432C19.3238 12.1187 19.0146 11.8799 18.6562 11.7478ZM13.0312 14.9259C12.7777 15.0192 12.5475 15.1664 12.3565 15.3574C12.1655 15.5484 12.0183 15.7787 11.925 16.0322L10.5 19.9012L9.07406 16.0312C8.98079 15.778 8.83365 15.548 8.64282 15.3572C8.45198 15.1663 8.222 15.0192 7.96875 14.9259L4.09875 13.5L7.96875 12.0741C8.222 11.9808 8.45198 11.8337 8.64282 11.6428C8.83365 11.452 8.98079 11.222 9.07406 10.9688L10.5 7.09875L11.9259 10.9688C12.0192 11.2223 12.1664 11.4525 12.3574 11.6435C12.5484 11.8345 12.7787 11.9817 13.0322 12.075L16.9012 13.5L13.0312 14.9259ZM13.125 3.75C13.125 3.45163 13.2435 3.16548 13.4545 2.9545C13.6655 2.74353 13.9516 2.625 14.25 2.625H15.375V1.5C15.375 1.20163 15.4935 0.915483 15.7045 0.704505C15.9155 0.493526 16.2016 0.375 16.5 0.375C16.7984 0.375 17.0845 0.493526 17.2955 0.704505C17.5065 0.915483 17.625 1.20163 17.625 1.5V2.625H18.75C19.0484 2.625 19.3345 2.74353 19.5455 2.9545C19.7565 3.16548 19.875 3.45163 19.875 3.75C19.875 4.04837 19.7565 4.33452 19.5455 4.5455C19.3345 4.75647 19.0484 4.875 18.75 4.875H17.625V6C17.625 6.29837 17.5065 6.58452 17.2955 6.7955C17.0845 7.00647 16.7984 7.125 16.5 7.125C16.2016 7.125 15.9155 7.00647 15.7045 6.7955C15.4935 6.58452 15.375 6.29837 15.375 6V4.875H14.25C13.9516 4.875 13.6655 4.75647 13.4545 4.5455C13.2435 4.33452 13.125 4.04837 13.125 3.75ZM23.625 8.25C23.625 8.54837 23.5065 8.83452 23.2955 9.0455C23.0845 9.25647 22.7984 9.375 22.5 9.375H22.125V9.75C22.125 10.0484 22.0065 10.3345 21.7955 10.5455C21.5845 10.7565 21.2984 10.875 21 10.875C20.7016 10.875 20.4155 10.7565 20.2045 10.5455C19.9935 10.3345 19.875 10.0484 19.875 9.75V9.375H19.5C19.2016 9.375 18.9155 9.25647 18.7045 9.0455C18.4935 8.83452 18.375 8.54837 18.375 8.25C18.375 7.95163 18.4935 7.66548 18.7045 7.4545C18.9155 7.24353 19.2016 7.125 19.5 7.125H19.875V6.75C19.875 6.45163 19.9935 6.16548 20.2045 5.9545C20.4155 5.74353 20.7016 5.625 21 5.625C21.2984 5.625 21.5845 5.74353 21.7955 5.9545C22.0065 6.16548 22.125 6.45163 22.125 6.75V7.125H22.5C22.7984 7.125 23.0845 7.24353 23.2955 7.4545C23.5065 7.66548 23.625 7.95163 23.625 8.25Z"
|
||||
fill={fillCurrentColor ? 'currentColor' : undefined}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './CopilotSVG';
|
||||
Reference in New Issue
Block a user