diff --git a/src/client/components/CodeBlock.tsx b/src/client/components/CodeBlock.tsx new file mode 100644 index 0000000..ef77f19 --- /dev/null +++ b/src/client/components/CodeBlock.tsx @@ -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 ( +
+
+        {props.code}
+      
+
+ ); +}); +CodeBlock.displayName = 'CodeBlock'; diff --git a/src/client/components/survey/SurveyDownloadBtn.tsx b/src/client/components/survey/SurveyDownloadBtn.tsx index dc5b9f0..609948d 100644 --- a/src/client/components/survey/SurveyDownloadBtn.tsx +++ b/src/client/components/survey/SurveyDownloadBtn.tsx @@ -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 = React.memo( return ( - + diff --git a/src/client/components/survey/SurveyEditForm.tsx b/src/client/components/survey/SurveyEditForm.tsx index 3e28538..b4c97f7 100644 --- a/src/client/components/survey/SurveyEditForm.tsx +++ b/src/client/components/survey/SurveyEditForm.tsx @@ -53,7 +53,7 @@ export type SurveyEditFormValues = z.infer; function generateDefaultItem() { return { label: 'New Field', - name: 'field-' + generateRandomString(4), + name: 'field_' + generateRandomString(4), type: 'text' as const, }; } diff --git a/src/client/components/survey/SurveyUsageBtn.tsx b/src/client/components/survey/SurveyUsageBtn.tsx new file mode 100644 index 0000000..dd7a3a1 --- /dev/null +++ b/src/client/components/survey/SurveyUsageBtn.tsx @@ -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 = 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 ( + + + + + + + {t('Example code')} + + {t('Add this example code into your project')} + + + + + + + + + ); + } +); +SurveyUsageBtn.displayName = 'SurveyUsageBtn'; diff --git a/src/client/package.json b/src/client/package.json index 9aa352d..9dc60b7 100644 --- a/src/client/package.json +++ b/src/client/package.json @@ -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 ", diff --git a/src/client/routes/survey/$surveyId/index.tsx b/src/client/routes/survey/$surveyId/index.tsx index 93b9793..1f8c5a5 100644 --- a/src/client/routes/survey/$surveyId/index.tsx +++ b/src/client/routes/survey/$surveyId/index.tsx @@ -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() { -
+
{t('Count')}
{count}
-
+
+
diff --git a/src/client/utils/__snapshots__/survey.spec.ts.snap b/src/client/utils/__snapshots__/survey.spec.ts.snap new file mode 100644 index 0000000..6432b36 --- /dev/null +++ b/src/client/utils/__snapshots__/survey.spec.ts.snap @@ -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( + '', + '', + { + textField, // Text + } + ); + console.log('Submit success'); + } catch (err) { + console.error(err); + throw new Error(err); + } +};" +`; diff --git a/src/client/utils/survey.spec.ts b/src/client/utils/survey.spec.ts new file mode 100644 index 0000000..0381b4e --- /dev/null +++ b/src/client/utils/survey.spec.ts @@ -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: '', + workspaceId: '', + name: 'Test', + payload: { + items: [ + { + name: 'textField', + label: 'Text', + type: 'text', + }, + ], + }, + createdAt: '', + updatedAt: '', + }) + ).matchSnapshot(); + }); +}); diff --git a/src/client/utils/survey.ts b/src/client/utils/survey.ts new file mode 100644 index 0000000..1cd3335 --- /dev/null +++ b/src/client/utils/survey.ts @@ -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; +}