feat: add survey usage button

This commit is contained in:
moonrailgun 2024-05-15 20:32:25 +08:00
parent cc0bd73ed1
commit 2b75a8edad
9 changed files with 195 additions and 6 deletions

View File

@ -0,0 +1,37 @@
import { useEvent } from '@/hooks/useEvent';
import React, { useState } from 'react';
import { Button } from './ui/button';
import { LuCopy, LuCopyCheck } from 'react-icons/lu';
import { toast } from 'sonner';
import { useTranslation } from '@i18next-toolkit/react';
export const CodeBlock: React.FC<{
code: string;
}> = React.memo((props) => {
const [copied, setCopied] = useState(false);
const { t } = useTranslation();
const handleCopy = useEvent(() => {
window.navigator.clipboard.writeText(props.code);
toast(t('Copied into clipboard!'));
setCopied(true);
});
return (
<div className="group relative overflow-auto">
<pre className="rounded-sm border border-zinc-800 bg-zinc-900 p-3 pr-12 text-sm">
<code>{props.code}</code>
</pre>
<Button
className="absolute right-1 top-1 bg-opacity-50 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-opacity-50"
variant="outline"
size="icon"
Icon={copied ? LuCopyCheck : LuCopy}
onClick={handleCopy}
/>
</div>
);
});
CodeBlock.displayName = 'CodeBlock';

View File

@ -10,7 +10,7 @@ import {
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { useTranslation } from '@i18next-toolkit/react';
import { LuCalendar } from 'react-icons/lu';
import { LuCalendar, LuDownloadCloud } from 'react-icons/lu';
import { cn } from '@/utils/style';
import dayjs from 'dayjs';
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
@ -131,7 +131,7 @@ export const SurveyDownloadBtn: React.FC<SurveyDownloadBtnProps> = React.memo(
return (
<Dialog>
<DialogTrigger asChild>
<Button>{t('Download')}</Button>
<Button Icon={LuDownloadCloud}>{t('Download')}</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>

View File

@ -53,7 +53,7 @@ export type SurveyEditFormValues = z.infer<typeof addFormSchema>;
function generateDefaultItem() {
return {
label: 'New Field',
name: 'field-' + generateRandomString(4),
name: 'field_' + generateRandomString(4),
type: 'text' as const,
};
}

View File

@ -0,0 +1,59 @@
import React from 'react';
import {
DialogHeader,
Dialog,
DialogTrigger,
DialogContent,
DialogTitle,
DialogDescription,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { useTranslation } from '@i18next-toolkit/react';
import { LuCode2 } from 'react-icons/lu';
import { trpc } from '@/api/trpc';
import { useCurrentWorkspaceId } from '@/store/user';
import { CodeBlock } from '../CodeBlock';
import { generateSurveyExampleCode } from '@/utils/survey';
interface SurveyUsageBtnProps {
surveyId: string;
}
export const SurveyUsageBtn: React.FC<SurveyUsageBtnProps> = React.memo(
(props) => {
const { surveyId } = props;
const workspaceId = useCurrentWorkspaceId();
const { t } = useTranslation();
const { data: info } = trpc.survey.get.useQuery({
workspaceId,
surveyId,
});
const fields = info?.payload.items ?? [];
const exampleCode = generateSurveyExampleCode(window.location.origin, info);
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" Icon={LuCode2}>
{t('Usage')}
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('Example code')}</DialogTitle>
<DialogDescription>
{t('Add this example code into your project')}
</DialogDescription>
</DialogHeader>
<CodeBlock code="npm install tianji-client-sdk" />
<CodeBlock code={exampleCode} />
</DialogContent>
</Dialog>
);
}
);
SurveyUsageBtn.displayName = 'SurveyUsageBtn';

View File

@ -12,7 +12,7 @@
"translation:extract": "i18next-toolkit extract",
"translation:scan": "i18next-toolkit scan",
"translation:translate": "i18next-toolkit translate",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "vitest"
},
"keywords": [],
"author": "moonrailgun <moonrailgun@gmail.com>",

View File

@ -20,6 +20,7 @@ import { DataTable, createColumnHelper } from '@/components/DataTable';
import { useMemo } from 'react';
import { SurveyDownloadBtn } from '@/components/survey/SurveyDownloadBtn';
import dayjs from 'dayjs';
import { SurveyUsageBtn } from '@/components/survey/SurveyUsageBtn';
type SurveyResultItem =
AppRouterOutput['survey']['resultList']['items'][number];
@ -124,14 +125,15 @@ function PageComponent() {
<ScrollArea className="h-full overflow-hidden p-4">
<ScrollBar orientation="horizontal" />
<div className="mb-4 ">
<div className="mb-4">
<Card>
<CardHeader>
<CardTitle>{t('Count')}</CardTitle>
</CardHeader>
<CardContent className="flex justify-between">
<div>{count}</div>
<div>
<div className="flex gap-2">
<SurveyUsageBtn surveyId={surveyId} />
<SurveyDownloadBtn surveyId={surveyId} />
</div>
</CardContent>

View File

@ -0,0 +1,28 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`survey > example code 1`] = `
"import { submitSurvey, initOpenapiSDK } from 'tianji-client-sdk';
initOpenapiSDK('https://example.com');
/**
* Submit Survey into Test
*
* @param {text} textField Text
*/
async function submitForm(textField) {
try {
await submitSurvey(
'<workspaceId>',
'<surveyId>',
{
textField, // Text
}
);
console.log('Submit success');
} catch (err) {
console.error(err);
throw new Error(err);
}
};"
`;

View File

@ -0,0 +1,25 @@
import { describe, test, expect } from 'vitest';
import { generateSurveyExampleCode } from './survey';
describe('survey', () => {
test('example code', () => {
expect(
generateSurveyExampleCode('https://example.com', {
id: '<surveyId>',
workspaceId: '<workspaceId>',
name: 'Test',
payload: {
items: [
{
name: 'textField',
label: 'Text',
type: 'text',
},
],
},
createdAt: '',
updatedAt: '',
})
).matchSnapshot();
});
});

View File

@ -0,0 +1,38 @@
import { AppRouterOutput } from '@/api/trpc';
/**
* Generate survey example code
*/
export function generateSurveyExampleCode(
host: string,
info?: AppRouterOutput['survey']['get']
): string {
const fields = info?.payload.items ?? [];
const exampleCode = `import { submitSurvey, initOpenapiSDK } from 'tianji-client-sdk';
initOpenapiSDK('${host}');
/**
* Submit Survey into ${info?.name ?? 'Tianji'}
*
* ${fields.map((field) => `@param {${field.type}} ${field.name} ${field.label}`).join('\n * ')}
*/
async function submitForm(${fields.map((field) => field.name).join(', ')}) {
try {
await submitSurvey(
'${info?.workspaceId}',
'${info?.id}',
{
${fields.map((field) => `${field.name}, // ${field.label}`).join('\n ')}
}
);
console.log('Submit success');
} catch (err) {
console.error(err);
throw new Error(err);
}
};`;
return exampleCode;
}