diff --git a/package-lock.json b/package-lock.json index e0b63b9c4..b0df71dbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,7 +52,7 @@ "devDependencies": { "@babel/eslint-parser": "~7.17.0", "@pingcap-inc/tidb-community-editor": "0.6.7", - "@pingcap-inc/tidb-community-site-components": "^1.8.19", + "@pingcap-inc/tidb-community-site-components": "^1.8.20", "@pingcap-inc/tidb-community-ui": "^1.3.3", "@sentry/webpack-plugin": "~1.18.8", "@svgr/webpack": "~6.2.1", @@ -5378,11 +5378,10 @@ } }, "node_modules/@pingcap-inc/tidb-community-site-components": { - "version": "1.8.19", - "resolved": "https://npm.pkg.github.com/download/@pingcap-inc/tidb-community-site-components/1.8.19/cd8dcfe0428a7120eb5a3415d4f23c9b27829f87", - "integrity": "sha512-Wen5C56H8tFnUB9ZCuhHwkFWiO178sx5U3/QVF9NtY8q2dEY3Mv0tnrTj3udjrvm1IlwqFMu/nESm8wZi2z0OA==", + "version": "1.8.20", + "resolved": "https://npm.pkg.github.com/download/@pingcap-inc/tidb-community-site-components/1.8.20/31ef25a8c1df6266700f4a8f8669a3cf358c1bad", + "integrity": "sha512-8hTCvavn+Di9QicRYS4J2fPU2J6QcwYL4aG/1i/aQyrkyjIYfSNuOHh1KXb7RQxafc7dno1WF6wQR5IZ6u+6UQ==", "dev": true, - "license": "MIT", "peerDependencies": { "@ant-design/icons": "^4.7.0", "@pingcap-inc/tidb-community-ui": "*", @@ -31062,9 +31061,9 @@ } }, "@pingcap-inc/tidb-community-site-components": { - "version": "1.8.19", - "resolved": "https://npm.pkg.github.com/download/@pingcap-inc/tidb-community-site-components/1.8.19/cd8dcfe0428a7120eb5a3415d4f23c9b27829f87", - "integrity": "sha512-Wen5C56H8tFnUB9ZCuhHwkFWiO178sx5U3/QVF9NtY8q2dEY3Mv0tnrTj3udjrvm1IlwqFMu/nESm8wZi2z0OA==", + "version": "1.8.20", + "resolved": "https://npm.pkg.github.com/download/@pingcap-inc/tidb-community-site-components/1.8.20/31ef25a8c1df6266700f4a8f8669a3cf358c1bad", + "integrity": "sha512-8hTCvavn+Di9QicRYS4J2fPU2J6QcwYL4aG/1i/aQyrkyjIYfSNuOHh1KXb7RQxafc7dno1WF6wQR5IZ6u+6UQ==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index ed7fc3953..c9bddf016 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "copy:locales": "rm -rf public/locales && cp -r packages/i18n/locales public", "deploy:production": "shipit production deploy", "deploy:preview": "shipit preview deploy", - "dev": "npm run start:i18n && npm run next:dev", + "dev": "npm run start:i18n & npm run next:dev", "lint": "next lint && eslint . --ext .js,.jsx,.ts,.tsx", "postinstall": "lerna bootstrap --ignore=@tidb-community/ui", "prepare": "husky install", @@ -81,7 +81,7 @@ "devDependencies": { "@babel/eslint-parser": "~7.17.0", "@pingcap-inc/tidb-community-editor": "0.6.7", - "@pingcap-inc/tidb-community-site-components": "^1.8.19", + "@pingcap-inc/tidb-community-site-components": "^1.8.20", "@pingcap-inc/tidb-community-ui": "^1.3.3", "@sentry/webpack-plugin": "~1.18.8", "@svgr/webpack": "~6.2.1", diff --git a/packages/datasource/src/api/index.d.ts b/packages/datasource/src/api/index.d.ts index e4acec326..1e2479f6a 100644 --- a/packages/datasource/src/api/index.d.ts +++ b/packages/datasource/src/api/index.d.ts @@ -9,6 +9,7 @@ export * as points from './points'; export { me } from './me'; export * as cms from './cms'; export * as common from './common'; +export * as support from './support'; export type ApiResponse = { detail: Detail; diff --git a/packages/datasource/src/api/index.js b/packages/datasource/src/api/index.js index 57a37f166..2915f1de7 100644 --- a/packages/datasource/src/api/index.js +++ b/packages/datasource/src/api/index.js @@ -11,6 +11,7 @@ export * as asktug from './asktug'; export * as points from './points'; export * as cms from './cms'; export * as common from './common'; +export * as support from './support'; export { default as client } from './client'; export { default as blogClient } from './blogClient'; diff --git a/packages/datasource/src/api/support/index.ts b/packages/datasource/src/api/support/index.ts new file mode 100644 index 000000000..7cfcd7901 --- /dev/null +++ b/packages/datasource/src/api/support/index.ts @@ -0,0 +1,14 @@ +import client from '../client'; + +export const sendPhoneCode = ({ phone }: { phone: string }) => + client.post('/api/official/contact-us/sms-code', { + phone, + }); + +export const submitSupportForm = (data: { + name: string; + phone?: string; + phone_code?: string; + company: string; + consult_detail: string; +}) => client.post('/api/community-contact-us', data); diff --git a/src/pages/_app.page.js b/src/pages/_app.page.js index 77813cd43..6ffa91833 100644 --- a/src/pages/_app.page.js +++ b/src/pages/_app.page.js @@ -7,7 +7,6 @@ import React, { useEffect, useState } from 'react'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import useSWR, { SWRConfig } from 'swr'; -import { Provider } from 'react-redux'; import { api, useApiErrorListener } from '@tidb-community/datasource'; import { appWithTranslation } from 'next-i18next'; import { constants, createAppGlobalStyle, utils } from '@tidb-community/ui'; @@ -22,14 +21,13 @@ import ErrorPage from './_error.page'; import nextI18NextConfig from '@/next-i18next.config'; import { authContext, AuthContext, MeContext } from '~/context'; import { isEmptyOrNil } from '~/utils/common.utils'; -import { store } from '~/redux'; import '@fullcalendar/common/main.css'; import '@fullcalendar/daygrid/main.css'; // Import Swiper styles -import "swiper/css"; -import "swiper/css/pagination"; +import 'swiper/css'; +import 'swiper/css/pagination'; import { SiteComponentsContext, @@ -165,16 +163,14 @@ const App = ({ Component, pageProps, router }) => { revalidateOnFocus: false, }} > - - - - - - - - - - + + + + + + + + ); }; diff --git a/src/pages/contact-us/index.page.js b/src/pages/contact-us/index.page.js index 7a526e980..8d6389199 100644 --- a/src/pages/contact-us/index.page.js +++ b/src/pages/contact-us/index.page.js @@ -43,9 +43,7 @@ const Page = () => { 想要获得商业专家产品最高响应级别 7*24 支持服务 规划/实施/主动式巡检/故障协查/知识转移/重要时期保障 - - > 联系 PingCAP - + > 商业支持咨询 diff --git a/src/pages/support/_components/SupportForm.tsx b/src/pages/support/_components/SupportForm.tsx new file mode 100644 index 000000000..ecfe5a0fc --- /dev/null +++ b/src/pages/support/_components/SupportForm.tsx @@ -0,0 +1,44 @@ +import styled from 'styled-components'; +import { colors } from '@tidb-community/ui'; + +export const SupportContainer = styled.div` + margin-top: 24px; + margin-left: auto; + margin-right: auto; + max-width: 759px; + background: #ffffff; + border: 1px solid #ececec; + border-radius: 12px; + padding: 48px 76px; + position: relative; +`; + +export const SupportForm = styled('form')` + margin-top: 24px; + margin-left: auto; + margin-right: auto; + max-width: 759px; + background: #ffffff; + border: 1px solid #ececec; + border-radius: 12px; + padding: 48px 76px; + position: relative; +`; + +export const SupportFormDescription = styled('p')` + font-size: 16px; + line-height: 22.4px; + text-align: center; +`; + +export const SupportFormRequiredLabel = styled('span')` + &:before { + display: inline-block; + margin-right: 4px; + color: ${colors.AntD.error}; + font-size: 14px; + font-family: 'SimSun,sans-serif'; + line-height: 1; + content: '*'; + } +`; diff --git a/src/pages/support/_components/SupportFormContent.tsx b/src/pages/support/_components/SupportFormContent.tsx new file mode 100644 index 000000000..b08a06314 --- /dev/null +++ b/src/pages/support/_components/SupportFormContent.tsx @@ -0,0 +1,217 @@ +import { api } from '@tidb-community/datasource'; +import { withVerifyCode } from '@tidb-community/ui'; +import { Button, Checkbox, Col, Input, message, Result, Row } from 'antd'; +import { Formik } from 'formik'; +import { FormItem } from 'formik-antd'; +import React, { useState } from 'react'; +import * as Yup from 'yup'; +import { + SupportContainer, + SupportForm, + SupportFormDescription, + SupportFormRequiredLabel, +} from '~/pages/support/_components/SupportForm'; +import { form as formUtils } from '~/utils'; + +type UnauthorizedFormValues = { + name: string; + company: string; + consult_detail: string; + phone: string; + phone_code: string; +}; + +const INITIAL_VALUES: UnauthorizedFormValues = { name: '', company: '', consult_detail: '', phone: '', phone_code: '' }; + +const fields = { + name: { + placeholder: '输入您的姓名', + name: 'name', + }, + company: { + placeholder: '请输入您的公司名', + name: 'company', + }, + consult_detail: { + placeholder: '请添加描述', + name: 'consult_detail', + }, + phone: { + placeholder: '请输入手机号', + name: 'phone', + }, + phone_code: { + placeholder: '验证码', + name: 'phone_code', + }, +}; + +const unauthorizedSchema = Yup.object({ + phone: Yup.string().required('请输入手机号'), + phone_code: Yup.string().required('请输入验证码'), + name: Yup.string().min(2, '请输入有效姓名').required('未填写姓名'), + company: Yup.string().min(2, '请输入有效公司名').required('未填写公司'), + consult_detail: Yup.string(), +}); + +const authorizedSchema = Yup.object({ + name: Yup.string().min(2, '请输入有效姓名').required('未填写姓名'), + company: Yup.string().min(2, '请输入有效公司名').required('未填写公司'), + consult_detail: Yup.string(), +}); + +const VerifyCodeInput = withVerifyCode(Input); + +export function SupportFormContent({ authorized }: { authorized: boolean }) { + const [agree, setAgree] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [succeed, setSucceed] = useState(false); + + const onSubmit = formUtils.wrapFormikSubmitFunction((values) => { + setIsSubmitting(true); + + return api.support + .submitSupportForm({ + ...values, + }) + .then(() => { + message.success('提交成功'); + setSucceed(true); + }) + .finally(() => { + setIsSubmitting(false); + }); + }); + + if (succeed) { + return ( + + + 提交成功!我们将在 1 个工作日内 和您联系,为您提供所需的商业支持和咨询服务。感谢您对 TiDB + 的信任与支持,期待与您的进一步交流。 + + } + /> + + ); + } + + return ( + + {(formik) => ( + + + 为了更好地为您提供商业支持和个性化服务,请填写以下信息。我们承诺保护您的隐私,并仅将信息用于提供服务,我们将在 + 1 个工作日内和您联系。 + + + {!authorized && ( + + + + 手机号} + labelCol={{ xs: 24 }} + labelAlign={'left'} + > + + + + +  } + colon={false} + labelCol={{ xs: 24 }} + labelAlign={'left'} + > + api.support.sendPhoneCode({ phone: formik.values.phone })} + limitSeconds={60} + buttonDisabled={!formik.values.phone} + sendVerifyCodeBtnText="发送" + initialLimited={false} + countDownFormatter={(a) => `${a}`} + disabled={isSubmitting} + /> + + + + + )} + + 姓名} + labelCol={{ xs: 24 }} + labelAlign={'left'} + > + + + + + 公司} + labelAlign={'left'} + > + + + + + 咨询事宜(非必填)} + labelAlign={'left'} + > + + + + + + setAgree(ev.target.checked)} + disabled={isSubmitting} + > + 我已阅读并同意{' '} + + 《隐私协议》 + + + + )} + + ); +} diff --git a/src/pages/support/_components/SupportHeading.tsx b/src/pages/support/_components/SupportHeading.tsx new file mode 100644 index 000000000..dd81238ca --- /dev/null +++ b/src/pages/support/_components/SupportHeading.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components'; + +export const SupportHeading = styled('div')` + text-align: center; + margin-top: 24px; +`; + +export const SupportHeadingTitle = styled('h1')` + font-weight: 600; + font-size: 36px; + line-height: 50.4px; + color: #000000; +`; + +export const SupportHeadingHeadline = styled('p')` + margin-top: 16px; + font-size: 16px; + line-height: 24px; + font-weight: 400; +`; diff --git a/src/pages/support/_components/decorate.png b/src/pages/support/_components/decorate.png new file mode 100644 index 000000000..f2939cda1 Binary files /dev/null and b/src/pages/support/_components/decorate.png differ diff --git a/src/pages/support/index.page.tsx b/src/pages/support/index.page.tsx new file mode 100644 index 000000000..63a422d27 --- /dev/null +++ b/src/pages/support/index.page.tsx @@ -0,0 +1,52 @@ +import { Spin } from 'antd'; +import React, { useContext } from 'react'; +import { CommunityHead } from '~/components'; +import Container from '~/components/Container'; +import { MeContext } from '~/context'; +import { CoreLayout } from '~/layouts'; +import { SupportFormContent } from '~/pages/support/_components/SupportFormContent'; +import { + SupportHeading, + SupportHeadingHeadline, + SupportHeadingTitle, +} from '~/pages/support/_components/SupportHeading'; +import DecorateImg from './_components/decorate.png'; + +function SupportPage() { + const { isMeValidating, meData } = useContext(MeContext); + + const isLoadingMeData = !meData && isMeValidating; + + return ( + + + + + 商业支持咨询 + + 想要了解规划/实施/主动式巡检/故障协查/数据库迁移/重要时期保障等服务? +
+ 想要获得商业专家产品最高响应级别 7*24 支持服务? +
+
+ + + + 装饰图片 +
+
+ ); +} + +export default SupportPage; diff --git a/src/redux/index.js b/src/redux/index.js deleted file mode 100644 index 0ba83eb0e..000000000 --- a/src/redux/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit'; - -export const store = configureStore({ - reducer: {}, -}); - -export {}; diff --git a/src/utils/index.js b/src/utils/index.js index 07eb253af..b5d91f320 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -27,5 +27,4 @@ export * as errors from './errors.utils'; export * as form from './form.utils'; export * as link from './link.utils'; export * as redDots from './redDots.utils'; -export * as redux from './redux.utils'; export * as cdn from './cdn.utils'; diff --git a/src/utils/redux.utils.js b/src/utils/redux.utils.js deleted file mode 100644 index 95757ac09..000000000 --- a/src/utils/redux.utils.js +++ /dev/null @@ -1,9 +0,0 @@ -import { capitalize } from './common.utils'; - -export const createSetters = (keys = []) => - keys.reduce((acc, key) => { - acc[`set${capitalize(key)}`] = (state, action) => { - state[key] = action.payload; - }; - return acc; - }, {});