This commit is contained in:
Ivan Zinchenko 2026-04-15 18:57:24 +03:00
commit 4942dbe198
93 changed files with 4523 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
# Local
.DS_Store
*.local
*.log*
# Dist
node_modules
dist/
# Profile
.rspack-profile-*/
# IDE
.vscode/*
!.vscode/extensions.json
.idea

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
# Lock files
package-lock.json
pnpm-lock.yaml
yarn.lock

3
.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

20
AGENTS.md Normal file
View File

@ -0,0 +1,20 @@
# AGENTS.md
You are an expert in JavaScript, Rsbuild, and web application development. You write maintainable, performant, and accessible code.
## Commands
- `pnpm run dev` - Start the dev server
- `pnpm run build` - Build the app for production
- `pnpm run preview` - Preview the production build locally
## Docs
- Rsbuild: https://rsbuild.rs/llms.txt
- Rspack: https://rspack.rs/llms.txt
## Tools
### Prettier
- Run `pnpm run format` to format your code

10
Caddyfile Normal file
View File

@ -0,0 +1,10 @@
:80 {
root * /srv
encode zstd gzip
try_files {path} /index.html
@html path /index.html
header @html Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
file_server
}

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM node:20-alpine AS build
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM caddy:2.8-alpine AS runtime
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srv
EXPOSE 80

36
README.md Normal file
View File

@ -0,0 +1,36 @@
# Rsbuild project
## Setup
Install the dependencies:
```bash
pnpm install
```
## Get started
Start the dev server, and the app will be available at [http://localhost:3000](http://localhost:3000).
```bash
pnpm run dev
```
Build the app for production:
```bash
pnpm run build
```
Preview the production build locally:
```bash
pnpm run preview
```
## Learn more
To learn more about Rsbuild, check out the following resources:
- [Rsbuild documentation](https://rsbuild.rs) - explore Rsbuild features and APIs.
- [Rsbuild GitHub repository](https://github.com/web-infra-dev/rsbuild) - your feedback and contributions are welcome!

34
package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "mts-public",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rsbuild build",
"dev": "rsbuild dev --open",
"format": "prettier --write .",
"preview": "rsbuild preview"
},
"dependencies": {
"@chakra-ui/react": "^3.34.0",
"@emotion/react": "^11.14.0",
"mobx": "^6.15.0",
"mobx-react-lite": "^4.1.1",
"next-themes": "^0.4.6",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-hook-form": "^7.72.1",
"react-icons": "^5.6.0",
"react-router": "^7.14.1",
"use-mask-input": "^3.9.0"
},
"devDependencies": {
"@rsbuild/core": "^1.7.1",
"@rsbuild/plugin-react": "^1.4.2",
"@rsbuild/plugin-type-check": "^1.3.4",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"prettier": "^3.7.3",
"typescript": "^5.9.3"
}
}

2434
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

1
public/favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="M48 0H0v48h48V0Z" fill="#FF0032"/><path d="M31.975 3.691v2.21h4.83v8.865h2.7V5.9h4.82V3.691h-12.35ZM14.609 3.692l-3.534 7.324L7.55 3.692H3.69v11.074h2.71V6.843l3.446 6.696h2.465l3.446-6.696v7.923h2.7V3.692h-3.849ZM41.628 40.617c-.07.442-.246.805-.53 1.08-.266.255-.629.442-1.08.55-.442.098-1.1.157-1.787.157-.717 0-1.346-.089-1.886-.275a2.314 2.314 0 0 1-1.217-.923c-.294-.432-.442-1.06-.442-1.875V38.2c0-.804.148-1.433.442-1.874a2.352 2.352 0 0 1 1.218-.923c.53-.187 1.168-.275 1.885-.275.687 0 1.345.059 1.786.157.452.108.815.285 1.08.55.285.275.462.638.53 1.08h2.7c-.078-.913-.382-1.69-.922-2.317-.491-.57-1.178-.982-2.033-1.237-.824-.246-1.914-.374-3.141-.374-1.296 0-2.416.187-3.339.55a4.284 4.284 0 0 0-2.16 1.846c-.48.815-.726 1.914-.746 3.25v.127l.148.01-.148.01v.128c.02 1.335.265 2.434.747 3.25a4.284 4.284 0 0 0 2.16 1.845c.922.363 2.042.55 3.338.55 1.227 0 2.317-.128 3.141-.373.855-.255 1.532-.668 2.033-1.237.54-.619.854-1.404.922-2.317h-2.7v-.01Z" fill="#fff"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h48v48H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

14
rsbuild.config.ts Normal file
View File

@ -0,0 +1,14 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
// Docs: https://rsbuild.rs/config/
export default defineConfig({
plugins: [
pluginReact(),
// pluginTypeCheck()
],
html: {
title: 'МТС Бизнес',
},
});

25
src/App.tsx Normal file
View File

@ -0,0 +1,25 @@
import { BrowserRouter, Route, Routes } from 'react-router';
import AppLayout from './components/layouts/AppLayout';
import CartPage from './pages/CartPage';
import IndexPage from './pages/IndexPage';
import NotFoundPage from './pages/NotFoundPage';
import OrderReturnPage from './pages/OrderReturnPage';
import PrivacyPage from './pages/PrivacyPage';
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route element={<AppLayout />}>
<Route index element={<IndexPage />} />
<Route path="/cart" element={<CartPage />} />
<Route path="/order/return" element={<OrderReturnPage />} />
<Route path="/privacy" element={<PrivacyPage />} />
<Route path="*" element={<NotFoundPage />} />
</Route>
</Routes>
</BrowserRouter>
);
};
export default App;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/images/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
src/assets/images/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
src/assets/images/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
src/assets/images/12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
src/assets/images/13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
src/assets/images/14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

1
src/assets/images/15.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 638 KiB

BIN
src/assets/images/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
src/assets/images/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
src/assets/images/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
src/assets/images/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
src/assets/images/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

9
src/assets/images/7.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 591 KiB

BIN
src/assets/images/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
src/assets/images/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 23.4004C7 22.4004 7.04999 21.6713 7.15285 21.1262C7.50751 18.5033 8.55999 15.2305 11.1226 10.5873C11.7576 9.43679 13.3672 9.51631 14.044 10.6405C14.1534 10.8223 14.2634 11.0007 14.3739 11.176C15.9419 7.95696 17.87 5.37781 19.9719 3.49845C21.4386 2.18697 23.4668 3.66414 24.0063 5.60218C24.7855 8.40115 26.559 10.7544 28.3269 13.1003C30.6683 16.207 33 19.3009 33 23.4004C33 30.55 28.0436 36.2025 21.4358 36.9225C21.0353 36.9745 20.5601 37 19.9998 37C12.6874 36.9999 7 31.0592 7 23.4004ZM23.0677 23.6267C24.0664 25.1501 24.875 26.3837 24.875 28.6418C24.875 31.4751 22.9085 33.6001 20.0614 33.6001C17.2143 33.6001 15.125 31.4751 15.125 28.6418C15.125 23.8799 17.5663 19.4939 20.0614 16.6001C20.8179 20.1946 22.0428 22.0633 23.0677 23.6267Z" fill="url(#paint0_linear_1167_3827)"/>
<defs>
<linearGradient id="paint0_linear_1167_3827" x1="35.0081" y1="3.00001" x2="7.35069" y2="5.88221" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M37 16.9746V21.3101C37 28.8799 37 32.6649 34.4128 34.0532C32.2031 35.239 29.3828 33.9915 24.3397 31.1756C23.0121 31.2407 21.5654 31.2791 20.0002 31.2791C17.3653 31.2791 15.0661 31.1701 13.1062 31.0089C9.66503 30.7256 7.94446 30.584 5.75263 28.4995C3.5608 26.415 3.42327 24.8518 3.14822 21.7252C3.05622 20.6795 3 19.5612 3 18.3896C3 17.218 3.05621 16.0997 3.1482 15.054C3.42325 11.9274 3.56077 10.3641 5.75264 8.27957C7.9445 6.19503 9.66515 6.05344 13.1065 5.77025C15.0663 5.60898 17.3654 5.5 20.0002 5.5C22.634 5.5 24.9325 5.6089 26.892 5.77008C30.3348 6.05328 32.0562 6.19487 34.2483 8.27978C36.4403 10.3647 36.5777 11.9288 36.8525 15.0571C36.9062 15.6691 36.9477 16.306 36.9729 16.9638L37 16.9746ZM23.4011 19.9999C24.34 19.9999 25.1011 19.2786 25.1011 18.3887C25.1011 17.4989 24.34 16.7775 23.4011 16.7775L16.6005 16.7775C15.6616 16.7775 14.9005 17.4989 14.9005 18.3887C14.9005 19.2786 15.6616 19.9999 16.6005 19.9999H23.4011Z" fill="url(#paint0_linear_1167_3831)"/>
<defs>
<linearGradient id="paint0_linear_1167_3831" x1="39.626" y1="5.50001" x2="3.97585" y2="11.1959" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,19 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6845 7.50383C15.0658 6.56539 16.4342 6.56539 16.8155 7.50383L19.7328 14.6831C19.8492 14.9696 20.0829 15.1964 20.3781 15.3094L27.7748 18.1408C28.7417 18.5109 28.7417 19.8391 27.7748 20.2092L20.3781 23.0406C20.0829 23.1536 19.8492 23.3804 19.7328 23.6669L16.8155 30.8462C16.4342 31.7846 15.0658 31.7846 14.6845 30.8462L11.7672 23.6669C11.6508 23.3804 11.4171 23.1536 11.1219 23.0406L3.72516 20.2092C2.75828 19.8391 2.75828 18.5109 3.72516 18.1408L11.1219 15.3094C11.4171 15.1964 11.6508 14.9696 11.7672 14.6831L14.6845 7.50383Z" fill="url(#paint0_linear_1167_3836)"/>
<path d="M27.1722 3.81562C27.3432 3.39479 27.9568 3.39479 28.1278 3.81562L29.0209 6.01354C29.0731 6.14202 29.1779 6.24372 29.3103 6.29439L31.5748 7.16124C32.0084 7.32721 32.0084 7.92279 31.5748 8.08876L29.3103 8.95561C29.1779 9.00628 29.0731 9.10798 29.0209 9.23646L28.1278 11.4344C27.9568 11.8552 27.3432 11.8552 27.1722 11.4344L26.2791 9.23646C26.2269 9.10798 26.1221 9.00628 25.9897 8.95561L23.7252 8.08876C23.2916 7.92279 23.2916 7.32721 23.7252 7.16124L25.9897 6.29439C26.1221 6.24372 26.2269 6.14202 26.2791 6.01354L27.1722 3.81562Z" fill="url(#paint1_linear_1167_3836)"/>
<path d="M30.3811 25.3919C30.6205 24.8027 31.4795 24.8027 31.7189 25.3919L32.9693 28.469C33.0424 28.6488 33.1891 28.7912 33.3744 28.8622L36.5447 30.0757C37.1518 30.3081 37.1518 31.1419 36.5447 31.3743L33.3744 32.5878C33.1891 32.6588 33.0424 32.8012 32.9693 32.981L31.7189 36.0581C31.4795 36.6473 30.6205 36.6473 30.3811 36.0581L29.1307 32.981C29.0576 32.8012 28.9109 32.6588 28.7256 32.5878L25.5553 31.3743C24.9482 31.1419 24.9482 30.3081 25.5553 30.0757L28.7256 28.8622C28.9109 28.7912 29.0576 28.6488 29.1307 28.469L30.3811 25.3919Z" fill="url(#paint2_linear_1167_3836)"/>
<defs>
<linearGradient id="paint0_linear_1167_3836" x1="39.626" y1="3.50001" x2="3.77262" y2="8.53401" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3836" x1="39.626" y1="3.50001" x2="3.77262" y2="8.53401" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint2_linear_1167_3836" x1="39.626" y1="3.50001" x2="3.77262" y2="8.53401" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,14 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.1111 5.5C25.657 5.5 27.7136 5.779 29.2729 6.11808C30.7017 6.42876 31.4161 6.5841 32.299 7.58196C33.1819 8.57982 33.2844 9.46229 33.4894 11.2272C33.75 13.4699 34 16.527 34 20C34 23.473 33.75 26.5301 33.4894 28.7728C33.2844 30.5377 33.1819 31.4201 32.299 32.4179C31.4162 33.4158 30.7018 33.5711 29.2731 33.8818C27.7138 34.221 25.6571 34.5 23.1111 34.5C22.966 34.5 22.8224 34.4991 22.6805 34.4973C23.2467 33.7643 23.6428 32.9802 23.9011 32.0867C24.1334 31.2833 24.2364 30.4234 24.3122 29.6667H27.7777C28.6368 29.6667 29.3333 28.9453 29.3333 28.0556C29.3333 27.1658 28.6368 26.4444 27.7777 26.4444H24.5633C24.6272 25.2405 24.6667 23.8975 24.6667 22.4167C24.6667 19.688 24.5327 17.4274 24.3586 15.6422L24.3444 15.4953C24.2642 14.6655 24.1678 13.6688 23.9012 12.7467C23.5691 11.5978 23.0093 10.6299 22.153 9.71873C21.3354 8.84873 20.4618 8.23531 19.3929 7.85201C18.5342 7.54411 17.6215 7.42381 16.9081 7.32978L16.7748 7.31216C16.0398 7.21444 15.2389 7.14322 14.3879 7.1196C15.0858 6.52313 15.7774 6.37279 16.9498 6.11792C18.5092 5.77892 20.5657 5.5 23.1111 5.5Z" fill="url(#paint0_linear_1167_3837)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.5555 22.4167C21.5555 25.0364 21.4268 27.1893 21.2631 28.8683C21.0764 30.7822 20.9831 31.7391 19.9237 32.8666C18.8642 33.9941 18.0363 34.1044 16.3805 34.3248C15.5866 34.4305 14.7101 34.5 13.7778 34.5C12.8454 34.5 11.9688 34.4305 11.175 34.3248C9.51921 34.1044 8.69131 33.9941 7.63188 32.8666C6.57245 31.7391 6.47912 30.7822 6.29246 28.8684C6.1287 27.1894 6 25.0365 6 22.4167C6 19.7974 6.12865 17.6447 6.29235 15.9658C6.47904 14.0511 6.57238 13.0938 7.63206 11.9662C8.69174 10.8386 9.5201 10.7284 11.1768 10.5082C11.9702 10.4027 12.8461 10.3333 13.7778 10.3333C14.7094 10.3333 15.5853 10.4027 16.3786 10.5082C18.0354 10.7284 18.8638 10.8386 19.9235 11.9662C20.9832 13.0938 21.0765 14.0512 21.2632 15.966C21.4269 17.6449 21.5555 19.7975 21.5555 22.4167ZM10.6667 15.1667C10.6667 14.2769 11.3631 13.5556 12.2222 13.5556H15.3333C16.1924 13.5556 16.8889 14.2769 16.8889 15.1667C16.8889 16.0565 16.1924 16.7778 15.3333 16.7778H12.2222C11.3631 16.7778 10.6667 16.0565 10.6667 15.1667Z" fill="url(#paint1_linear_1167_3837)"/>
<defs>
<linearGradient id="paint0_linear_1167_3837" x1="36.1626" y1="5.50001" x2="6.56658" y2="9.39415" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3837" x1="36.1626" y1="5.50001" x2="6.56658" y2="9.39415" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,24 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9983 3.5C16.3658 3.5 12.2222 9.76061 12.2222 16.7006C12.2222 19.2817 13.7778 28.25 13.7778 28.25H26.2222C26.2222 28.25 27.7778 18.8344 27.7778 16.7006C27.7778 9.76061 23.6337 3.5 19.9983 3.5ZM20 17.1125C21.0739 17.1125 21.9444 16.1891 21.9444 15.05C21.9444 13.9109 21.0739 12.9875 20 12.9875C18.9261 12.9875 18.0556 13.9109 18.0556 15.05C18.0556 16.1891 18.9261 17.1125 20 17.1125Z" fill="url(#paint0_linear_1167_3839)"/>
<path d="M29.3048 28.7086L29.2159 29.2472L32.1394 29.8674C32.5964 29.9644 33.0702 29.8389 33.4307 29.5255C33.7911 29.2121 34 28.7438 34 28.2495V24.1244C34 22.0471 33.0779 20.0909 31.5111 18.8444L30.7924 18.2727C30.6409 19.8607 30.3374 22.0083 30.0733 23.788C29.8765 25.1142 29.68 26.3706 29.533 27.2944C29.4594 27.7567 29.398 28.1366 29.3549 28.4015L29.3048 28.7086Z" fill="url(#paint1_linear_1167_3839)"/>
<path d="M9.92836 23.9667C9.65412 22.1581 9.34246 19.9614 9.19585 18.282L8.48889 18.8445C6.9221 20.0909 6 22.0471 6 24.1244V28.2495C6 28.7438 6.20895 29.2121 6.56935 29.5255C6.92975 29.8389 7.40364 29.9644 7.86062 29.8674L10.7874 29.2465L10.718 28.8466L10.6996 28.7397L10.6492 28.4445C10.6059 28.1899 10.5443 27.8246 10.4705 27.3784C10.323 26.4871 10.1259 25.2691 9.92836 23.9667Z" fill="url(#paint2_linear_1167_3839)"/>
<path d="M17.5437 31.5061C17.1945 31.0366 16.6815 30.725 16.122 30.725C14.8991 30.725 14.1074 32.0989 14.8329 33.1562C16.2439 35.2123 17.9787 36.5 19.6098 36.5C21.2421 36.5 22.9777 35.2124 24.3891 33.1564C25.115 32.0991 24.3232 30.725 23.1001 30.725C22.5407 30.725 22.0279 31.0365 21.6786 31.5058C20.636 32.9071 19.7939 33.1833 19.6098 33.1833C19.4276 33.1833 18.5862 32.9078 17.5437 31.5061Z" fill="url(#paint3_linear_1167_3839)"/>
<defs>
<linearGradient id="paint0_linear_1167_3839" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3839" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint2_linear_1167_3839" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint3_linear_1167_3839" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="9" viewBox="0 0 18 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.264899 2.95294C1.38479 -0.214388 5.34696 -1.02562 7.54602 1.46216L9 3.10704L10.454 1.46216C12.653 -1.02562 16.6152 -0.214389 17.7351 2.95294C18.0883 3.95187 18.0883 5.04813 17.7351 6.04706C16.6152 9.21439 12.653 10.0256 10.454 7.53784L9 5.89296L7.54601 7.53785C5.34696 10.0256 1.38479 9.21439 0.264899 6.04706C-0.0882995 5.04813 -0.0882997 3.95187 0.264899 2.95294ZM7.7687 4.5L6.20644 2.73262C4.92706 1.28527 2.62192 1.75724 1.97039 3.59994L1.11764 3.27644L1.97039 3.59994C1.7649 4.1811 1.7649 4.81889 1.97039 5.40006C2.62193 7.24276 4.92706 7.71473 6.20644 6.26738L7.7687 4.5ZM10.2313 4.5L11.7936 6.26738C13.0729 7.71473 15.3781 7.24276 16.0296 5.40006C16.2351 4.81889 16.2351 4.1811 16.0296 3.59994C15.3781 1.75724 13.0729 1.28527 11.7936 2.73263L10.2313 4.5Z" fill="#1D2023"/>
</svg>

After

Width:  |  Height:  |  Size: 933 B

View File

@ -0,0 +1,3 @@
<svg width="26" height="13" viewBox="0 0 26 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.382632 4.26536C2.00026 -0.309671 7.72338 -1.48146 10.8998 2.112L13 4.48794L15.1002 2.112C18.2766 -1.48146 23.9997 -0.309672 25.6174 4.26536C26.1275 5.70825 26.1275 7.29174 25.6174 8.73464C23.9997 13.3097 18.2766 14.4815 15.1002 10.888L13 8.51205L10.8998 10.888C7.72338 14.4815 2.00026 13.3097 0.382632 8.73464C-0.127544 7.29174 -0.127544 5.70825 0.382632 4.26536ZM11.2215 6.5L8.96486 3.94712C7.11686 1.8565 3.78722 2.53823 2.84611 5.19992L1.61437 4.73264L2.84611 5.19992C2.5493 6.03937 2.5493 6.96062 2.84611 7.80008C3.78723 10.4618 7.11686 11.1435 8.96485 9.05288L11.2215 6.5ZM14.7785 6.5L17.0351 9.05288C18.8831 11.1435 22.2128 10.4618 23.1539 7.80008C23.4507 6.96062 23.4507 6.03937 23.1539 5.19992C22.2128 2.53823 18.8831 1.8565 17.0351 3.94713L14.7785 6.5Z" fill="#1D2023"/>
</svg>

After

Width:  |  Height:  |  Size: 935 B

View File

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="#FF0032"/>
<path d="M21.319 2.43896V3.93186H24.5352V9.77255L24.5358 9.86871H26.3364V3.93186H29.5525V2.43896H21.319Z" fill="white"/>
<path d="M9.73653 2.46121L7.38432 7.34502L5.03208 2.46121H2.46387V9.84583H4.26437V4.56082L6.56359 9.02575H8.20509L10.5049 4.56082V9.84583H12.3054V2.46121H9.73653Z" fill="white"/>
<path d="M27.7475 27.076C27.7139 27.3487 27.5896 27.6022 27.3947 27.7959C27.1922 27.9801 26.9433 28.1057 26.6747 28.1591C26.2824 28.2368 25.8829 28.273 25.4829 28.2671C25.057 28.2747 24.6328 28.2126 24.227 28.0832C23.8943 27.9751 23.6074 27.7588 23.4121 27.4686C23.2158 27.178 23.1189 26.7585 23.1189 26.2251V25.4685C23.1189 24.9311 23.2178 24.5116 23.4121 24.2211C23.607 23.9315 23.8931 23.7154 24.225 23.6071C24.6308 23.4777 25.0551 23.4156 25.481 23.4232C25.8809 23.4172 26.2804 23.4534 26.6728 23.5312C26.9413 23.5847 27.1902 23.7103 27.3927 23.8944C27.5876 24.0881 27.7119 24.3416 27.7455 24.6143H29.5473C29.5147 24.0463 29.2981 23.5044 28.9301 23.0704C28.5672 22.6713 28.0975 22.3847 27.5767 22.2445C26.8935 22.0642 26.1886 21.9799 25.4823 21.9938C24.619 21.9938 23.8703 22.1182 23.257 22.3629C22.6503 22.5965 22.1421 23.031 21.8171 23.594C21.4997 24.1392 21.3328 24.867 21.3217 25.7591V25.8409V25.8448V25.9266C21.3328 26.8187 21.4997 27.5465 21.8171 28.0917C22.1421 28.6547 22.6503 29.0892 23.257 29.3228C23.8709 29.5676 24.6203 29.6919 25.4823 29.6919C26.1886 29.7061 26.8936 29.6218 27.5767 29.4412C28.0977 29.3015 28.5676 29.0148 28.9301 28.6152C29.2982 28.1814 29.5149 27.6394 29.5473 27.0713L27.7475 27.076Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 3.5C16.5861 3.5 12.7051 4.98303 9.89084 6.32576C8.06892 7.19503 7.15797 7.62967 6.53055 8.71947C5.90313 9.80928 5.95839 10.9127 6.06891 13.1196C6.38323 19.396 7.54535 25.5613 11.277 30.641C13.1236 33.1547 16.991 36.5 20 36.5C23.009 36.5 26.8764 33.1547 28.723 30.6411C32.4547 25.5613 33.6169 19.3959 33.9311 13.1194C34.0416 10.9127 34.0968 9.80927 33.4694 8.71948C32.842 7.6297 31.9311 7.19507 30.1092 6.32581C27.295 4.98307 23.414 3.5 20 3.5ZM27 15.8492C27 16.2649 26.8505 16.6806 26.5515 16.9977L19.5272 24.4743C19.24 24.7789 18.8506 24.95 18.4444 24.95C18.0383 24.95 17.6489 24.7789 17.3617 24.4743L14.2263 21.1485C13.9273 20.8313 13.7778 20.4157 13.7778 20C13.7778 19.5843 13.9273 19.1687 14.2263 18.8515C14.8243 18.2172 15.7938 18.2172 16.3918 18.8515L18.4444 21.0288L24.386 14.7007C24.685 14.3836 25.0769 14.225 25.4688 14.225C25.8606 14.225 26.2525 14.3836 26.5515 14.7007C26.8505 15.0179 27 15.4335 27 15.8492Z" fill="url(#paint0_linear_1167_3835)"/>
<defs>
<linearGradient id="paint0_linear_1167_3835" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,14 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.1072 16.6263V17.8786C16.1101 17.9193 16.1117 17.9604 16.1117 18.0018V19.1843L22.479 19.1843C22.7692 19.1843 23.0532 19.1793 23.3297 19.1701C25.2942 19.1047 26.2229 17.8958 26.2229 16.6263C26.2229 15.014 24.9905 13.5911 22.9003 13.5563C22.7614 13.554 22.6209 13.5528 22.479 13.5528L19.0747 13.5528C18.0053 13.5528 17.3788 13.555 16.916 13.5918C16.6805 13.6105 16.5662 13.6338 16.5261 13.6437C16.3843 13.72 16.2686 13.8398 16.1949 13.9867C16.1854 14.0282 16.1629 14.1466 16.1448 14.3905C16.1093 14.8698 16.1072 15.5187 16.1072 16.6263Z" fill="url(#paint0_linear_1167_3838)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.62262 8.21628C6.58037 10.3315 6.42894 12.4388 6.12608 16.6534C6.04757 17.746 6 18.873 6 20C6 21.127 6.04757 22.254 6.12608 23.3466C6.42894 27.5612 6.58037 29.6685 8.62262 31.7837C10.6649 33.8989 12.6995 34.0557 16.7688 34.3694C17.8237 34.4507 18.9119 34.5 20 34.5C21.0881 34.5 22.1763 34.4507 23.2312 34.3694C27.3005 34.0557 29.3351 33.8989 31.3774 31.7837C33.4196 29.6685 33.5711 27.5612 33.8739 23.3466C33.9524 22.254 34 21.127 34 20C34 18.873 33.9524 17.746 33.8739 16.6534C33.5711 12.4388 33.4196 10.3315 31.3774 8.21628C29.3351 6.1011 27.3005 5.94426 23.2312 5.63059C22.1763 5.54927 21.0881 5.5 20 5.5C18.9119 5.5 17.8237 5.54927 16.7688 5.63059C12.6995 5.94426 10.6649 6.1011 8.62262 8.21628ZM22.479 22.4066L16.1117 22.4066V24.8306L23.1089 24.8276C23.968 24.8276 24.6644 25.5489 24.6644 26.4387C24.6644 27.3285 23.968 28.0499 23.1089 28.0499L16.1117 28.0528V28.8554C16.1117 29.7452 15.4152 30.4665 14.5561 30.4665C13.697 30.4665 13.0005 29.7452 13.0005 28.8554V28.0528C12.1415 28.0528 11.4452 27.3315 11.4452 26.4417C11.4452 25.5519 12.1415 24.8306 13.0005 24.8306V22.4139C12.1414 22.4139 11.445 21.6926 11.445 20.8028C11.445 19.9146 12.139 19.1942 12.9961 19.1917V16.6263C12.9961 14.5107 12.9961 13.4529 13.3702 12.6346C13.7547 11.7935 14.4086 11.1163 15.2206 10.7181C16.0107 10.3306 17.0321 10.3306 19.0747 10.3306L22.479 10.3306C22.638 10.3306 22.7952 10.3319 22.9504 10.3345C26.466 10.3931 29.3341 12.9847 29.3341 16.6263C29.3341 20.0055 26.6907 22.2821 23.4297 22.3906C23.1215 22.4009 22.8041 22.4066 22.479 22.4066Z" fill="url(#paint1_linear_1167_3838)"/>
<defs>
<linearGradient id="paint0_linear_1167_3838" x1="36.1626" y1="5.50001" x2="6.56658" y2="9.39415" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3838" x1="36.1626" y1="5.50001" x2="6.56658" y2="9.39415" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/images/fdf.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4997 34.874C12.3126 30.0243 7.20914 24.8021 7.00489 17.1083C6.97962 16.1562 7.05674 15.2064 7.13371 14.2584L7.14001 14.1808C7.59562 8.5555 11.845 4.05948 17.3865 3.61013C18.2406 3.54087 19.1182 3.5 20 3.5C20.8818 3.5 21.7594 3.54087 22.6135 3.61013C28.155 4.05948 32.4044 8.5555 32.86 14.1808L32.8663 14.2577C32.9433 15.2059 33.0204 16.156 32.9951 17.1083C32.7909 24.8021 27.6873 30.0244 22.5002 34.8741C21.3408 35.958 20.7611 36.5 19.9999 36.5C19.2388 36.5 18.6591 35.958 17.4997 34.874ZM15.9727 15.7485C16.0606 14.5491 16.1046 13.9494 16.6974 13.3475C17.2903 12.7455 17.8809 12.7009 19.0622 12.6116C19.3684 12.5885 19.6843 12.5745 20.0002 12.5745C20.3161 12.5745 20.6319 12.5885 20.9382 12.6116C22.1195 12.7009 22.7101 12.7455 23.303 13.3475C23.8958 13.9494 23.9398 14.5491 24.0277 15.7485C24.0505 16.0594 24.0643 16.3801 24.0643 16.7008C24.0643 17.0216 24.0505 17.3423 24.0277 17.6532C23.9398 18.8526 23.8958 19.4523 23.303 20.0542C22.7101 20.6561 22.1195 20.7008 20.9382 20.79C20.6319 20.8132 20.3161 20.8272 20.0002 20.8272C19.6843 20.8272 19.3684 20.8132 19.0622 20.79C17.8809 20.7008 17.2903 20.6561 16.6974 20.0542C16.1046 19.4523 16.0606 18.8526 15.9727 17.6532C15.9499 17.3423 15.9361 17.0216 15.9361 16.7008C15.9361 16.3801 15.9499 16.0594 15.9727 15.7485Z" fill="url(#paint0_linear_1167_3834)"/>
<defs>
<linearGradient id="paint0_linear_1167_3834" x1="35.0081" y1="3.50001" x2="7.36896" y2="6.46759" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,19 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.5736 11.2956C28.8646 10.9751 29.0057 10.5396 28.9998 10.1065C28.9938 9.66219 28.8332 9.22052 28.521 8.90831C28.0388 8.43315 27.5508 7.97381 27.0651 7.53317C24.1032 4.84634 22.6951 3.50195 20.0043 3.5C17.3135 3.49806 15.9035 4.84034 12.9383 7.52299C12.4517 7.96326 11.9629 8.42216 11.4801 8.89678L11.4861 8.89085L11.444 8.93234L11.4801 8.89678C11.1648 9.21142 11.0037 9.65812 11.0001 10.1064C10.9966 10.5361 11.1377 10.9673 11.4263 11.2852L11.4275 11.2865C12.0059 11.922 12.8739 11.9496 13.4846 11.3519L13.5303 11.3072C13.9625 10.8837 14.4025 10.471 14.8439 10.0717C17.9631 7.24972 18.5575 6.79991 20.0023 6.80095C21.4471 6.802 22.0408 7.25274 25.1564 10.0791C25.5971 10.4789 26.0366 10.8921 26.4685 11.3163L26.5139 11.3609C27.1245 11.9599 27.9937 11.9328 28.5724 11.2968L28.5736 11.2956Z" fill="url(#paint0_linear_1167_3832)"/>
<path d="M23.0103 11.3961C21.858 10.5134 21.1122 10.1008 19.9975 10.1C18.8829 10.0992 18.1385 10.5091 16.9892 11.3869C16.3205 11.8977 15.4892 12.3782 15.4892 13.4001C15.4892 14.3114 16.1608 15.0501 16.9892 15.0501C17.3429 15.0501 17.668 14.9154 17.9245 14.6901C17.9474 14.6798 17.9695 14.6665 17.9905 14.6501C18.861 13.9668 19.2562 13.696 19.5747 13.5196C19.763 13.4152 19.838 13.3998 19.9955 13.4C20.1514 13.4001 20.2267 13.4151 20.4171 13.5211C20.7373 13.6994 21.1346 13.9727 22.0069 14.6592C22.0352 14.6815 22.0657 14.6981 22.0973 14.7093C22.3502 14.9231 22.6667 15.0501 23.0103 15.0501C23.8387 15.0501 24.5103 14.3114 24.5103 13.4001C24.5103 12.3724 23.6804 11.9094 23.0103 11.3961Z" fill="url(#paint1_linear_1167_3832)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.9388 18.3273C27.0963 19.5964 27.1782 20.5905 27.342 22.5789C27.4362 23.7217 27.4998 25.0628 27.4998 26.6C27.4998 28.1378 27.4361 29.4793 27.342 30.6222C27.1782 32.6097 27.0963 33.6034 25.9389 34.8724C24.7815 36.1414 23.8954 36.2266 22.1233 36.3969C21.463 36.4603 20.7503 36.5 19.9999 36.5C19.2497 36.5 18.5369 36.4603 17.8767 36.3969C16.1045 36.2266 15.2184 36.1414 14.061 34.8724C12.9036 33.6034 12.8217 32.6096 12.6579 30.6221C12.5637 29.4791 12.5 28.1378 12.5 26.6C12.5 25.0629 12.5637 23.7218 12.6578 22.5791C12.8216 20.5906 12.9035 19.5964 14.0611 18.3273C15.2187 17.0582 16.1052 16.9731 17.8784 16.803C18.5381 16.7397 19.2503 16.7001 19.9999 16.7001C20.7496 16.7001 21.4618 16.7397 22.1216 16.803C23.8947 16.9731 24.7812 17.0582 25.9388 18.3273ZM20.0003 27.0121C21.0356 27.0121 21.8749 26.0889 21.8749 24.95C21.8749 23.8112 21.0356 22.888 20.0003 22.888C18.965 22.888 18.1257 23.8112 18.1257 24.95C18.1257 26.0889 18.965 27.0121 20.0003 27.0121Z" fill="url(#paint2_linear_1167_3832)"/>
<defs>
<linearGradient id="paint0_linear_1167_3832" x1="30.3902" y1="3.50001" x2="11.1412" y2="4.93083" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3832" x1="30.3902" y1="3.50001" x2="11.1412" y2="4.93083" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint2_linear_1167_3832" x1="30.3902" y1="3.50001" x2="11.1412" y2="4.93083" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.9002 16.2266C31.9002 16.7885 31.8566 17.3398 31.7726 17.877L25.8953 24.9199L24.7108 23.6897C22.788 21.6929 19.6706 21.6929 17.7478 23.6897C15.825 25.6866 15.825 28.9241 17.7478 30.921L18.3449 31.5411C16.6468 31.4979 15.1168 31.4038 13.7562 31.2811C11.171 31.048 9.59167 30.9055 8.02106 29.8147C5.61536 28.391 4 25.7538 4 22.7358C4 18.2069 7.63775 14.5355 12.1252 14.5355C12.1968 14.5355 12.2683 14.5364 12.3395 14.5383C13.1144 9.69365 17.1684 6 22.0531 6C27.4915 6 31.9002 10.5786 31.9002 16.2266ZM35.6196 23.2832C36.1998 22.5601 36.1058 21.4853 35.4094 20.8827C34.7131 20.2801 33.6782 20.3778 33.098 21.1009L26.0425 29.8937L22.3898 26.1001C21.7488 25.4345 20.7097 25.4345 20.0688 26.1001C19.4278 26.7658 19.4278 27.8449 20.0688 28.5106L24.8017 33.4259C25.5865 34.2409 26.8757 34.1802 27.5862 33.2947L35.6196 23.2832Z" fill="url(#paint0_linear_1167_3829)"/>
<defs>
<linearGradient id="paint0_linear_1167_3829" x1="38.4715" y1="6.00001" x2="4.87682" y2="11.2322" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,14 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.2583 11.1276C26.354 11.1276 26.5713 11.1251 26.7685 11.1521C26.978 11.1809 27.3201 11.2602 27.6405 11.5231L27.7762 11.6472L27.8978 11.7858C28.1546 12.1125 28.2327 12.4611 28.261 12.675C28.2875 12.8763 28.285 13.0996 28.285 13.1975V21.9118H30.9732C31.069 21.9118 31.2876 21.9078 31.4848 21.9349C31.6942 21.9637 32.0356 22.0437 32.3554 22.3058L32.4911 22.4314L32.6127 22.57C32.8697 22.8969 32.9477 23.2453 32.9759 23.4592C33.0024 23.6603 33 23.8822 32.9999 23.9803V32.9313C32.9999 33.029 33.0023 33.2511 32.9759 33.4524C32.9437 33.6967 32.8464 34.1168 32.4911 34.4801C32.1356 34.8432 31.7243 34.9437 31.4848 34.9767C31.2876 35.0038 31.069 34.9998 30.9732 34.9998H8.91514C8.85634 34.9998 8.72232 35.0017 8.59855 34.9911C8.45683 34.979 8.20263 34.9433 7.92863 34.7963C7.61775 34.6292 7.36286 34.369 7.19934 34.0514C7.05544 33.7715 7.02184 33.5118 7.00995 33.3672C6.9996 33.2407 7.00006 33.1038 7.00006 33.0439V26.7012C7.00005 26.6034 6.99765 26.3814 7.02409 26.1801C7.05636 25.9356 7.1532 25.5142 7.50887 25.1509C7.86442 24.7882 8.2759 24.6888 8.51517 24.6558C8.71255 24.6288 8.93124 24.6313 9.0268 24.6313H14.3028L16.0412 22.6133C16.0693 22.5807 16.1454 22.4912 16.225 22.4127C16.3242 22.3149 16.4854 22.1754 16.7154 22.0691L16.8836 22.0027C17.0456 21.9486 17.189 21.9276 17.292 21.919C17.4033 21.9098 17.5204 21.9118 17.562 21.9118H19.4403L19.6495 13.147C19.6518 13.0516 19.6542 12.8324 19.6848 12.6346C19.7222 12.3937 19.8265 11.9832 20.1795 11.6313L20.3138 11.5115C20.6308 11.2569 20.9662 11.1788 21.1731 11.1507C21.3676 11.1243 21.5816 11.1276 21.6748 11.1276H26.2583ZM20.3251 27.1458C19.905 27.1778 19.6936 27.1937 19.4827 27.4085C19.2718 27.6241 19.2554 27.8396 19.2241 28.2689C19.216 28.3801 19.2114 28.4962 19.2114 28.611C19.2114 28.7255 19.216 28.8405 19.2241 28.9516C19.2554 29.3811 19.2717 29.5964 19.4827 29.8119C19.6936 30.027 19.9049 30.0427 20.3251 30.0746C20.4338 30.0829 20.5465 30.0891 20.6586 30.0891C20.7707 30.0891 20.8835 30.0829 20.9922 30.0746C21.4122 30.0427 21.6236 30.027 21.8345 29.8119C22.0452 29.5965 22.0619 29.3809 22.0932 28.9516C22.1012 28.8406 22.1059 28.7255 22.1059 28.611C22.1059 28.4961 22.1013 28.3802 22.0932 28.2689C22.0619 27.8396 22.0454 27.624 21.8345 27.4085C21.6236 27.1934 21.4122 27.1778 20.9922 27.1458C20.8835 27.1376 20.7707 27.1329 20.6586 27.1328C20.5465 27.1328 20.4337 27.1376 20.3251 27.1458ZM25.3735 27.1458C24.9535 27.1778 24.742 27.1937 24.5312 27.4085C24.3202 27.6241 24.3038 27.8396 24.2725 28.2689C24.2644 28.3801 24.2598 28.4962 24.2598 28.611C24.2598 28.7255 24.2644 28.8405 24.2725 28.9516C24.3038 29.3811 24.3202 29.5964 24.5312 29.8119C24.7421 30.027 24.9533 30.0427 25.3735 30.0746C25.4822 30.0829 25.595 30.0891 25.7071 30.0891C25.8192 30.0891 25.9319 30.0829 26.0406 30.0746C26.4606 30.0427 26.6721 30.027 26.883 29.8119C27.0937 29.5965 27.1103 29.3809 27.1416 28.9516C27.1497 28.8406 27.1543 28.7255 27.1543 28.611C27.1543 28.4961 27.1497 28.3802 27.1416 28.2689C27.1103 27.8396 27.0939 27.624 26.883 27.4085C26.6721 27.1934 26.4607 27.1778 26.0406 27.1458C25.9319 27.1376 25.8192 27.1329 25.7071 27.1328C25.595 27.1328 25.4822 27.1376 25.3735 27.1458Z" fill="url(#paint0_linear_1167_3840)"/>
<path d="M26.8392 5C27.8612 5 28.6892 5.84569 28.6892 6.88951C28.6892 7.93331 27.8612 8.77902 26.8392 8.77902H25.8272C25.8184 8.77901 25.8091 8.77771 25.8004 8.77758C25.603 9.2932 25.113 9.65954 24.5382 9.65954H23.7962C23.0477 9.65922 22.4408 9.03841 22.4408 8.27381C22.4411 7.50943 23.0478 6.88983 23.7962 6.88951H23.9757C23.9757 5.84574 24.8053 5.00008 25.8272 5H26.8392Z" fill="url(#paint1_linear_1167_3840)"/>
<defs>
<linearGradient id="paint0_linear_1167_3840" x1="35.0081" y1="5.00001" x2="7.43495" y2="8.25655" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3840" x1="35.0081" y1="5.00001" x2="7.43495" y2="8.25655" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/assets/images/s_002.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 12h-.005c0-5.523-4.476-10-9.998-10S2 6.477 2 12v2.5q0 .278.013.554c.053 1.141.08 1.712.703 2.316.622.604 1.137.612 2.167.628h.235c1.03-.017 1.544-.025 2.165-.628s.648-1.173.702-2.313a12 12 0 0 0 0-1.116c-.054-1.14-.08-1.71-.702-2.313-.621-.603-1.136-.611-2.165-.628H4.061a8 8 0 0 1 15.872 0c-.352 0-.704-.005-1.057 0-1.029.017-1.543.025-2.165.628-.62.603-.648 1.173-.701 2.313a12 12 0 0 0 0 1.116c.053 1.14.08 1.71.701 2.313.622.603 1.136.611 2.165.628.24.004.48-.002.72-.01-.098.24-.266.504-.636.875-.447.449-.747.602-1.06.699-.42.13-.944.184-2.022.278q-.86.076-1.882.12A1.998 1.998 0 1 0 11.997 22c1.55 0 2.901-.068 4.054-.168 2.024-.175 3.037-.263 4.326-1.557 1.287-1.293 1.37-2.263 1.53-4.2z"/></svg>

After

Width:  |  Height:  |  Size: 777 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M4.682 4.686c-1.313 1.313-1.41 2.62-1.605 5.237-.05.678-.08 1.377-.08 2.077s.03 1.399.08 2.077c.195 2.616.292 3.924 1.605 5.237s2.62 1.41 5.237 1.605c.678.05 1.378.081 2.077.081s1.4-.03 2.077-.081c2.616-.195 3.924-.292 5.237-1.605s1.41-2.62 1.605-5.237c.05-.678.081-1.377.081-2.077s-.03-1.399-.081-2.077c-.195-2.616-.292-3.924-1.605-5.237s-2.62-1.41-5.237-1.605A28 28 0 0 0 11.996 3c-.7 0-1.399.03-2.077.081-2.616.195-3.924.292-5.237 1.605M8.495 10a3.501 3.501 0 1 1 7.002 0 3.501 3.501 0 0 1-7.002 0m4.35 5.021c1.547.077 3.068.44 5.854 1.145-.145.915-.355 1.285-.803 1.734-.706.706-1.22.82-3.971 1.024-.637.048-1.287.076-1.929.076s-1.292-.028-1.929-.076c-2.75-.204-3.265-.318-3.97-1.024-.449-.448-.658-.82-.804-1.733 2.79-.706 4.312-1.069 5.858-1.146a17 17 0 0 1 1.695 0"/></svg>

After

Width:  |  Height:  |  Size: 869 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M2 16a1 1 0 0 0 1 1h2.929a6 6 0 0 0 4.243-1.757L12 13.414l1.828 1.829A6 6 0 0 0 17 16.903v.045c.007 1.134.055 1.729.448 1.965.447.27 1.04-.108 2.224-.864l.52-.332.024-.015C21.4 16.945 22 16.563 22 16s-.599-.945-1.784-1.702l-.024-.015-.52-.332c-1.185-.756-1.777-1.134-2.224-.864-.37.222-.434.761-.446 1.768h-.001a4 4 0 0 1-1.758-1.027L13.414 12l1.829-1.828A4 4 0 0 1 17 9.146h.001c.012 1.006.076 1.545.446 1.767.447.27 1.04-.108 2.224-.864l.52-.332.024-.015C21.4 8.945 22 8.563 22 8s-.599-.945-1.784-1.702l-.024-.015-.52-.332c-1.184-.756-1.777-1.134-2.224-.864-.393.236-.441.83-.447 1.965H17v.044a6 6 0 0 0-3.172 1.661L12 10.586l-1.828-1.829A6 6 0 0 0 5.929 7H3a1 1 0 1 0 0 2h2.929a4 4 0 0 1 2.828 1.172L10.586 12l-1.829 1.828A4 4 0 0 1 5.93 15H3a1 1 0 0 0-1 1"/></svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13.596 9.423C14.186 8.942 15 8.276 15 7a3 3 0 1 0-5.988.264c.103 1.106.844 1.712 1.392 2.16.334.272.596.486.596.719 0 .266-.384.439-.935.687-1.092.491-1.924 1.037-2.76 1.943-.598.65-.925 1.167-.994 1.348-.202.533-.262.774-.286.909-.021.122-.025.215-.025.57 0 1.36.016 1.658.068 1.853.19.724.755 1.29 1.479 1.48.195.05.492.067 1.853.067H12a1 1 0 0 1 0 2H9.4c-1.235 0-1.853 0-2.36-.133a4.07 4.07 0 0 1-2.907-2.907C4 17.453 4 16.835 4 15.6c0-.683 0-1.025.441-2.188.37-.975 1.782-2.559 2.932-3.39q.22-.16.421-.278l-.06-.117-.072-.14A5 5 0 0 1 7.001 7a5 5 0 1 1 9.326 2.508l-.061.12-.06.116a5 5 0 0 1 .42.277c.98.708 2.147 1.96 2.702 2.918a1 1 0 1 1-1.685 1.076c-.125-.236-.44-.69-.948-1.242-.836-.907-1.668-1.452-2.76-1.943-.551-.248-.935-.421-.935-.687 0-.233.261-.447.596-.72"/><path d="M22.207 16.207a1 1 0 1 0-1.414-1.414L17 18.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0z"/></svg>

After

Width:  |  Height:  |  Size: 968 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M8.779 11.667c.785.386 1.556.765 2.17 1.379s.992 1.384 1.378 2.17l.088.178 1.71 3.465q.039.076.072.13a.3.3 0 0 0 .106-.077c.105-.1.272-.309.498-.733.473-.887.955-2.223 1.693-4.294l.423-1.186c1.004-2.817 1.676-4.717 1.952-6.095.135-.673.141-1.07.109-1.295-.023-.163-.058-.196-.075-.212l-.003-.003-.003-.003c-.016-.016-.049-.051-.211-.075-.226-.032-.623-.026-1.296.109-1.378.276-3.278.948-6.095 1.952L10.11 7.5c-2.072.738-3.408 1.22-4.295 1.693-.424.226-.632.393-.732.498a.3.3 0 0 0-.077.106q.052.034.13.072L8.6 11.58q.089.046.178.088m-1.064 1.706c.964.476 1.446.714 1.82 1.087.372.373.61.855 1.086 1.819l1.71 3.465c.266.537.672 1.031 1.252 1.18.35.09.69.098 1.036.02 1.612-.363 2.328-2.371 3.76-6.388l.422-1.185c1.963-5.507 2.944-8.26 1.513-9.691-1.43-1.431-4.184-.45-9.69 1.513l-1.186.423C5.42 7.048 3.413 7.764 3.05 9.376c-.077.345-.07.685.02 1.036.15.58.643.985 1.18 1.25z"/></svg>

After

Width:  |  Height:  |  Size: 972 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M15 21a3 3 0 0 0 3-3h2a1 1 0 1 0 0-2h-2a3 3 0 0 0-3-3h-3a3 3 0 0 0-3 3H4a1 1 0 1 0 0 2h5a3 3 0 0 0 3 3zm-4-3v-2a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1M6 8H4a1 1 0 1 1 0-2h2a3 3 0 0 1 3-3h3a3 3 0 0 1 3 3h5a1 1 0 1 1 0 2h-5a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3m7-2a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1z"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 12a1 1 0 0 1 1-1h4a1 1 0 1 1 0 2h-4a1 1 0 0 1-1-1"/><path fill-rule="evenodd" d="M4.685 4.687C3.373 6 3.275 7.308 3.081 9.924A28 28 0 0 0 3 12.002c0 .699.03 1.398.08 2.077.195 2.616.293 3.924 1.605 5.236 1.313 1.313 2.621 1.41 5.237 1.605.678.05 1.378.081 2.077.081s1.4-.03 2.078-.08c2.616-.195 3.924-.293 5.237-1.605 1.312-1.313 1.41-2.621 1.605-5.237.05-.679.08-1.378.08-2.078s-.03-1.399-.08-2.077c-.195-2.616-.293-3.924-1.605-5.237-1.313-1.312-2.621-1.41-5.237-1.604A28 28 0 0 0 11.999 3c-.699 0-1.399.031-2.077.082-2.616.194-3.924.292-5.237 1.604M12 5.001c-.643 0-1.292.029-1.93.076-2.75.205-3.264.318-3.97 1.025-.28.279-.466.528-.603.898h13.005c-.137-.37-.323-.619-.603-.898-.706-.707-1.22-.82-3.97-1.025a26 26 0 0 0-1.93-.076M18.838 9H5.16c-.028.315-.056.67-.086 1.073A26 26 0 0 0 5 12c0 .643.028 1.292.075 1.93.205 2.75.318 3.264 1.025 3.97s1.22.82 3.97 1.025c.638.047 1.287.076 1.93.076.642 0 1.291-.029 1.928-.076 2.75-.205 3.265-.318 3.971-1.025s.82-1.22 1.025-3.97A26 26 0 0 0 19 12c0-.642-.029-1.291-.076-1.928Q18.88 9.47 18.838 9"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M3.002 15.474a1.498 1.498 0 0 0 2.216 1.315l2.838-1.559c.398.023.919.045 1.382.065l-1.881 5.373a1 1 0 0 0 .944 1.33h2.227a3 3 0 0 0 2.629-1.554l2.81-5.108c2.34-.13 3.997-.45 4.955-.689 1.277-.317 1.88-1.388 1.88-2.648 0-1.344-.683-2.454-2.05-2.763-.982-.222-2.59-.5-4.809-.617l-2.786-5.067a3 3 0 0 0-2.629-1.554H8.501a1 1 0 0 0-.944 1.33l1.871 5.346c-.424.02-.912.042-1.281.065l-2.948-1.57a1.5 1.5 0 0 0-2.197 1.318c0 1.198.21 2.355.5 3.511-.286 1.145-.5 2.289-.5 3.476m2.034-6.126c.045.536.137 1.084.267 1.607l.26 1.043-.263 1.054a10 10 0 0 0-.26 1.555l2.472-1.41c.641.036 1.282.072 1.923.1l2.78.116-2.305 6.585h.818a1 1 0 0 0 .876-.518l3.344-6.08 1.107-.061c1.957-.109 3.383-.357 4.267-.558.44-.1.68-.292.68-.782 0-.531-.183-.747-.68-.854-.893-.191-2.324-.426-4.283-.529l-1.112-.058-3.323-6.042a1 1 0 0 0-.876-.518H9.91l2.292 6.546-2.768.128q-.865.044-1.73.098z"/></svg>

After

Width:  |  Height:  |  Size: 961 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 16a1 1 0 1 1 0 2H9a1 1 0 1 1 0-2zm-1-3a1 1 0 0 0-1-1H9a1 1 0 1 0 0 2h2a1 1 0 0 0 1-1"/><path fill-rule="evenodd" d="M19.99 9.45c.01.133.01.297.01.5V12c0 1.55-.068 2.903-.168 4.056-.175 2.024-.263 3.036-1.557 4.325s-2.264 1.37-4.205 1.532a25 25 0 0 1-4.14 0c-1.94-.161-2.911-.242-4.205-1.532-1.294-1.289-1.381-2.301-1.557-4.325A47 47 0 0 1 4 12c0-1.549.068-2.901.168-4.054.175-2.025.263-3.038 1.557-4.327s2.265-1.37 4.207-1.532A25 25 0 0 1 12 2h.012c.554-.002 1.076-.004 1.579.295.48.286 1.398 1.095 3.197 2.893 1.515 1.516 2.398 2.657 2.803 3.18q.114.148.179.228c.15.184.203.487.22.853m-6.989-5.1q.149.124.356.311a56 56 0 0 1 2.017 1.942A30 30 0 0 1 17.542 9H17.5c-1.033 0-2.067-.134-2.825-.263-.548-.093-.822-.14-1.047-.365s-.272-.5-.365-1.047C13.133 6.567 13 5.534 13 4.5zm-2 .15c0 1.189.151 2.344.29 3.16l.016.102c.064.413.204 1.322.907 2.024.702.703 1.611.843 2.024.907l.101.016c.817.139 1.972.291 3.161.291q.252 0 .5-.009V12a45 45 0 0 1-.16 3.882c-.094 1.078-.149 1.601-.278 2.022-.097.312-.25.612-.699 1.06-.46.458-.755.606-1.043.696-.393.123-.879.173-1.916.26a23 23 0 0 1-3.808 0c-1.037-.087-1.522-.137-1.916-.26-.288-.09-.584-.238-1.043-.696-.449-.448-.602-.748-.699-1.06-.13-.42-.184-.944-.278-2.022A45 45 0 0 1 6 12c0-1.49.065-2.785.16-3.88.094-1.08.149-1.603.278-2.024.097-.313.25-.613.699-1.06.46-.458.755-.607 1.043-.697.394-.122.88-.173 1.918-.259q.442-.037.91-.058Q11 4.259 11 4.5"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7.5q-.256 0-.498.02l-.07.004c-.52.038-.986.072-1.41.497-.425.425-.46.891-.497 1.411l-.005.07a7 7 0 0 0 0 .997l.005.069c.038.52.072.986.496 1.41.425.426.891.46 1.411.498l.07.005a7 7 0 0 0 .997 0l.07-.005c.519-.038.985-.072 1.41-.497s.459-.891.497-1.411l.005-.07a7 7 0 0 0 0-.997l-.005-.069c-.038-.52-.072-.986-.497-1.41-.425-.426-.891-.46-1.41-.498l-.07-.005A7 7 0 0 0 12 7.5"/><path fill-rule="evenodd" d="M18.248 16.103c-.217.315-.463.621-.957 1.233l-1.05 1.302c-1.598 1.984-2.398 2.975-3.397 3.248-.553.15-1.135.15-1.688 0-1-.273-1.799-1.264-3.398-3.248l-1.05-1.301c-.493-.613-.74-.919-.956-1.235a9.97 9.97 0 0 1-1.723-4.878A17 17 0 0 1 4 9.939v-.275c0-.525.036-1.037.079-1.557.287-3.505 1.837-5.29 5.322-5.876.83-.14 1.715-.232 2.599-.232s1.769.093 2.599.232c3.485.585 5.035 2.371 5.322 5.876.043.52.079 1.032.079 1.556 0 .786 0 1.179-.03 1.56a9.97 9.97 0 0 1-1.722 4.88M16.6 14.97c-.163.237-.351.473-.856 1.099l-1.06 1.314c-.826 1.025-1.351 1.673-1.787 2.114-.413.416-.567.456-.58.46a1.2 1.2 0 0 1-.635 0c-.013-.004-.167-.044-.579-.46-.436-.44-.961-1.089-1.788-2.114l-1.05-1.302c-.512-.635-.701-.873-.865-1.11a7.97 7.97 0 0 1-1.377-3.9A15 15 0 0 1 6 9.937v-.275a17 17 0 0 1 .072-1.393c.124-1.507.496-2.346.958-2.86.454-.507 1.229-.96 2.702-1.207A14 14 0 0 1 12 4c.749 0 1.52.079 2.268.204 1.473.248 2.248.7 2.702 1.206.462.515.834 1.354.958 2.861.042.512.072.955.072 1.393 0 .816-.001 1.12-.023 1.407a7.97 7.97 0 0 1-1.377 3.9"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M15.33 6.914a8 8 0 0 1 .58-.58c.201-.18.338-.303.459-.403a3 3 0 0 1 .127-.1q.046.033.128.1a17 17 0 0 1 .756.685q.142.143.282.298c.18.201.303.338.403.458q.067.082.101.128a3 3 0 0 1-.101.128 17 17 0 0 1-.685.756 17 17 0 0 1-.756.685 3 3 0 0 1-.128.1 3 3 0 0 1-.127-.1 17 17 0 0 1-.757-.685 16 16 0 0 1-.685-.756 3 3 0 0 1-.1-.128q.034-.046.1-.128c.1-.12.223-.257.403-.458m-1.487 2.508c-.693-.77-1.039-1.156-1.039-1.922s.346-1.152 1.039-1.922a10 10 0 0 1 .73-.732c.771-.692 1.157-1.038 1.923-1.038.767 0 1.152.346 1.923 1.038a10 10 0 0 1 .73.732c.693.77 1.04 1.156 1.04 1.922s-.347 1.152-1.04 1.922a10 10 0 0 1-.73.732c-.771.692-1.156 1.038-1.923 1.038-.766 0-1.151-.346-1.922-1.038a10 10 0 0 1-.731-.732m-6.946-3.4Q7.204 6 7.5 6q.297 0 .603.022c.272.019.46.032.619.049.082.009.14.017.183.024q.01.061.024.183c.017.16.03.347.049.619q.021.307.022.603 0 .297-.022.603c-.019.272-.032.46-.049.619-.009.082-.017.14-.024.183a3 3 0 0 1-.183.024c-.16.017-.347.03-.619.049A9 9 0 0 1 7.5 9q-.297 0-.603-.022c-.272-.019-.46-.032-.619-.049a3 3 0 0 1-.183-.024 3 3 0 0 1-.024-.183c-.017-.16-.03-.347-.049-.619A9 9 0 0 1 6 7.5q0-.297.022-.603c.019-.272.032-.46.049-.619.009-.082.017-.14.024-.183q.061-.01.183-.024c.16-.017.347-.03.619-.049m-2.87.737c.072-1.044.108-1.565.637-2.095.53-.53 1.051-.565 2.095-.637Q7.125 4 7.5 4q.376 0 .741.027c1.044.072 1.565.108 2.095.637.53.53.565 1.051.637 2.095Q11 7.125 11 7.5q0 .376-.027.741c-.072 1.044-.108 1.565-.637 2.095-.53.53-1.051.565-2.095.637A11 11 0 0 1 7.5 11q-.376 0-.741-.027c-1.044-.072-1.565-.108-2.095-.637-.53-.53-.565-1.051-.637-2.095A11 11 0 0 1 4 7.5q0-.376.027-.741M7.5 15a9 9 0 0 0-.603.022c-.272.019-.46.032-.619.049-.082.009-.14.017-.183.024a3 3 0 0 0-.024.183c-.017.16-.03.347-.049.619A9 9 0 0 0 6 16.5q0 .297.022.603c.019.272.032.46.049.619.009.082.017.14.024.183q.061.01.183.024c.16.017.347.03.619.049A9 9 0 0 0 7.5 18q.297 0 .603-.022c.272-.019.46-.032.619-.049.082-.009.14-.017.183-.024q.01-.061.024-.183c.017-.16.03-.347.049-.619q.021-.307.022-.603 0-.297-.022-.603c-.019-.272-.032-.46-.049-.619a3 3 0 0 0-.024-.183 3 3 0 0 0-.183-.024c-.16-.017-.347-.03-.619-.049A9 9 0 0 0 7.5 15m-2.836-1.336c-.53.53-.565 1.051-.637 2.095Q4 16.125 4 16.5q0 .376.027.741c.072 1.044.108 1.565.637 2.095.53.53 1.051.565 2.095.637Q7.125 20 7.5 20q.376 0 .741-.027c1.044-.072 1.565-.108 2.095-.637.53-.53.565-1.051.637-2.095Q11 16.875 11 16.5q0-.376-.027-.741c-.072-1.044-.108-1.565-.637-2.095-.53-.53-1.051-.565-2.095-.637A11 11 0 0 0 7.5 13q-.376 0-.741.027c-1.044.072-1.565.108-2.095.637m11.233 1.358q.307-.021.603-.022.297 0 .603.022c.272.019.46.032.619.049.082.009.14.017.183.024q.01.061.024.183c.017.16.03.347.049.619q.021.307.022.603 0 .297-.022.603c-.019.272-.032.46-.049.619-.009.082-.017.14-.024.183a3 3 0 0 1-.183.024c-.16.017-.347.03-.619.049A9 9 0 0 1 16.5 18q-.297 0-.603-.022c-.272-.019-.46-.032-.619-.049a3 3 0 0 1-.183-.024 3 3 0 0 1-.024-.183c-.017-.16-.03-.347-.049-.619A9 9 0 0 1 15 16.5q0-.297.022-.603c.019-.272.032-.46.049-.619.009-.082.017-.14.024-.183q.061-.01.183-.024c.16-.017.347-.03.619-.049m-2.87.737c.072-1.044.108-1.565.637-2.095.53-.53 1.051-.565 2.095-.637Q16.125 13 16.5 13q.376 0 .741.027c1.044.072 1.565.108 2.095.637.53.53.565 1.051.637 2.095q.026.366.027.741 0 .376-.027.741c-.072 1.044-.108 1.565-.637 2.095-.53.53-1.051.565-2.095.637A11 11 0 0 1 16.5 20q-.376 0-.741-.027c-1.044-.072-1.565-.108-2.095-.637-.53-.53-.565-1.051-.637-2.095A11 11 0 0 1 13 16.5q0-.376.027-.741"/></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M16.105 17.519c.425.425.382 1.132-.14 1.43A7.96 7.96 0 0 1 12 20a7.96 7.96 0 0 1-3.965-1.05c-.522-.299-.565-1.006-.14-1.431l.77-.77c.336-.336.859-.378 1.293-.184.624.28 1.315.435 2.042.435.728 0 1.419-.155 2.042-.435.434-.194.957-.152 1.293.184zm1.414-1.415c.425.426 1.132.383 1.43-.14A7.96 7.96 0 0 0 20 12a7.96 7.96 0 0 0-1.05-3.965c-.299-.522-1.006-.565-1.431-.14l-.77.77c-.336.336-.378.859-.184 1.293.28.623.435 1.315.435 2.042s-.155 1.419-.435 2.042c-.194.434-.152.957.185 1.293zM15.965 5.05c.522.299.565 1.006.14 1.431l-.77.77c-.336.336-.859.378-1.293.184A5 5 0 0 0 12 7a5 5 0 0 0-2.715.8.06.06 0 0 1-.078-.007L7.896 6.48c-.426-.425-.383-1.132.14-1.43A7.96 7.96 0 0 1 12 4c1.443 0 2.796.382 3.965 1.05M6.481 7.895c-.425-.425-1.132-.382-1.43.14A7.96 7.96 0 0 0 4 12c0 1.443.382 2.796 1.05 3.965.299.522 1.006.565 1.431.14l.77-.77c.336-.336.378-.859.184-1.293A5 5 0 0 1 7 12a4.97 4.97 0 0 1 .801-2.715.06.06 0 0 0-.008-.078zM22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-7 0a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,14 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.7001 13C21.7001 15.2856 20.6359 17.3154 18.9898 18.593L19.1265 18.6301C21.7381 19.3407 26.0663 24.0493 26.6083 26.7696C26.645 26.954 27.009 28.7818 26.6267 30.1751C26.1443 31.9331 24.8075 33.3071 23.0971 33.8029C22.4171 34 21.5858 34 19.9232 34H9.87679C8.21424 34 7.38296 34 6.70298 33.8029C4.99252 33.3071 3.65574 31.9331 3.17334 30.1751C2.79104 28.7818 3.155 26.9541 3.19175 26.7696C3.73378 24.0493 8.06192 19.3407 10.6736 18.6301L10.8103 18.593C9.16422 17.3154 8.10007 15.2856 8.10007 13C8.10007 9.13401 11.1445 6 14.9001 6C18.6556 6 21.7001 9.13401 21.7001 13Z" fill="url(#paint0_linear_1167_3833)"/>
<path d="M30.1232 34H28.5169C29.1365 33.1506 29.6095 32.1801 29.8986 31.1265C30.2491 29.8492 30.2242 28.567 30.165 27.7565C30.1015 26.8869 29.9691 26.218 29.9389 26.0663C29.6818 24.7759 29.0588 23.5493 28.4357 22.5541C27.7811 21.5085 26.9709 20.4739 26.1131 19.5406C25.4785 18.8503 24.7887 18.1837 24.0767 17.5876C24.7316 16.202 25.1 14.6452 25.1 13C25.1 10.499 24.2506 8.20216 22.8325 6.39865C23.5417 6.14048 24.3049 6 25.1 6C28.8556 6 31.9001 9.13401 31.9001 13C31.9001 15.2856 30.8359 17.3154 29.1898 18.593L29.3264 18.6301C31.9381 19.3407 36.2662 24.0493 36.8083 26.7696C36.845 26.954 37.209 28.7818 36.8267 30.1751C36.3443 31.9331 35.0075 33.3071 33.297 33.8029C32.617 34 31.7858 34 30.1232 34Z" fill="url(#paint1_linear_1167_3833)"/>
<defs>
<linearGradient id="paint0_linear_1167_3833" x1="39.626" y1="6.00001" x2="4.04025" y2="11.8887" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3833" x1="39.626" y1="6.00001" x2="4.04025" y2="11.8887" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,14 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 20C6 23.3616 6.14772 26.1602 6.34216 28.3879C6.5854 31.1747 6.70702 32.5681 8.25721 34.244C9.8074 35.92 10.956 36.0562 13.2531 36.3287C14.1419 36.4341 15.1013 36.5 16.1111 36.5C16.5763 36.5 17.0309 36.486 17.4727 36.4606C15.1988 34.2025 13.7778 30.9898 13.7778 27.425C13.7778 20.5905 19.0011 15.05 25.4444 15.05C25.6678 15.05 25.8897 15.0567 26.1099 15.0698C26.05 13.7961 25.9701 12.6436 25.8802 11.6133C25.6369 8.8253 25.5152 7.43133 23.9647 5.75527C22.4141 4.07921 21.2649 3.9431 18.9665 3.67089C18.0785 3.5657 17.1199 3.5 16.1111 3.5C15.1023 3.5 14.1439 3.5657 13.2558 3.67087C10.9573 3.94308 9.80804 4.07919 8.25748 5.75528C6.70692 7.43136 6.58528 8.82541 6.34201 11.6135C6.14765 13.8411 6 16.6393 6 20ZM13.7778 8.45C12.9187 8.45 12.2222 9.18873 12.2222 10.1C12.2222 11.0113 12.9187 11.75 13.7778 11.75H18.4444C19.3036 11.75 20 11.0113 20 10.1C20 9.18873 19.3036 8.45 18.4444 8.45H13.7778Z" fill="url(#paint0_linear_1167_3828)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34 27.425C34 32.437 30.1695 36.5 25.4444 36.5C20.7193 36.5 16.8889 32.437 16.8889 27.425C16.8889 22.413 20.7193 18.35 25.4444 18.35C30.1695 18.35 34 22.413 34 27.425ZM20 27.425L20 27.4096L23.2806 29.367C24.0888 29.8492 24.6667 30.7563 24.6667 31.7399V33.1415C22.0282 32.7412 20 30.3343 20 27.425ZM25.6155 23.3745C26.039 23.0376 26.208 22.2617 26.2214 21.7084C28.8602 22.1083 30.8889 24.5154 30.8889 27.425C30.8889 27.5621 30.8844 27.6981 30.8755 27.8329L30.1797 27.5405C29.9803 27.4612 29.7696 27.4224 29.5585 27.4251C29.4531 27.4264 29.3476 27.4381 29.2433 27.4602L26.7568 28.0542C25.9858 28.2178 25.209 27.803 24.8736 27.0488L24.3944 25.9712C24.0478 25.192 24.5002 24.2618 25.15 23.7448L25.6155 23.3745Z" fill="url(#paint1_linear_1167_3828)"/>
<defs>
<linearGradient id="paint0_linear_1167_3828" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
<linearGradient id="paint1_linear_1167_3828" x1="36.1626" y1="3.50001" x2="6.45143" y2="6.93544" gradientUnits="userSpaceOnUse">
<stop stop-color="#45B6FC"/>
<stop offset="1" stop-color="#0070E5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,118 @@
import { Box, Button, HStack, IconButton, Image, Text, VStack } from "@chakra-ui/react";
import { observer } from "mobx-react-lite";
import { LuMinus, LuPlus, LuTrash2 } from "react-icons/lu";
import { cartStore } from "../../store/cartStore";
const formatPrice = (price: number) =>
new Intl.NumberFormat("ru-RU", {
style: "currency",
currency: "RUB",
maximumFractionDigits: 0,
}).format(price);
type CartContentProps = {
onCheckout: () => void;
};
const CartContent = observer(({ onCheckout }: CartContentProps) => {
if (cartStore.itemsList.length === 0) {
return (
<Box bg="white" borderRadius="24px" p="10">
<Text fontSize="22px" color="#5f6b7e">
Корзина пуста
</Text>
</Box>
);
}
return (
<VStack align="stretch" gap="4">
{cartStore.itemsList.map((item) => (
<Box
key={item.product.id}
bg="white"
borderRadius="24px"
p="6"
border="1px solid"
borderColor="#e8ebf2"
>
<HStack justify="space-between" align="center">
<HStack gap="4" align="stretch">
<Box w="110px" h="90px">
<Image
src={item.product.imageSrc}
alt={`TODO: product image ${item.product.name}`}
w="full"
h="full"
objectFit="contain"
/>
</Box>
<VStack align="start" gap="1">
<Text fontSize="22px" fontWeight="700">
{item.product.name}
</Text>
<Text color="#5f6b7e">{item.product.description}</Text>
<Text fontWeight="700" color="#e30613">
{formatPrice(item.product.price)}/мес
</Text>
</VStack>
</HStack>
<HStack gap="4">
<HStack border="1px solid" borderColor="#d7ddea" borderRadius="full" p="1">
<IconButton
aria-label="Уменьшить количество"
size="sm"
variant="ghost"
onClick={() => cartStore.decrease(item.product.id)}
>
<LuMinus />
</IconButton>
<Text minW="28px" textAlign="center" fontWeight="700">
{item.quantity}
</Text>
<IconButton
aria-label="Увеличить количество"
size="sm"
variant="ghost"
onClick={() => cartStore.increase(item.product.id)}
>
<LuPlus />
</IconButton>
</HStack>
<Text minW="130px" fontWeight="800" textAlign="right">
{formatPrice(item.product.price * item.quantity)}
</Text>
<IconButton
aria-label="Удалить товар"
variant="ghost"
colorPalette="red"
onClick={() => cartStore.remove(item.product.id)}
>
<LuTrash2 />
</IconButton>
</HStack>
</HStack>
</Box>
))}
<Box bg="white" borderRadius="24px" p="6" border="1px solid" borderColor="#e8ebf2">
<VStack align="stretch" gap="4">
<HStack justify="space-between">
<Text fontSize="26px" fontWeight="700">
Итого
</Text>
<Text fontSize="28px" fontWeight="800" color="#e30613">
{formatPrice(cartStore.totalPrice)}
</Text>
</HStack>
<Button bg="#e30613" color="white" _hover={{ bg: "#c90511" }} onClick={onCheckout}>
Оформить
</Button>
</VStack>
</Box>
</VStack>
);
});
export default CartContent;

View File

@ -0,0 +1,20 @@
import { Button, HStack, Text } from "@chakra-ui/react";
type CartPageHeaderProps = {
onContinue: () => void;
};
const CartPageHeader = ({ onContinue }: CartPageHeaderProps) => {
return (
<HStack justify="space-between" mb="6">
<Text fontSize="40px" fontWeight="700">
Корзина
</Text>
<Button variant="outline" onClick={onContinue}>
Продолжить покупки
</Button>
</HStack>
);
};
export default CartPageHeader;

View File

@ -0,0 +1,128 @@
import { Button, Dialog, Input, Text, VStack } from "@chakra-ui/react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { Link } from "react-router";
import { useHookFormMask } from "use-mask-input";
export type CheckoutFormValues = {
customerName: string;
email: string;
phoneNumber: string;
};
type CheckoutDialogProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
onSubmit: (values: CheckoutFormValues) => Promise<void> | void;
};
const CheckoutDialog = ({ open, onOpenChange, onSubmit }: CheckoutDialogProps) => {
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting },
} = useForm<CheckoutFormValues>({
defaultValues: {
customerName: "",
email: "",
phoneNumber: "",
},
mode: "onBlur",
});
const registerWithMask = useHookFormMask(register);
useEffect(() => {
if (!open) {
reset();
}
}, [open, reset]);
return (
<Dialog.Root open={open} onOpenChange={(details) => onOpenChange(details.open)}>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content as="form" onSubmit={handleSubmit(onSubmit)}>
<Dialog.Header>
<Dialog.Title>Оформление заказа</Dialog.Title>
</Dialog.Header>
<Dialog.Body>
<VStack align="stretch" gap="4">
<VStack align="stretch" gap="1">
<Text fontWeight="600">Имя</Text>
<Input {...register("customerName", { required: "Укажите имя" })} />
{errors.customerName ? (
<Text fontSize="12px" color="#e30613">
{errors.customerName.message}
</Text>
) : null}
</VStack>
<VStack align="stretch" gap="1">
<Text fontWeight="600">Email</Text>
<Input
type="email"
placeholder="name@example.com"
{...register("email", {
required: "Укажите email",
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "Некорректный email",
},
})}
/>
{errors.email ? (
<Text fontSize="12px" color="#e30613">
{errors.email.message}
</Text>
) : null}
</VStack>
<VStack align="stretch" gap="1">
<Text fontWeight="600">Номер телефона</Text>
<Input
placeholder="+7 (___) ___-__-__"
{...registerWithMask("phoneNumber", "+7 (999) 999-99-99", {
required: "Укажите номер телефона",
})}
/>
{errors.phoneNumber ? (
<Text fontSize="12px" color="#e30613">
{errors.phoneNumber.message}
</Text>
) : null}
</VStack>
<Text fontSize="13px" color="#5f6b7e" lineHeight="1.4">
Нажимая «Оформить», вы соглашаетесь с{" "}
<Link
to="/privacy"
color="#0070e5"
style={{ textDecoration: "underline" }}
target="_blank"
rel="noreferrer"
>
обработкой персональных данных
</Link>
.
</Text>
</VStack>
</Dialog.Body>
<Dialog.Footer>
<Dialog.ActionTrigger asChild>
<Button variant="outline">Отмена</Button>
</Dialog.ActionTrigger>
<Button
bg="#e30613"
color="white"
_hover={{ bg: "#c90511" }}
loading={isSubmitting}
type="submit"
>
Оформить
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
);
};
export default CheckoutDialog;

View File

@ -0,0 +1,99 @@
import {
Badge,
Box,
Container,
Flex,
HStack,
Icon,
IconButton,
Image,
Text,
} from '@chakra-ui/react';
import { observer } from 'mobx-react-lite';
import { LuShoppingCart, LuMapPin, LuPhone } from 'react-icons/lu';
import logo from '~/assets/images/MTS_Logo_134dd5da5d.svg';
import { useNavigate } from 'react-router';
import { cartStore } from '../../store/cartStore';
const IndexHeader = observer(() => {
const navigate = useNavigate();
return (
<>
<Box h="40px" borderBottom="1px solid" borderColor="#e0e3ea">
<Container maxW="1280px" h="full" px="6">
<Flex h="full" align="center" justify="space-between">
<HStack gap="6" color="#7a7f8c" fontSize="14px" fontWeight="500">
<Text>Частным клиентам</Text>
<Text color="#e30613">Бизнесу</Text>
<Text>Ещё</Text>
</HStack>
<HStack gap="6" color="#8a90a0" fontSize="13px" fontWeight="600">
<HStack gap="2">
<Icon as={LuMapPin} boxSize="3.5" />
<Text>Санкт-Петербург и Ленинградская область</Text>
</HStack>
<HStack gap="2">
<Icon as={LuPhone} boxSize="3.5" />
<Text>8 800 234-00-10</Text>
</HStack>
</HStack>
</Flex>
</Container>
</Box>
<Container maxW="1280px" px="6" pt="7">
<Flex align="center" justify="space-between" mb="7">
<HStack gap="3">
<Image
src={logo}
alt="МТС Бизнес"
width="50px"
cursor="pointer"
onClick={() => navigate('/')}
/>
<Text
fontFamily={'heading'}
fontSize={24}
cursor="pointer"
onClick={() => navigate('/')}
>
БИЗНЕС
</Text>
</HStack>
<Box position="relative">
<IconButton
aria-label="Корзина"
variant="outline"
size="lg"
borderRadius="full"
onClick={() => navigate('/cart')}
>
<LuShoppingCart />
</IconButton>
{cartStore.totalCount > 0 ? (
<Badge
position="absolute"
top="-2"
right="-2"
borderRadius="full"
bg="#e30613"
color="white"
minW="22px"
h="22px"
display="flex"
alignItems="center"
justifyContent="center"
fontSize="12px"
>
{cartStore.totalCount}
</Badge>
) : null}
</Box>
</Flex>
</Container>
</>
);
});
export default IndexHeader;

View File

@ -0,0 +1,62 @@
import { Box, Image, Text, VStack } from '@chakra-ui/react';
import heroImage from '~/assets/images/9.png';
const IndexHeroBanner = () => {
return (
<Box
bg="linear-gradient(108deg, #368cd5 0%, #255cb1 44%, #d9e3f7 100%)"
borderRadius="36px"
h="610px"
position="relative"
overflow="hidden"
p="10"
mb="12"
>
<VStack
align="start"
gap="8"
maxW="580px"
position="relative"
zIndex="1"
mt="10"
>
<VStack align="start" gap="4">
<Text
color="white"
fontSize="72px"
fontWeight="700"
lineHeight="1.04"
>
Тариф для M2M-устройств
</Text>
<Text color="whiteAlpha.900" fontSize="42px" fontWeight="700">
от 420 /год
</Text>
<Text
color="whiteAlpha.900"
fontSize="36px"
maxW="520px"
lineHeight="1.24"
>
Удобно планируйте расходы на трафик и обслуживание
</Text>
</VStack>
</VStack>
<Box
position="absolute"
right="10"
bottom="0"
w="44%"
h="84%"
display="flex"
alignItems="center"
justifyContent="center"
>
<Image src={heroImage} w="100%" h="100%" objectFit="contain" />
</Box>
</Box>
);
};
export default IndexHeroBanner;

View File

@ -0,0 +1,240 @@
import { Box, HStack, Image, Separator, Text, VStack } from '@chakra-ui/react';
import { observer } from 'mobx-react-lite';
import { useMemo, useState } from 'react';
import {
categories,
products,
type CategoryId,
type Product,
} from '../../data/products';
import { cartStore } from '../../store/cartStore';
import { toaster } from '../ui/toaster';
const formatPrice = (price: number) =>
new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
maximumFractionDigits: 0,
}).format(price);
const IndexProductsSection = observer(() => {
const [activeCategory, setActiveCategory] = useState<CategoryId>(
categories[0].id,
);
const activeCategoryLabel =
categories.find((category) => category.id === activeCategory)?.label ?? '';
const visibleProducts = useMemo(
() => products.filter((product) => product.categoryId === activeCategory),
[activeCategory],
);
const featuredProduct = visibleProducts[0];
const secondaryProducts = visibleProducts.slice(1, 3);
const addToCart = (product: Product) => {
cartStore.add(product);
toaster.create({
type: 'success',
title: 'Товар добавлен в корзину',
description: product.name,
closable: true,
});
};
return (
<>
<Box
bg="white"
borderRadius="34px"
border="1px solid"
borderColor="#e8ebf2"
px="7"
py="6"
mb="10"
>
<HStack gap="5" align="start" justify="space-between">
{categories.map((item) => (
<Box
as="button"
key={item.label}
onClick={() => setActiveCategory(item.id)}
minW="0"
flex="1"
pb="2"
cursor="pointer"
transition="all 0.2s ease"
>
<VStack alignItems="center" gap="3">
<Box boxSize="8" bg="white">
<Image
src={item.imageSrc}
alt={`TODO: category image ${item.label}`}
w="full"
h="full"
objectFit="contain"
/>
</Box>
<Text
fontSize="16px"
color={activeCategory === item.id ? '#1f2533' : '#7c8497'}
fontWeight="600"
lineHeight="1.25"
lineClamp={2}
>
{item.label}
</Text>
{activeCategory === item.id ? (
<Separator
borderColor="#e30613"
borderWidth="2px"
mt="2"
w="120px"
/>
) : (
<Box h="2px" />
)}
</VStack>
</Box>
))}
</HStack>
</Box>
<Box
bg="white"
borderRadius="34px"
border="1px solid"
borderColor="#e8ebf2"
p="8"
>
<HStack justify="space-between" align="center" mb="6">
<Text fontSize="38px" fontWeight="700" color="#1d2433">
{activeCategoryLabel}
</Text>
</HStack>
{!featuredProduct ? (
<Text fontSize="18px" color="#5f6b7e">
В этой категории пока нет товаров
</Text>
) : (
<VStack align="stretch" gap="6">
<Box
borderRadius="34px"
bg="#f3f5f9"
p="8"
minH="360px"
position="relative"
overflow="hidden"
>
<HStack
align="stretch"
gap="6"
h="full"
cursor="pointer"
onClick={() => addToCart(featuredProduct)}
>
<VStack align="start" gap="4" flex="1" maxW="54%">
<HStack align="start" justify="space-between" w="full">
<Text
fontSize="48px"
fontWeight="700"
color="#151c2e"
lineHeight="1.1"
>
{featuredProduct.name}
</Text>
</HStack>
<Text fontSize="38px" color="#58657a" lineHeight="1.25">
{featuredProduct.description}
</Text>
<Text
fontSize="34px"
color="#e30613"
fontWeight="800"
mt="auto"
>
{formatPrice(featuredProduct.price)}/мес
</Text>
</VStack>
<Box
flex="1"
display="flex"
alignItems="center"
justifyContent="center"
position="relative"
minH="280px"
>
<Image
src={featuredProduct.imageSrc}
alt={`TODO: product image ${featuredProduct.name}`}
w="100%"
h="100%"
objectFit="contain"
/>
</Box>
</HStack>
</Box>
{secondaryProducts.length > 0 ? (
<HStack align="stretch" gap="6">
{secondaryProducts.map((product) => (
<Box
key={product.id}
flex="1"
borderRadius="34px"
bg="#f3f5f9"
p="8"
minH="360px"
cursor="pointer"
onClick={() => addToCart(product)}
>
<VStack align="start" gap="4" h="full">
<Box
w="100%"
h="180px"
display="flex"
alignItems="center"
justifyContent="center"
position="relative"
>
<Image
src={product.imageSrc}
alt={`TODO: product image ${product.name}`}
w="100%"
h="100%"
objectFit="contain"
/>
</Box>
<HStack align="start" justify="space-between" w="full">
<Text
fontSize="46px"
fontWeight="700"
color="#151c2e"
lineHeight="1.1"
>
{product.name}
</Text>
</HStack>
<Text
fontSize="34px"
color="#58657a"
lineHeight="1.25"
flex="1"
>
{product.description}
</Text>
<Text fontSize="32px" color="#e30613" fontWeight="800">
{formatPrice(product.price)}/мес
</Text>
</VStack>
</Box>
))}
</HStack>
) : null}
</VStack>
)}
</Box>
</>
);
});
export default IndexProductsSection;

View File

@ -0,0 +1,14 @@
import { Provider } from '../ui/provider';
import { Outlet } from 'react-router';
import { Toaster } from '../ui/toaster';
const AppLayout = () => {
return (
<Provider defaultTheme="light" forcedTheme="light">
<Outlet />
<Toaster />
</Provider>
);
};
export default AppLayout;

View File

@ -0,0 +1,108 @@
'use client';
import type { IconButtonProps, SpanProps } from '@chakra-ui/react';
import { ClientOnly, IconButton, Skeleton, Span } from '@chakra-ui/react';
import { ThemeProvider, useTheme } from 'next-themes';
import type { ThemeProviderProps } from 'next-themes';
import * as React from 'react';
import { LuMoon, LuSun } from 'react-icons/lu';
export interface ColorModeProviderProps extends ThemeProviderProps {}
export function ColorModeProvider(props: ColorModeProviderProps) {
return (
<ThemeProvider attribute="class" disableTransitionOnChange {...props} />
);
}
export type ColorMode = 'light' | 'dark';
export interface UseColorModeReturn {
colorMode: ColorMode;
setColorMode: (colorMode: ColorMode) => void;
toggleColorMode: () => void;
}
export function useColorMode(): UseColorModeReturn {
const { resolvedTheme, setTheme, forcedTheme } = useTheme();
const colorMode = forcedTheme || resolvedTheme;
const toggleColorMode = () => {
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
};
return {
colorMode: colorMode as ColorMode,
setColorMode: setTheme,
toggleColorMode,
};
}
export function useColorModeValue<T>(light: T, dark: T) {
const { colorMode } = useColorMode();
return colorMode === 'dark' ? dark : light;
}
export function ColorModeIcon() {
const { colorMode } = useColorMode();
return colorMode === 'dark' ? <LuMoon /> : <LuSun />;
}
interface ColorModeButtonProps extends Omit<IconButtonProps, 'aria-label'> {}
export const ColorModeButton = React.forwardRef<
HTMLButtonElement,
ColorModeButtonProps
>(function ColorModeButton(props, ref) {
const { toggleColorMode } = useColorMode();
return (
<ClientOnly fallback={<Skeleton boxSize="9" />}>
<IconButton
onClick={toggleColorMode}
variant="ghost"
aria-label="Toggle color mode"
size="sm"
ref={ref}
{...props}
css={{
_icon: {
width: '5',
height: '5',
},
}}
>
<ColorModeIcon />
</IconButton>
</ClientOnly>
);
});
export const LightMode = React.forwardRef<HTMLSpanElement, SpanProps>(
function LightMode(props, ref) {
return (
<Span
color="fg"
display="contents"
className="chakra-theme light"
colorPalette="gray"
colorScheme="light"
ref={ref}
{...props}
/>
);
},
);
export const DarkMode = React.forwardRef<HTMLSpanElement, SpanProps>(
function DarkMode(props, ref) {
return (
<Span
color="fg"
display="contents"
className="chakra-theme dark"
colorPalette="gray"
colorScheme="dark"
ref={ref}
{...props}
/>
);
},
);

View File

@ -0,0 +1,13 @@
'use client';
import { ChakraProvider } from '@chakra-ui/react';
import { ColorModeProvider, type ColorModeProviderProps } from './color-mode';
import { system } from '../../theme';
export function Provider(props: ColorModeProviderProps) {
return (
<ChakraProvider value={system}>
<ColorModeProvider {...props} />
</ChakraProvider>
);
}

View File

@ -0,0 +1,43 @@
'use client';
import {
Toaster as ChakraToaster,
Portal,
Spinner,
Stack,
Toast,
createToaster,
} from '@chakra-ui/react';
export const toaster = createToaster({
placement: 'bottom-end',
pauseOnPageIdle: true,
});
export const Toaster = () => {
return (
<Portal>
<ChakraToaster toaster={toaster} insetInline={{ mdDown: '4' }}>
{(toast) => (
<Toast.Root width={{ md: 'sm' }}>
{toast.type === 'loading' ? (
<Spinner size="sm" color="blue.solid" />
) : (
<Toast.Indicator />
)}
<Stack gap="1" flex="1" maxWidth="100%">
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
{toast.description && (
<Toast.Description>{toast.description}</Toast.Description>
)}
</Stack>
{toast.action && (
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
)}
{toast.closable && <Toast.CloseTrigger />}
</Toast.Root>
)}
</ChakraToaster>
</Portal>
);
};

View File

@ -0,0 +1,46 @@
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
import * as React from 'react';
export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean;
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement | null>;
content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean;
}
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) {
const {
showArrow,
children,
disabled,
portalled = true,
content,
contentProps,
portalRef,
...rest
} = props;
if (disabled) return children;
return (
<ChakraTooltip.Root {...rest}>
<ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
<Portal disabled={!portalled} container={portalRef}>
<ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ref} {...contentProps}>
{showArrow && (
<ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip />
</ChakraTooltip.Arrow>
)}
{content}
</ChakraTooltip.Content>
</ChakraTooltip.Positioner>
</Portal>
</ChakraTooltip.Root>
);
},
);

211
src/data/products.ts Normal file
View File

@ -0,0 +1,211 @@
import catCloud from '~/assets/images/oblachnye_resheniya.svg';
import catCollab from '~/assets/images/sovmestnaya_rabota.svg';
import catGeo from '~/assets/images/geoservisy.svg';
import catIndustry from '~/assets/images/otraslevye_resheniya.svg';
import catIot from '~/assets/images/internet_veshey.svg';
import catMoney from '~/assets/images/dengi_i_raschety.svg';
import catSecurity from '~/assets/images/besopasnost.svg';
import catSvaz from '~/assets/images/svyaz_i_internet.svg';
import prod1 from '~/assets/images/1.png';
import prod10 from '~/assets/images/10.png';
import prod11 from '~/assets/images/11.png';
import prod12 from '~/assets/images/12.png';
import prod13 from '~/assets/images/13.png';
import prod14 from '~/assets/images/14.png';
import prod15 from '~/assets/images/15.svg';
import prod16 from '~/assets/images/Image_Online_Kassa_4edaa3279f.webp';
import prod17 from '~/assets/images/Image_mini_mobile_employees_small_ace7893009.webp';
import prod18 from '~/assets/images/Image_Nabor_servisov_dlya_optovoj_kompanii_Catalog_de32716b9c.webp';
import prod2 from '~/assets/images/2.png';
import prod3 from '~/assets/images/3.png';
import prod4 from '~/assets/images/4.png';
import prod5 from '~/assets/images/5.png';
import prod6 from '~/assets/images/6.png';
import prod7 from '~/assets/images/7.svg';
import prod8 from '~/assets/images/8.png';
import prod9 from '~/assets/images/9.png';
export const categories = [
{ id: 'connect', label: 'Связь и интернет', imageSrc: catSvaz },
{ id: 'iot', label: 'Интернет вещей M2M/IoT', imageSrc: catIot },
{ id: 'security', label: 'Безопасность', imageSrc: catSecurity },
{ id: 'cloud', label: 'Облачные решения', imageSrc: catCloud },
{ id: 'collab', label: 'Совместная работа', imageSrc: catCollab },
{ id: 'geo', label: 'Геосервисы', imageSrc: catGeo },
{ id: 'money', label: 'Деньги и расчёты', imageSrc: catMoney },
{ id: 'industry', label: 'Отраслевые решения', imageSrc: catIndustry },
] as const;
export type CategoryId = (typeof categories)[number]['id'];
export type Product = {
id: string;
name: string;
description: string;
price: number;
categoryId: CategoryId;
imageSrc: string;
};
export const products: Product[] = [
{
id: '2c2ebb74-7f0b-4c20-aaf3-f138f9755a46',
name: 'Виртуальная АТС',
description: 'Облачная телефония для бизнеса с гибкой настройкой',
price: 1500,
categoryId: 'connect',
imageSrc: prod1,
},
{
id: '7891394c-67bd-4102-aed5-264ef3d77509',
name: 'Корпоративная мобильная связь',
description: 'Тариф для сотрудников с пакетами минут и интернета',
price: 800,
categoryId: 'connect',
imageSrc: prod2,
},
{
id: 'fd3894b3-5074-4569-9404-74aac27f08a6',
name: 'Интернет для офиса',
description: 'Высокоскоростной проводной интернет',
price: 1200,
categoryId: 'connect',
imageSrc: prod3,
},
{
id: 'e555cc38-812a-4be0-b6f2-d5ffa4b4c66f',
name: 'Облачное хранилище',
description: 'Безопасное хранение и доступ к данным',
price: 950,
categoryId: 'cloud',
imageSrc: prod4,
},
{
id: '4d75904b-3484-4d75-bf91-97f109bcc7c8',
name: 'Видеонаблюдение',
description: 'Система удалённого видеоконтроля',
price: 2000,
categoryId: 'security',
imageSrc: prod5,
},
{
id: '08b69d44-b8e1-435d-a29f-4c3888609e78',
name: 'Кибербезопасность',
description: 'Комплексная защита IT-инфраструктуры',
price: 3000,
categoryId: 'security',
imageSrc: prod6,
},
{
id: '7429b33c-4492-4ece-b3f3-5e40418a4f50',
name: 'IoT платформа',
description: 'Управление устройствами интернета вещей',
price: 2700,
categoryId: 'iot',
imageSrc: prod7,
},
{
id: 'dcf3244b-0bda-4a1c-acde-1f689482cb15',
name: 'SMS-рассылки',
description: 'Сервис массовых уведомлений клиентам',
price: 500,
categoryId: 'collab',
imageSrc: prod8,
},
{
id: '54f1b970-5b5e-4e3d-9198-32594d4148f3',
name: 'Email-рассылки',
description: 'Маркетинговые email-кампании',
price: 400,
categoryId: 'collab',
imageSrc: prod9,
},
{
id: 'a5a82af8-f9a4-4882-95e0-bc640b27290c',
name: 'VPN для бизнеса',
description: 'Защищённый удалённый доступ сотрудников',
price: 1100,
categoryId: 'security',
imageSrc: prod10,
},
{
id: '10d13943-8cee-4c1e-8c1c-0e6fd392e7d8',
name: 'CRM система',
description: 'Управление клиентской базой и продажами',
price: 2200,
categoryId: 'collab',
imageSrc: prod11,
},
{
id: '9bfbe77d-2377-4f16-9d21-fe7e2cd51963',
name: 'Облачные серверы',
description: 'Виртуальные машины для бизнеса',
price: 3500,
categoryId: 'cloud',
imageSrc: prod12,
},
{
id: '8bd07d68-ba04-4530-9ad9-1ec67ad4d55d',
name: 'Резервное копирование',
description: 'Автоматический бэкап данных',
price: 1300,
categoryId: 'cloud',
imageSrc: prod13,
},
{
id: '5c645b56-9cc3-4e8e-9577-d87c4ffb35be',
name: 'IP-телефоны',
description: 'Оборудование для корпоративной связи',
price: 5000,
categoryId: 'connect',
imageSrc: prod14,
},
{
id: '9abdb5d7-00fe-4315-bae6-22bb5d92846a',
name: 'Контроль сотрудников',
description: 'Система мониторинга рабочего времени',
price: 1700,
categoryId: 'industry',
imageSrc: prod15,
},
{
id: 'f0d8a50d-3e42-4950-beed-19b26548d9e3',
name: 'Wi-Fi для бизнеса',
description: 'Организация корпоративной Wi-Fi сети',
price: 1400,
categoryId: 'connect',
imageSrc: prod16,
},
{
id: '0ee72e3a-ba58-4065-9cd2-2d2bd7349aba',
name: 'Аналитика данных',
description: 'Инструменты бизнес-аналитики',
price: 2600,
categoryId: 'geo',
imageSrc: prod17,
},
{
id: 'd0239808-4b78-4f91-be7f-464ca52eea74',
name: 'Онлайн-кассы',
description: 'Решение для приёма платежей',
price: 4800,
categoryId: 'money',
imageSrc: prod18,
},
{
id: '795d0e18-2531-4eed-941b-1c74ff48a72e',
name: 'Электронный документооборот',
description: 'Обмен документами в цифровом виде',
price: 900,
categoryId: 'collab',
imageSrc: prod1,
},
{
id: '2dede89c-c492-45ab-8082-8bbd0a19a4e7',
name: 'Хостинг сайтов',
description: 'Размещение и поддержка веб-сайтов',
price: 700,
categoryId: 'industry',
imageSrc: prod2,
},
];

11
src/env.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/// <reference types="@rsbuild/core/types" />
/**
* Imports the SVG file as a React component.
* @requires [@rsbuild/plugin-svgr](https://npmjs.com/package/@rsbuild/plugin-svgr)
*/
declare module '*.svg?react' {
import type React from 'react';
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}

14
src/index.tsx Normal file
View File

@ -0,0 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './styles/fonts.css';
const rootEl = document.getElementById('root');
if (rootEl) {
const root = ReactDOM.createRoot(rootEl);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
}

85
src/pages/CartPage.tsx Normal file
View File

@ -0,0 +1,85 @@
import { Box, Container } from '@chakra-ui/react';
import { observer } from 'mobx-react-lite';
import { useState } from 'react';
import { useNavigate } from 'react-router';
import CartContent from '../components/cart-page/CartContent';
import CartPageHeader from '../components/cart-page/CartPageHeader';
import CheckoutDialog, {
type CheckoutFormValues,
} from '../components/cart-page/CheckoutDialog';
import IndexHeader from '../components/index-page/IndexHeader';
import { toaster } from '../components/ui/toaster';
import { cartStore } from '../store/cartStore';
const CartPage = observer(() => {
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const submitOrder = async (values: CheckoutFormValues) => {
if (cartStore.itemsList.length === 0) {
toaster.create({
type: 'error',
title: 'Корзина пуста',
closable: true,
});
return;
}
try {
const payload = {
customerName: values.customerName.trim(),
phoneNumber: values.phoneNumber.trim(),
email: values.email.trim(),
items: cartStore.itemsList.map((item) => ({
productId: item.product.id,
quantity: item.quantity,
})),
};
const response = await fetch('http://localhost:8080/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
cartStore.clear();
setOpen(false);
toaster.create({
type: 'success',
title: 'Заказ оформлен',
closable: true,
});
} catch {
toaster.create({
type: 'error',
title: 'Не удалось оформить заказ',
description: 'Проверьте данные и попробуйте снова',
closable: true,
});
}
};
return (
<Box bg="#f2f3f7" minH="100vh" pb="20">
<IndexHeader />
<Container maxW="1280px" px="6">
<CartPageHeader onContinue={() => navigate('/')} />
<CartContent onCheckout={() => setOpen(true)} />
</Container>
<CheckoutDialog
open={open}
onOpenChange={setOpen}
onSubmit={submitOrder}
/>
</Box>
);
});
export default CartPage;

18
src/pages/IndexPage.tsx Normal file
View File

@ -0,0 +1,18 @@
import { Box, Container } from '@chakra-ui/react';
import IndexHeader from '../components/index-page/IndexHeader';
import IndexHeroBanner from '../components/index-page/IndexHeroBanner';
import IndexProductsSection from '../components/index-page/IndexProductsSection';
const IndexPage = () => {
return (
<Box bg="#f2f3f7" minH="100vh" pb="20">
<IndexHeader />
<Container maxW="1280px" px="6">
<IndexHeroBanner />
<IndexProductsSection />
</Container>
</Box>
);
};
export default IndexPage;

View File

@ -0,0 +1,63 @@
import { Box, Button, Container, Text, VStack } from '@chakra-ui/react';
import { useNavigate } from 'react-router';
import IndexHeader from '../components/index-page/IndexHeader';
const NotFoundPage = () => {
const navigate = useNavigate();
return (
<Box bg="#f2f3f7" minH="100vh" pb="20">
<IndexHeader />
<Container maxW="1280px" px="6">
<Box
bg="white"
border="1px solid"
borderColor="#e8ebf2"
borderRadius="34px"
p="12"
textAlign="center"
>
<VStack gap="5">
<Text
fontFamily="heading"
fontSize="92px"
fontWeight="700"
color="#1d2433"
lineHeight="1"
>
404
</Text>
<Text
fontFamily="heading"
fontSize="40px"
fontWeight="700"
color="#1d2433"
>
Страница не найдена
</Text>
<Text fontSize="24px" color="#5f6b7e" maxW="760px">
Возможно, ссылка устарела или страница была перемещена. Вернитесь
на главную и продолжите работу с сервисами МТС Бизнес.
</Text>
<Button
mt="2"
bg="#e30613"
color="white"
h="56px"
px="10"
borderRadius="14px"
fontSize="18px"
fontWeight="700"
_hover={{ bg: '#c90511' }}
onClick={() => navigate('/')}
>
На главную
</Button>
</VStack>
</Box>
</Container>
</Box>
);
};
export default NotFoundPage;

View File

@ -0,0 +1,53 @@
import { Box, Button, Container, Text, VStack } from '@chakra-ui/react';
import { useNavigate } from 'react-router';
import IndexHeader from '../components/index-page/IndexHeader';
const OrderReturnPage = () => {
const navigate = useNavigate();
return (
<Box bg="#f2f3f7" minH="100vh" pb="20">
<IndexHeader />
<Container maxW="1280px" px="6">
<Box
bg="white"
borderRadius="34px"
border="1px solid"
borderColor="#e8ebf2"
p="12"
textAlign="center"
>
<VStack gap="5">
<Text
fontFamily="heading"
fontSize="48px"
fontWeight="700"
color="#1d2433"
>
Оплата прошла успешно
</Text>
<Text fontSize="24px" color="#5f6b7e" maxW="780px">
Поздравляем, ваш заказ оплачен. Спасибо, что выбрали МТС Бизнес.
</Text>
<Button
mt="2"
bg="#e30613"
color="white"
h="56px"
px="10"
borderRadius="14px"
fontSize="18px"
fontWeight="700"
_hover={{ bg: '#c90511' }}
onClick={() => navigate('/')}
>
На главную
</Button>
</VStack>
</Box>
</Container>
</Box>
);
};
export default OrderReturnPage;

81
src/pages/PrivacyPage.tsx Normal file
View File

@ -0,0 +1,81 @@
import { Box, Container, List, Text, VStack } from '@chakra-ui/react';
import IndexHeader from '../components/index-page/IndexHeader';
const PrivacyPage = () => {
return (
<Box bg="#f2f3f7" minH="100vh" pb="20">
<IndexHeader />
<Container maxW="1280px" px="6">
<Box
bg="white"
borderRadius="24px"
border="1px solid"
borderColor="#e8ebf2"
p="8"
>
<VStack align="stretch" gap="5" color="#1d2433">
<Text fontSize="36px" fontWeight="700">
Согласие на обработку персональных данных
</Text>
<Text lineHeight="1.6">
Заполняя форму на сайте business.mts.ru (далее Форма),
Пользователь свободно, своей волей и в своем интересе
предоставляет ПАО «Мобильные ТелеСистемы» (ИНН 7740000076,
юридический адрес: 109147, г. Москва, ул. Марксистская, д. 4, стр.
1) (далее Оператор) согласие на обработку своих персональных
данных (далее ПДн). Пользователь также подтверждает, что
ознакомлен и согласен с Политикой обработки персональных данных.
</Text>
<Text lineHeight="1.6">Оператор обрабатывает ПДн в целях:</Text>
<List.Root ps="6" gap="2">
<List.Item>рассмотрения заявки;</List.Item>
<List.Item>информационного взаимодействия;</List.Item>
<List.Item>
проведения маркетинговых мероприятий Оператора и его партнёрам;
</List.Item>
<List.Item>
информирования об услугах и акциях, включая рассылку.
</List.Item>
</List.Root>
<Text lineHeight="1.6">
Согласие распространяется на ПДн, указанные в Форме, а также
полученные при дальнейшем взаимодействии, включая: ФИО, должность,
название компании, ИНН, регион, адрес, абонентский номер, адрес
электронной почты, иная информация, которая может быть
предоставлена Пользователем в полях «Комментарии» и «Загрузите
файлы».
</Text>
<Text lineHeight="1.6">
Оператор вправе обрабатывать ПДн с использованием средств
автоматизации и без таковых, включая следующие действия: сбор,
запись, систематизация, накопление, хранение, уточнение
(обновление, изменение), извлечение, использование, передачу
(предоставление, доступ), блокирование, удаление, уничтожение.
</Text>
<Text lineHeight="1.6">
Для указанных целей ПДн могут передаваться партнёрам Оператора.
</Text>
<Text lineHeight="1.6">
Согласие действует с момента предоставления до достижения целей
обработки ПДн.
</Text>
<Text lineHeight="1.6">
Пользователь вправе отозвать согласие в любой момент, направив
письменное заявление по адресу: 109147, г. Москва, ул.
Марксистская, д. 4, стр. 1.
</Text>
</VStack>
</Box>
</Container>
</Box>
);
};
export default PrivacyPage;

95
src/store/cartStore.ts Normal file
View File

@ -0,0 +1,95 @@
import { makeAutoObservable, reaction } from 'mobx';
import type { Product } from '../data/products';
export type CartItem = {
product: Product;
quantity: number;
};
class CartStore {
private static readonly STORAGE_KEY = 'mts-public-cart';
items = new Map<string, CartItem>();
constructor() {
makeAutoObservable(this);
this.hydrate();
reaction(
() =>
this.itemsList.map((item) => ({
product: item.product,
quantity: item.quantity,
})),
(items) => {
if (typeof window === 'undefined') return;
localStorage.setItem(CartStore.STORAGE_KEY, JSON.stringify(items));
},
);
}
private hydrate() {
if (typeof window === 'undefined') return;
const raw = localStorage.getItem(CartStore.STORAGE_KEY);
if (!raw) return;
try {
const parsed = JSON.parse(raw) as CartItem[];
this.items = new Map(
parsed
.filter((item) => item?.product?.id && item.quantity > 0)
.map((item) => [item.product.id, item]),
);
} catch {
localStorage.removeItem(CartStore.STORAGE_KEY);
}
}
add(product: Product) {
const existing = this.items.get(product.id);
if (existing) {
existing.quantity += 1;
return;
}
this.items.set(product.id, { product, quantity: 1 });
}
remove(productId: string) {
this.items.delete(productId);
}
increase(productId: string) {
const item = this.items.get(productId);
if (!item) return;
item.quantity += 1;
}
decrease(productId: string) {
const item = this.items.get(productId);
if (!item) return;
if (item.quantity <= 1) {
this.items.delete(productId);
return;
}
item.quantity -= 1;
}
clear() {
this.items.clear();
}
get itemsList() {
return Array.from(this.items.values());
}
get totalCount() {
return this.itemsList.reduce((acc, item) => acc + item.quantity, 0);
}
get totalPrice() {
return this.itemsList.reduce(
(acc, item) => acc + item.product.price * item.quantity,
0,
);
}
}
export const cartStore = new CartStore();

41
src/styles/fonts.css Normal file
View File

@ -0,0 +1,41 @@
@font-face {
font-family: 'MTS Compact';
src: url('../assets/fonts/MTSCompact_Regular-s.p.dc07201f.woff2')
format('woff2');
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: 'MTS Compact';
src: url('../assets/fonts/MTSCompact_Medium-s.p.07bd8ac4.woff2')
format('woff2');
font-style: normal;
font-weight: 500;
font-display: swap;
}
@font-face {
font-family: 'MTS Compact';
src: url('../assets/fonts/MTSCompact_Bold-s.p.f0b1dee4.woff2') format('woff2');
font-style: normal;
font-weight: 700;
font-display: swap;
}
@font-face {
font-family: 'MTS Wide';
src: url('../assets/fonts/MTSWide_Medium-s.p.5cfaeb8c.woff2') format('woff2');
font-style: normal;
font-weight: 500;
font-display: swap;
}
@font-face {
font-family: 'MTS Wide';
src: url('../assets/fonts/MTSWide_Bold-s.p.9358ad40.woff2') format('woff2');
font-style: normal;
font-weight: 700;
font-display: swap;
}

105
src/theme/index.ts Normal file
View File

@ -0,0 +1,105 @@
// theme.ts
import { createSystem, defaultConfig, defineConfig } from '@chakra-ui/react';
const config = defineConfig({
theme: {
tokens: {
colors: {
brand: {
50: { value: '#f5f2ff' },
100: { value: '#e9e3ff' },
200: { value: '#d1c6ff' },
300: { value: '#b39cff' },
400: { value: '#8f6cf7' },
500: { value: '#7a5af8' },
600: { value: '#6941c6' },
700: { value: '#53389e' },
800: { value: '#42307d' },
900: { value: '#2c1c5f' },
},
accent: {
50: { value: '#ffe5e8' },
100: { value: '#ffccd3' },
200: { value: '#ff99a8' },
300: { value: '#ff667d' },
400: { value: '#ff3352' },
500: { value: '#ff0033' },
600: { value: '#cc0029' },
700: { value: '#99001f' },
800: { value: '#660014' },
900: { value: '#33000a' },
},
gray: {
50: { value: '#f9fafb' },
100: { value: '#f2f4f7' },
200: { value: '#e4e7ec' },
300: { value: '#d0d5dd' },
400: { value: '#98a2b3' },
500: { value: '#667085' },
600: { value: '#475467' },
700: { value: '#344054' },
800: { value: '#1d2939' },
900: { value: '#101828' },
},
},
fonts: {
heading: {
value: "'MTS Wide', 'MTS Compact', Inter, system-ui, sans-serif",
},
body: { value: "'MTS Compact', Inter, system-ui, sans-serif" },
},
radii: {
sm: { value: '8px' },
md: { value: '12px' },
lg: { value: '16px' },
xl: { value: '24px' },
},
shadows: {
sm: { value: '0 1px 2px rgba(16,24,40,0.05)' },
md: { value: '0 4px 12px rgba(16,24,40,0.1)' },
lg: { value: '0 12px 24px rgba(16,24,40,0.12)' },
},
},
semanticTokens: {
colors: {
bg: {
DEFAULT: { value: '{colors.gray.50}' },
subtle: { value: '{colors.gray.100}' },
panel: { value: '#ffffff' },
gradient: {
value: 'linear-gradient(135deg, #7a5af8 0%, #b39cff 100%)',
},
},
fg: {
DEFAULT: { value: '{colors.gray.900}' },
muted: { value: '{colors.gray.500}' },
},
primary: {
solid: { value: '{colors.brand.500}' },
contrast: { value: '#ffffff' },
muted: { value: '{colors.brand.100}' },
},
danger: {
solid: { value: '{colors.accent.500}' },
contrast: { value: '#ffffff' },
},
border: {
DEFAULT: { value: '{colors.gray.200}' },
strong: { value: '{colors.gray.300}' },
},
},
},
},
});
export const system = createSystem(defaultConfig, config);

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"lib": ["DOM", "ES2020"],
"jsx": "react-jsx",
"target": "ES2020",
"noEmit": true,
"skipLibCheck": true,
"useDefineForClassFields": true,
/* modules */
"module": "ESNext",
"moduleDetection": "force",
"moduleResolution": "bundler",
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"noUncheckedSideEffectImports": true,
/* type checking */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"paths": {
"~/*": ["./src/*"]
}
},
"include": ["src"]
}