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 (
+
+
+
+
Звоните по телефону
+
+ +7 (900) 241-34-34
+
+
мы работаем с 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 (
+
+ {Icon && }
+ {children}
+
+ );
+}
+
+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