diff --git a/package-lock.json b/package-lock.json index 07f5d04..68e6db0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "ocenka-web", "version": "0.1.0", "dependencies": { + "@maskito/core": "^3.9.1", + "@maskito/phone": "^3.9.1", + "@maskito/react": "^3.9.1", "next": "15.3.4", "nodemailer": "^7.0.3", "react": "^19.0.0", @@ -15,7 +18,6 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", - "@iconify-icon/react": "^3.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -282,29 +284,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@iconify-icon/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@iconify-icon/react/-/react-3.0.0.tgz", - "integrity": "sha512-TOyzGUIfOFbmRQflMbf8k8bBGbeVAVCnvJ/rKz2SgBOV6VZLv7E9gzLvkPzfkZ2HZL+GbRtLNX/GJq2EI54OFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconify-icon": "^3.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/cyberalien" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "dev": true, - "license": "MIT" - }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.34.2", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", @@ -701,6 +680,44 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@maskito/core": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.9.1.tgz", + "integrity": "sha512-Sa3GFdgWZKRBLAVrrHbT0uI6fI8zMy2/yPvBuhxG5W4a+uYRCb9mpC52yZaUcVa7Gq4OvFIy03c53LntuXStmw==", + "license": "Apache-2.0" + }, + "node_modules/@maskito/kit": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.9.1.tgz", + "integrity": "sha512-r9kR4mwKz7jILUknHYpQPMBy7sXsptoOivt8S8ymf7yzTlJpfhfS+C2WiAKR24UyilhKBezC5kFR3nljKnHBCw==", + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "@maskito/core": "^3.9.1" + } + }, + "node_modules/@maskito/phone": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.9.1.tgz", + "integrity": "sha512-a/pefAll5iwhdB/o/t/h+LJfUG94dt6JsQyhC7owxAzGicDq3O6qZ/fApsdNWfI7VvBc846Hd0IEb4ds5tNX/g==", + "license": "Apache-2.0", + "peerDependencies": { + "@maskito/core": "^3.9.1", + "@maskito/kit": "^3.9.1", + "libphonenumber-js": ">=1.0.0" + } + }, + "node_modules/@maskito/react": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@maskito/react/-/react-3.9.1.tgz", + "integrity": "sha512-DF125ifk8X8cye9zsPvJScaPo9I/hhVCYcIC2UrBdS3BtVgxk+/tPeA5qQWUHi7xGKNDDniYMFXqVlZaofWw4w==", + "license": "Apache-2.0", + "peerDependencies": { + "@maskito/core": "^3.9.1", + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", @@ -3468,19 +3485,6 @@ "url": "https://github.com/sponsors/typicode" } }, - "node_modules/iconify-icon": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-3.0.0.tgz", - "integrity": "sha512-yPcnpkn8HUEUckrxxJBOer3jbGv3bqozHsLMLBRxhk3As1X76BgV2mS2a1HTNOIagR8nUs30H3qAd9GLe8Mnlg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@iconify/types": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/cyberalien" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4120,6 +4124,13 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", + "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==", + "license": "MIT", + "peer": true + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", diff --git a/package.json b/package.json index bdb7904..bae9d84 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "prepare": "husky" }, "dependencies": { + "@maskito/core": "^3.9.1", + "@maskito/phone": "^3.9.1", + "@maskito/react": "^3.9.1", "next": "15.3.4", "nodemailer": "^7.0.3", "react": "^19.0.0", diff --git a/public/images/em-rounded-logo.png b/public/images/em-rounded-logo.png new file mode 100644 index 0000000..e25a70d Binary files /dev/null and b/public/images/em-rounded-logo.png differ diff --git a/public/images/step1.png b/public/images/step1.png new file mode 100644 index 0000000..6f84a8d Binary files /dev/null and b/public/images/step1.png differ diff --git a/public/images/step2.png b/public/images/step2.png new file mode 100644 index 0000000..462c61e Binary files /dev/null and b/public/images/step2.png differ diff --git a/public/images/step3.png b/public/images/step3.png new file mode 100644 index 0000000..7e873d1 Binary files /dev/null and b/public/images/step3.png differ diff --git a/public/images/step4.png b/public/images/step4.png new file mode 100644 index 0000000..26492e7 Binary files /dev/null and b/public/images/step4.png differ diff --git a/public/images/tg-rounded-logo.png b/public/images/tg-rounded-logo.png new file mode 100644 index 0000000..711b983 Binary files /dev/null and b/public/images/tg-rounded-logo.png differ diff --git a/public/images/wa-rounded-logo.png b/public/images/wa-rounded-logo.png new file mode 100644 index 0000000..97823c1 Binary files /dev/null and b/public/images/wa-rounded-logo.png differ diff --git a/src/core/styles/variables.scss b/src/core/styles/variables.scss index 123cb88..c5ac3f3 100644 --- a/src/core/styles/variables.scss +++ b/src/core/styles/variables.scss @@ -23,6 +23,7 @@ $color-lightgray: #E4E1E1; $color-darkgray: #999999; $color-text: #333333; $color-text-light: #222222; +$color-green: #23A455; $color-link: #333333; $color-link-hover: #009283; $color-error: #ff0000; diff --git a/src/entities/callback-form/index.ts b/src/entities/callback-form/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/entities/callback-form/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/entities/callback-form/styles.module.scss b/src/entities/callback-form/styles.module.scss new file mode 100644 index 0000000..aeb8818 --- /dev/null +++ b/src/entities/callback-form/styles.module.scss @@ -0,0 +1,26 @@ +.Container { + background: $color-green; + width: 100%; + border-radius: rem(16px); + padding: rem(30px) rem(20px); + margin-bottom: 20px; + display: flex; + flex-direction: column; +} + + +.Title { + font-family: $font-roboto; + font-weight: 500; + font-size: 20px; + line-height: 130%; + color: $color-white; + align-self: center; + margin-bottom: 30px; +} + +.Form { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 40px; +} \ No newline at end of file diff --git a/src/entities/callback-form/ui.tsx b/src/entities/callback-form/ui.tsx new file mode 100644 index 0000000..5e2f392 --- /dev/null +++ b/src/entities/callback-form/ui.tsx @@ -0,0 +1,27 @@ +import s from './styles.module.scss'; +import { Button, Input, PhoneInput } from '@shared/ui'; + +type CallbackFormProps = { + pageName: string; +}; + +function CallbackForm({ pageName }: CallbackFormProps) { + return ( +
+

Узнать точную стоимость и срок экспертизы

+
+ + + + +
+ ); +} + +export { CallbackForm }; diff --git a/src/entities/connect/index.ts b/src/entities/connect/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/entities/connect/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/entities/connect/style.module.scss b/src/entities/connect/style.module.scss new file mode 100644 index 0000000..ddf8895 --- /dev/null +++ b/src/entities/connect/style.module.scss @@ -0,0 +1,61 @@ +.Container { + display: grid; + grid-template-columns: auto 200px; + gap: 100px; +} + +.Icons { + display: flex; + flex-direction: row; + justify-content: space-around; + + .Icon { + display: flex; + flex-direction: column; + align-items: center; + + .Image { + margin-bottom: 16px; + } + + .Description { + font-family: $font-roboto; + font-weight: 400; + font-size: 16px; + line-height: 130%; + color: $color-green; + text-transform: uppercase; + } + } +} + +.CallOrder { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 16px; + + .Title { + font-family: $font-roboto; + font-weight: 400; + font-size: 20px; + line-height: 100%; + color: $color-text; + } + + .Btn { + border-width: 1px 1px 1px 1px; + border-color: $color-green; + border-radius: 15px 15px 15px 15px; + box-shadow: 2px 2px 5px 0px $color-green; + } + + .Description { + font-family: $font-roboto; + font-weight: 300; + font-size: 14px; + line-height: 100%; + color: $color-text; + } +} \ No newline at end of file diff --git a/src/entities/connect/ui.tsx b/src/entities/connect/ui.tsx new file mode 100644 index 0000000..8c419f0 --- /dev/null +++ b/src/entities/connect/ui.tsx @@ -0,0 +1,36 @@ +import s from './style.module.scss'; +import { Button } from '@shared/ui'; +import Image from 'next/image'; +import emailImg from '@public/images/em-rounded-logo.png'; +import tgImg from '@public/images/tg-rounded-logo.png'; +import waImg from '@public/images/wa-rounded-logo.png'; + +function Connect() { + return ( +
+
+
+ {''} +

WHATSAPP

+
+
+ {''} +

TELEGRAM

+
+
+ {''} +

EMAIL

+
+
+
+

Звоните по телефону

+ +

мы работаем с 08:00 до 17:00

+
+
+ ); +} + +export { Connect }; diff --git a/src/entities/index.ts b/src/entities/index.ts index 181c0b2..30029b8 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,2 +1,4 @@ -export { TopMenu } from './top-menu'; -export { BaseMenu } from './base-menu'; +export * from './top-menu'; +export * from './base-menu'; +export * from './callback-form'; +export * from './connect'; diff --git a/src/shared/ui/button/index.ts b/src/shared/ui/button/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/shared/ui/button/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/shared/ui/button/styles.module.scss b/src/shared/ui/button/styles.module.scss new file mode 100644 index 0000000..12a4e0c --- /dev/null +++ b/src/shared/ui/button/styles.module.scss @@ -0,0 +1,105 @@ +.Button { + display: flex; + align-items: center; + justify-content: center; + padding: rem(8px) rem(10px); + border-radius: rem(16px); + min-height: rem(40px); + + font-family: $font-roboto; + font-weight: $font-regular; + font-size: rem(16px); + line-height: 100%; + + transition: all 0.15s linear; + white-space: nowrap; + width: max-content; + + @include ifdesktop{ + font-size: rem(20px); + border-radius: rem(16px); + padding: rem(4px) rem(24px); + min-height: rem(48px); + } + + &_fullWidth { + width: 100%; + } + + svg { + width: rem(18px); + height: rem(18px); + //fill: var(--text-primary); + + margin-right: rem(18px); + } + + &:hover { + cursor: pointer; + box-shadow: 1px 1px 1px 0px $color-darkgray; + } + + &:active { + box-shadow: inset 1px 1px 2px 0px $color-darkgray; + } + + &:hover svg { + fill: var(--white); + } + + &_default { + background: $color-green; + color: $color-white; + } + + &_green { + background: $color-green; + color: $color-white; + } + + &_ghost { + background: transparent; + color: $color-white; + border: 1px solid $color-white; + + &:hover { + cursor: pointer; + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:active { + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:hover svg { + fill: var(--white); + } + } + + &_white { + background: $color-white; + color: $color-green; + border: 1px solid $color-green; + + &:hover { + cursor: pointer; + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:active { + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:hover svg { + fill: var(--white); + } + } + + &_disabled { + cursor: not-allowed; + } +} \ No newline at end of file diff --git a/src/shared/ui/button/ui.tsx b/src/shared/ui/button/ui.tsx new file mode 100644 index 0000000..ffa8844 --- /dev/null +++ b/src/shared/ui/button/ui.tsx @@ -0,0 +1,53 @@ +import s from './styles.module.scss'; +import { + ButtonHTMLAttributes, + DetailedHTMLProps, + FunctionComponent, + ReactNode, + SVGProps, +} from 'react'; +import { clsx } from 'clsx'; + +type ButtonProps = { + className?: string; + children?: ReactNode; + disabled?: boolean; + Icon?: FunctionComponent>; + onClick?: () => void; + variant?: 'default' | 'green' | 'ghost' | 'white'; + fullWidth?: boolean; +} & DetailedHTMLProps< + ButtonHTMLAttributes, + HTMLButtonElement +>; + +function Button({ + className, + children, + onClick, + Icon, + disabled, + variant = 'default', + fullWidth = false, + ...props +}: ButtonProps) { + return ( + + ); +} + +export { Button }; diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts new file mode 100644 index 0000000..8b6fb1a --- /dev/null +++ b/src/shared/ui/index.ts @@ -0,0 +1,3 @@ +export * from './input'; +export * from './phone-input'; +export * from './button'; diff --git a/src/shared/ui/input/index.ts b/src/shared/ui/input/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/shared/ui/input/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/shared/ui/input/styles.module.scss b/src/shared/ui/input/styles.module.scss new file mode 100644 index 0000000..6f9017b --- /dev/null +++ b/src/shared/ui/input/styles.module.scss @@ -0,0 +1,98 @@ +.Container { + position: relative; + display: block; +} + +.Input { + display: flex; + background: $color-white; + border: 1px solid $color-darkgray; + border-radius: rem(16px); + padding: rem(4px) rem(10px); + transition: border ease .5s; + + font-family: $font-roboto; + font-weight: $font-regular; + font-size: rem(16px); + line-height: 100%; + color: $color-text; + width: max-content; + + @include iftablet { + font-size: rem(16px); + } + + @include iflaptop { + font-size: rem(18px); + padding: rem(10px) rem(16px); + } + + @include ifdesktop { + font-size: rem(20px); + } + + &:focus { + border-color: $color-green; + transition: border-color ease .5s; + } + + &:hover { + border-color: $color-text; + transition: border-color ease .5s; + } + + &:focus:hover { + border-color: $color-green; + transition: border-color ease .5s; + } + + &_error { + border-color: $color-error; + } + + &_fullWidth { + width: 100%; + } + + &_ghost { + background: transparent; + color: $color-white; + border: 1px solid $color-white; + + &:focus { + //border-color: $color-orange; + border-color: $color-white; + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:hover { + border-color: $color-white; + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &:focus:hover { + //border-color: $color-orange; + border-color: $color-white; + box-shadow: 0 0 0 0.2rem rgb(76 175 80 / 50%); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + &::placeholder { + color: $color-white; + } + } +} + +.Error { + position: absolute; + z-index: 2; + left: rem(8px); + bottom: rem(-16px); + font-family: $font-roboto; + font-weight: $font-light; + font-size: rem(12px); + line-height: 100%; + color: $color-error; +} \ No newline at end of file diff --git a/src/shared/ui/input/ui.tsx b/src/shared/ui/input/ui.tsx new file mode 100644 index 0000000..b471341 --- /dev/null +++ b/src/shared/ui/input/ui.tsx @@ -0,0 +1,50 @@ +'use client'; + +import s from './styles.module.scss'; + +import { DetailedHTMLProps, forwardRef, InputHTMLAttributes, Ref } from 'react'; +import { clsx } from 'clsx'; + +type InputProps = { + wrapperClassName?: string; + className?: string; + fullWidth?: boolean; + variant?: 'default' | 'ghost'; + error?: string | boolean; + errorTextColor?: string; +} & DetailedHTMLProps, HTMLInputElement>; + +const Input = forwardRef(function Input( + { + className, + fullWidth = false, + variant = 'default', + error = false, + errorTextColor, + ...props + }: InputProps, + ref: Ref, +) { + return ( +
+ + {error && ( + + {error} + + )} +
+ ); +}); + +export { Input }; diff --git a/src/shared/ui/phone-input/index.ts b/src/shared/ui/phone-input/index.ts new file mode 100644 index 0000000..5ecdd1f --- /dev/null +++ b/src/shared/ui/phone-input/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/src/shared/ui/phone-input/ui.tsx b/src/shared/ui/phone-input/ui.tsx new file mode 100644 index 0000000..ef92ef6 --- /dev/null +++ b/src/shared/ui/phone-input/ui.tsx @@ -0,0 +1,27 @@ +'use client'; + +import { useMaskito } from '@maskito/react'; +import { maskitoPhoneOptionsGenerator } from '@maskito/phone'; +import metadata from 'libphonenumber-js/min/metadata'; +import { DetailedHTMLProps, InputHTMLAttributes } from 'react'; +import { Input } from '@shared/ui'; + +type PhoneInput = { + className?: string; + variant?: 'default' | 'ghost'; + error?: string | boolean; + errorTextColor?: string; + fullWidth?: boolean; +} & DetailedHTMLProps, HTMLInputElement>; + +function PhoneInput({ ...props }: PhoneInput) { + const options = maskitoPhoneOptionsGenerator({ + countryIsoCode: 'RU', + metadata, + }); + const maskedInputRef = useMaskito({ options }); + + return ; +} + +export { PhoneInput }; diff --git a/src/views/expertise/autotech/styles.module.scss b/src/views/expertise/autotech/styles.module.scss index 9edf554..86962f5 100644 --- a/src/views/expertise/autotech/styles.module.scss +++ b/src/views/expertise/autotech/styles.module.scss @@ -45,4 +45,4 @@ color: $color-text; margin-bottom: 16px; } -} \ No newline at end of file +} diff --git a/src/views/expertise/autotech/ui.tsx b/src/views/expertise/autotech/ui.tsx index b3d82d6..2b29362 100644 --- a/src/views/expertise/autotech/ui.tsx +++ b/src/views/expertise/autotech/ui.tsx @@ -1,4 +1,5 @@ import s from './styles.module.scss'; +import { CallbackForm, Connect } from '@/entities'; function AutoTech() { return ( @@ -48,7 +49,7 @@ function AutoTech() { изменен. - WIDGET +

Документы, необходимые для экспертизы:

Для начала работ по автомобильной экспертизе эксперту понадобятся @@ -74,7 +75,7 @@ function AutoTech() { Для того, чтобы заказать выполнение автотехнической экспертизы, вы можете воспользоваться любым удобным способом.

- WIDGET +
sidebar