feat(wren-ui): Integrating suggested questios API (#73)

* feat(wren-ui): integrating suggested questios API

* fix(wren-ui): fix suggested questions types in resolver

---------

Co-authored-by: shimin <shimin@shimins-MacBook-Pro.local>
This commit is contained in:
Shimin
2024-04-03 16:17:42 +08:00
committed by GitHub
co-authored by shimin
parent 9bc20250a4
commit 3e47c645a8
9 changed files with 143 additions and 53 deletions
+12 -6
View File
@@ -14,11 +14,6 @@ export type Scalars = {
JSON: any;
};
export type AskQuestionResponse = {
__typename?: 'AskQuestionResponse';
questions: Array<Maybe<Scalars['String']>>;
};
export type AskingTask = {
__typename?: 'AskingTask';
candidates: Array<ResultCandidate>;
@@ -399,7 +394,6 @@ export type PreviewDataInput = {
export type Query = {
__typename?: 'Query';
askQuestions: AskQuestionResponse;
askingTask: AskingTask;
autoGenerateRelation?: Maybe<Array<RecommandRelations>>;
diagram: Diagram;
@@ -408,6 +402,7 @@ export type Query = {
model: DetailedModel;
modelSync?: Maybe<ModelSyncResponse>;
onboardingStatus: OnboardingStatusResponse;
suggestedQuestions: SuggestedQuestionResponse;
thread: DetailedThread;
threadResponse: ThreadResponse;
threads: Array<Thread>;
@@ -501,6 +496,17 @@ export type SimpleMeasureInput = {
type: Scalars['String'];
};
export type SuggestedQuestion = {
__typename?: 'SuggestedQuestion';
label: Scalars['String'];
question: Scalars['String'];
};
export type SuggestedQuestionResponse = {
__typename?: 'SuggestedQuestionResponse';
questions: Array<Maybe<SuggestedQuestion>>;
};
export type Task = {
__typename?: 'Task';
id: Scalars['String'];
@@ -7,6 +7,11 @@ 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 }> } | null };
export type SuggestedQuestionsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type SuggestedQuestionsQuery = { __typename?: 'Query', suggestedQuestions: { __typename?: 'SuggestedQuestionResponse', questions: Array<{ __typename?: 'SuggestedQuestion', label: string, question: string } | null> } };
export type AskingTaskQueryVariables = Types.Exact<{
taskId: Types.Scalars['String'];
}>;
@@ -101,6 +106,43 @@ export const CommonResponseFragmentDoc = gql`
}
}
`;
export const SuggestedQuestionsDocument = gql`
query SuggestedQuestions {
suggestedQuestions {
questions {
label
question
}
}
}
`;
/**
* __useSuggestedQuestionsQuery__
*
* To run a query within a React component, call `useSuggestedQuestionsQuery` and pass it any options that fit your needs.
* When your component renders, `useSuggestedQuestionsQuery` 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 } = useSuggestedQuestionsQuery({
* variables: {
* },
* });
*/
export function useSuggestedQuestionsQuery(baseOptions?: Apollo.QueryHookOptions<SuggestedQuestionsQuery, SuggestedQuestionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<SuggestedQuestionsQuery, SuggestedQuestionsQueryVariables>(SuggestedQuestionsDocument, options);
}
export function useSuggestedQuestionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SuggestedQuestionsQuery, SuggestedQuestionsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<SuggestedQuestionsQuery, SuggestedQuestionsQueryVariables>(SuggestedQuestionsDocument, options);
}
export type SuggestedQuestionsQueryHookResult = ReturnType<typeof useSuggestedQuestionsQuery>;
export type SuggestedQuestionsLazyQueryHookResult = ReturnType<typeof useSuggestedQuestionsLazyQuery>;
export type SuggestedQuestionsQueryResult = Apollo.QueryResult<SuggestedQuestionsQuery, SuggestedQuestionsQueryVariables>;
export const AskingTaskDocument = gql`
query AskingTask($taskId: String!) {
askingTask(taskId: $taskId) {
+11
View File
@@ -26,6 +26,17 @@ const COMMON_RESPONSE = gql`
}
`;
export const SUGGESTED_QUESTIONS = gql`
query SuggestedQuestions {
suggestedQuestions {
questions {
label
question
}
}
}
`;
export const ASKING_TASK = gql`
query AskingTask($taskId: String!) {
askingTask(taskId: $taskId) {
+10 -10
View File
@@ -64,16 +64,16 @@ export const sampleDatasets: Record<string, SampleDataset> = {
],
questions: [
{
question: 'What are the top 5 selling albums in the US',
label: 'Ranking ',
question: 'What are the top 5 selling albums in the US?',
label: 'Ranking',
},
{
question: 'What is the total revenue generated from each genre',
question: 'What is the total revenue generated from each genre?',
label: 'Aggregation',
},
{
question:
'Which customers have made purchases of tracks from albums in each genre',
'Which customers have made purchases of tracks from albums in each genre?',
label: 'General',
},
],
@@ -160,17 +160,17 @@ export const sampleDatasets: Record<string, SampleDataset> = {
questions: [
{
question:
'What are the top 3 value for orders placed by customers in each city',
'What are the top 3 value for orders placed by customers in each city?',
label: 'Ranking',
},
{
question:
'What is the average score of reviews submitted for orders placed by customers in each city',
'What is the average score of reviews submitted for orders placed by customers in each city?',
label: 'Aggregation',
},
{
question:
'What is the total value of payments made by customers from each state',
'What is the total value of payments made by customers from each state?',
label: 'Aggregation',
},
],
@@ -241,17 +241,17 @@ export const sampleDatasets: Record<string, SampleDataset> = {
questions: [
{
question:
'How many three-pointers were made by each player in each game',
'How many three-pointers were made by each player in each game?',
label: 'Aggregation',
},
{
question:
'What is the differences in turnover rates between teams with high and low average scores',
'What is the differences in turnover rates between teams with high and low average scores?',
label: 'Comparison',
},
{
question:
'Which teams had the highest average points scored per game throughout the season',
'Which teams had the highest average points scored per game throughout the season?',
label: 'Ranking',
},
],
@@ -9,14 +9,19 @@ import { IContext } from '../types';
import { getLogger } from '@server/utils';
import { format } from 'sql-formatter';
import { constructCteSql } from '../services/askingService';
import { SampleDatasetName, getSampleAskQuestions } from '../data';
import {
SuggestedQuestion,
SampleDatasetName,
getSampleAskQuestions,
} from '../data';
const logger = getLogger('AskingResolver');
logger.level = 'debug';
export interface AskQuestion {
questions: string[];
export interface SuggestedQuestionResponse {
questions: SuggestedQuestion[];
}
export interface Task {
id: string;
}
@@ -57,7 +62,7 @@ export class AskingResolver {
_root: any,
_args: any,
ctx: IContext,
): Promise<AskQuestion> {
): Promise<SuggestedQuestionResponse> {
const project = await ctx.projectService.getCurrentProject();
const { sampleDataset } = project;
if (!sampleDataset) {
@@ -4,6 +4,8 @@ import { makeIterable } from '@/utils/iteration';
import EllipsisWrapper from '@/components/EllipsisWrapper';
const DemoBlock = styled.div`
user-select: none;
height: 150px;
&:hover {
border-color: var(--geekblue-6) !important;
transition: border-color ease 0.2s;
@@ -15,17 +17,17 @@ interface Props {
onSelect: (data: any) => void;
}
const DemoTemplate = ({ title, summary, onSelect }) => {
const DemoTemplate = ({ label, question, onSelect }) => {
return (
<Col span={8}>
<DemoBlock
className="border border-gray-5 rounded px-3 pt-3 pb-4 cursor-pointer"
onClick={() => onSelect({ title, summary })}
onClick={() => onSelect({ label, question })}
>
<div className="d-flex justify-space-between align-center text-sm mb-3">
<div className="border border-gray-5 px-2 rounded-pill">{title}</div>
<div className="border border-gray-5 px-2 rounded-pill">{label}</div>
</div>
<EllipsisWrapper multipleLine={3} text={summary} />
<EllipsisWrapper multipleLine={4} text={question} />
</DemoBlock>
</Col>
);
@@ -45,6 +45,8 @@ const StyledCollapse = styled(Collapse)`
`;
const ResultBlock = styled.div`
user-select: none;
height: 129px;
&:hover {
border-color: var(--geekblue-6) !important;
transition: border-color ease 0.2s;
@@ -1,4 +1,11 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import {
useEffect,
useMemo,
useRef,
useState,
forwardRef,
useImperativeHandle,
} from 'react';
import { Input, Button } from 'antd';
import styled from 'styled-components';
import { PROCESS_STATE } from '@/utils/enum';
@@ -22,6 +29,11 @@ interface Props {
data?: AskingTask;
}
interface Attributes {
setValue: (value: string) => void;
submit: () => void;
}
const PromptStyle = styled.div`
position: fixed;
width: 768px;
@@ -49,7 +61,7 @@ const convertAskingTaskToProcessState = (data: AskingTask) => {
return processState;
};
export default function Prompt(props: Props) {
export default forwardRef<Attributes, Props>(function Prompt(props, ref) {
const $promptInput = useRef<HTMLTextAreaElement>(null);
const { data, onSubmit, onStop, onSelect } = props;
const [inputValue, setInputValue] = useState('');
@@ -102,16 +114,24 @@ export default function Prompt(props: Props) {
const inputEnter = (event) => {
if (event.shiftKey) return;
event.preventDefault();
if (!isProcessing) submitAsk();
submitAsk();
};
const submitAsk = async () => {
if (question) {
onSubmit && (await onSubmit(question));
}
if (isProcessing || !question) return;
onSubmit && (await onSubmit(question));
};
useImperativeHandle(
ref,
() => ({
setValue: (value: string) => setInputValue(value),
submit: submitAsk,
}),
[question, isProcessing, setInputValue],
);
return (
<PromptStyle className="d-flex align-end bg-gray-2 p-3 border border-gray-3 rounded">
<Input.TextArea
@@ -144,4 +164,4 @@ export default function Prompt(props: Props) {
/>
</PromptStyle>
);
}
});
+23 -21
View File
@@ -1,42 +1,41 @@
import { ComponentRef, useMemo, useRef } from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image';
import { Path } from '@/utils/enum';
import { nextTick } from '@/utils/time';
import SiderLayout from '@/components/layouts/SiderLayout';
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 { useCreateThreadMutation } from '@/apollo/client/graphql/home.generated';
const demoData = [
{
title: 'General question',
summary: 'Show me top 10 best-selling product last month.',
},
{
title: 'Drill into metrics',
summary:
'List the name of the users who successfully convert to paying customers last week.',
},
{
title: 'Aggregate data',
summary:
"Help me categorize customers' ages into groups segmented by every 10 years.",
},
];
import {
useSuggestedQuestionsQuery,
useCreateThreadMutation,
} from '@/apollo/client/graphql/home.generated';
export default function Home() {
const $prompt = useRef<ComponentRef<typeof Prompt>>(null);
const router = useRouter();
const homeSidebar = useHomeSidebar();
const askPrompt = useAskPrompt();
const { data: suggestedQuestionsData } = useSuggestedQuestionsQuery();
const [createThread] = useCreateThreadMutation({
onCompleted: () => homeSidebar.refetch(),
});
const isDemo = true;
const sampleQuestions = useMemo(
() => suggestedQuestionsData?.suggestedQuestions.questions || [],
[suggestedQuestionsData],
);
const onDemoSelect = () => {};
const isSampleDataset = sampleQuestions.length > 0;
const onDemoSelect = async ({ question }) => {
$prompt.current.setValue(question);
await nextTick();
$prompt.current.submit();
};
const onSelect = async (payload) => {
try {
@@ -64,9 +63,12 @@ export default function Home() {
Know more about your data
</div>
{isDemo && <DemoPrompt demo={demoData} onSelect={onDemoSelect} />}
{isSampleDataset && (
<DemoPrompt demo={sampleQuestions} onSelect={onDemoSelect} />
)}
</div>
<Prompt
ref={$prompt}
data={askPrompt.data}
onSubmit={askPrompt.onSubmit}
onStop={askPrompt.onStop}