Compare commits

..

18 Commits

Author SHA1 Message Date
Arseniy Sitnikov
8908d99245 Quick changes 2025-02-27 01:00:12 +03:00
Arseniy Sitnikov
9253069a07 Merge branch 'frontend' of github.com:Danya-Djan/db_kyc_project into frontend 2024-12-17 13:54:35 +03:00
Arseniy Sitnikov
a75e958944 im a dumb bobus 2024-12-17 13:54:05 +03:00
Arseniy Sitnikov
41bcc33175
Delete frontend/src/.DS_Store 2024-12-17 13:51:18 +03:00
Arseniy Sitnikov
17d46952c2
Delete frontend/.DS_Store 2024-12-17 13:51:08 +03:00
Arseniy Sitnikov
7be3835d57 added static images 2024-12-17 12:49:30 +03:00
Arseniy Sitnikov
22ca4b177c added auctions and rating 2024-12-17 11:16:46 +03:00
Arseniy Sitnikov
c8a3049349 add win popup 2024-12-13 15:26:27 +03:00
Arseniy Sitnikov
0cb8a5c7a9 fixed auctions and opened them 2024-12-13 12:17:17 +03:00
Arseniy Sitnikov
6ae11e83d6 auctions and bugs fixed about top imgs 2024-12-13 02:46:21 +03:00
Arseniy Sitnikov
07915ca426 add autoclick saving 2024-12-13 00:51:45 +03:00
Arseniy Sitnikov
51f4599031 fix token 2024-12-12 23:04:42 +03:00
Arseniy Sitnikov
16932959d5 autoclick add & add ranks 2024-12-12 23:01:22 +03:00
Arseniy Sitnikov
9e9fcc5f94 upd click request 2024-12-12 18:42:33 +03:00
Arseniy Sitnikov
86d3e9bbf4 fixes of bugs, loading animation, add max storage 2024-12-12 15:21:19 +03:00
Arseniy Sitnikov
26633b4b55 added energy, mult storage, button and profile | also fixed bugs 2024-12-11 05:02:21 +03:00
Arseniy Sitnikov
e9e0729a77 add clicks 2024-11-24 21:12:26 +03:00
Arseniy Sitnikov
4352fcd884 Initial frontend commit 2024-11-18 16:33:44 +03:00
270 changed files with 37149 additions and 169 deletions

22
.eslintrc.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: "plugin:react/recommended",
overrides: [],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["react", "@typescript-eslint"],
rules: {
"react-hooks/exhaustive-deps": [
"warn",
{
additionalHooks: "(useRecoilCallback|useRecoilTransaction_UNSTABLE)",
},
],
},
};

168
.gitignore vendored
View File

@ -1,169 +1 @@
.DS_Store .DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

22
frontend/.eslintrc.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: "plugin:react/recommended",
overrides: [],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["react", "@typescript-eslint"],
rules: {
"react-hooks/exhaustive-deps": [
"warn",
{
additionalHooks: "(useRecoilCallback|useRecoilTransaction_UNSTABLE)",
},
],
},
};

1
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/node_modules

12
frontend/Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM node:16.20.0
WORKDIR /opt/project
COPY package.json /opt/project
COPY package-lock.json /opt/project
RUN npm install
EXPOSE 3000
CMD [ "npm", "run", "dev" ]

75
frontend/bin/dev.js Normal file
View File

@ -0,0 +1,75 @@
const webpack = require("webpack");
const [webpackClientConfig, webpackServerConfig] = require("../webpack.config");
const nodemon = require("nodemon");
const path = require("path");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
const express = require("express");
const cors = require("cors");
const hmrServer = express();
const clientCompiler = webpack(webpackClientConfig);
const allowedOrigins = ["http://localhost:3000", "http://localhost:3001"];
hmrServer.use(
cors({
origin: function (origin, callback) {
// allow requests with no origin
// (like mobile apps or curl requests)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) === -1) {
var msg =
"The CORS policy for this site does not " +
"allow access from the specified Origin.";
return callback(new Error(msg), false);
}
return callback(null, true);
},
})
);
hmrServer.use(
webpackDevMiddleware(clientCompiler, {
publicPath: webpackClientConfig.output.publicPath,
serverSideRender: true,
noInfo: true,
watchOptions: {
ignore: /dist/,
},
writeToDisk: true,
stats: "errors-only",
})
);
hmrServer.use(
webpackHotMiddleware(clientCompiler, {
path: "/static/__webpack_hmr",
})
);
hmrServer.listen(3001, () => {
console.log("Hmr Server successfully started");
});
const compiler = webpack(webpackServerConfig);
compiler.run((err) => {
if (err) {
console.log(`compilation failed:`, err);
}
compiler.watch({}, (err) => {
if (err) {
console.log(`compilation failed:`, err);
}
console.log("Compilation was successfully");
});
nodemon({
script: path.resolve(__dirname, "../dist/server/server.js"),
watch: [
path.resolve(__dirname, "../dist/server"),
path.resolve(__dirname, "../dist/client"),
],
});
});

24
frontend/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Clicker"
/>
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="/fonts/">
<title>Clicker</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal_root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

30312
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

77
frontend/package.json Normal file
View File

@ -0,0 +1,77 @@
{
"name": "gpnevent",
"version": "1.0.0",
"description": "",
"main": "vite.config.js",
"type": "module",
"engines": {
"node": "16.x"
},
"scripts": {
"predeploy": "npm run build:dev",
"deploy": "gh-pages -d build:dev",
"build": "vite build",
"preview": "vite preview",
"dev": "vite serve",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Anna Efremova",
"license": "ISC",
"dependencies": {
"@hot-loader/react-dom": "^17.0.1",
"@redux-devtools/extension": "^3.2.5",
"@types/crypto-js": "^4.1.1",
"@types/intl-tel-input": "^18.1.1",
"@types/jest": "^28.1.6",
"@types/react": "^17.0.50",
"@types/react-dom": "^18.0.11",
"@types/react-linkify": "^1.0.4",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"@vitejs/plugin-react": "^4.0.4",
"axios": "^1.4.0",
"clean-webpack-plugin": "^4.0.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
"css-loader": "^3.4.2",
"eslint": "^8.40.0",
"eslint-plugin-react": "^7.32.2",
"express": "^4.17.1",
"file-loader": "^6.2.0",
"gh-pages": "^4.0.0",
"html-webpack-plugin": "^4.5.2",
"intl-tel-input": "^18.2.1",
"js-sha256": "^0.9.0",
"less": "^3.13.1",
"less-loader": "^5.0.0",
"nodemon": "^2.0.12",
"react": "^17.0.1",
"react-confetti": "^6.1.0",
"react-dom": "^17.0.1",
"react-hot-loader": "^4.13.0",
"react-linkify": "^1.0.0-alpha",
"react-player": "^2.12.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.11.1",
"react-select": "^5.7.3",
"redux": "^4.2.1",
"redux-devtools-extension": "^2.13.9",
"redux-thunk": "^2.4.2",
"style-loader": "^1.1.3",
"swiper": "^11.1.1",
"ts-jest": "^28.0.7",
"ts-loader": "^6.2.1",
"typescript": "4.6.4",
"usehooks-ts": "^3.1.0",
"vite": "^4.4.9",
"vite-plugin-html": "^3.2.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-middleware": "^3.7.3",
"webpack-dev-server": "^3.10.3",
"webpack-hot-middleware": "^2.25.0",
"webpack-node-externals": "^3.0.0"
}
}

BIN
frontend/public/.DS_Store vendored Normal file

Binary file not shown.

BIN
frontend/public/assets/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,15 @@
{
"short_name": "Clicker",
"name": "Clicker",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#222222",
"background_color": "#222222"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

46
frontend/src/App.tsx Normal file
View File

@ -0,0 +1,46 @@
import React, { useEffect, useState } from "react";
import './main.global.css';
import { hot } from "react-hot-loader/root";
import { Layout } from "./shared/Layout";
import { applyMiddleware, createStore } from "redux";
import { rootReducer } from "./store/reducer";
import { composeWithDevTools } from '@redux-devtools/extension';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { Route, Routes, BrowserRouter } from "react-router-dom";
import { AuctionMainPopups } from "./shared/Auction/AuctionMainPopups";
import { RoutePage } from "./shared/Pages/RoutePage";
const store = createStore(rootReducer, composeWithDevTools(
applyMiddleware(thunk)
));
function AppComponent() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<Provider store={store}>
{mounted && (<BrowserRouter>
<Layout>
<Routes>
<Route path='/' element={<RoutePage page='main' />} />
<Route path='/rating' element={<RoutePage page='rating' />} />
<Route path='/referral' element={<RoutePage page='referral' />} />
<Route path='/auction' element={<RoutePage page='auction' />} />
<Route path='/styles' element={<RoutePage page='styles' />} />
<Route path='/dev' element={<RoutePage page='dev' />} />
<Route path='*' element={<RoutePage page='main' />} />
</Routes>
<AuctionMainPopups />
</Layout>
</BrowserRouter>)}
</Provider>
)
};
export const App = hot(() => <AppComponent />);

BIN
frontend/src/assets/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

BIN
frontend/src/assets/dev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,7 @@
import * as React from "react";
import * as ReactDom from "react-dom";
import { App } from "../App";
window.addEventListener("load", () => {
ReactDom.hydrate(<App />, document.getElementById("main_root"));
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

10
frontend/src/index.tsx Normal file
View File

@ -0,0 +1,10 @@
import React from "react";
import ReactDOM from 'react-dom';
import { App } from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

View File

@ -0,0 +1,185 @@
@font-face {
font-family: 'Inter';
src: url('./fonts/Inter-Regular.woff2') format("woff2"),
url('./fonts/Inter-Regular.woff') format("woff");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('./fonts/Inter-SemiBold.woff2') format("woff2"),
url('./fonts/Inter-SemiBold.woff') format("woff");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('./fonts/Inter-Bold.woff2') format("woff2"),
url('./fonts/Inter-Bold.woff') format("woff");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('./fonts/Inter-ExtraBold.woff2') format("woff2"),
url('./fonts/Inter-ExtraBold.woff') format("woff");
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Raleway';
src: url('./fonts/Raleway-Regular.woff2') format("woff2"),
url('./fonts/Raleway-Regular.woff') format("woff");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Raleway';
src: url('./fonts/Raleway-SemiBold.woff2') format("woff2"),
url('./fonts/Raleway-SemiBold.woff') format("woff");
font-weight: 600;
font-style: normal;
font-display: swap;
}
:root {
--primary: #7EB4DB;
--gradientPrimary: linear-gradient(90deg, #90D7ED 13.05%, #6887C4 91.06%, #8085C0 172.24%);
--black: #000000;
--white: #FFFFFF;
--elBackround: #383838;
--textColor: #FFFFFF;
--textColor2: #8F8F8F;
--pink: #FC4848;
--red: #FF0000;
--blue: #7EB4DB;
--purple: #9747FF;
--gradientBlue: linear-gradient(90deg, #90D7ED 13.05%, #6887C4 91.06%, #8085C0 172.24%);
--gradientOrange: linear-gradient(302deg, #FF5421 -59.57%, #FF7248 43.7%, #FF9576 163.26%);
--gradientYellow: linear-gradient(302deg, #6ACB54 -59.57%, #DCBB5A 43.7%, #E2883D 163.26%);
--gradientOrangeYellow: linear-gradient(302deg, #FF805A -1.15%, #DEAE53 83.89%);
--grey6F: #6F6F6F;
--grey6C: #6C6C6C;
--grey35: #353535;
--grey34: #343434;
--grey1B: #1B1B1B;
--greyA4: #A4A4A4;
--grey46: #464646;
--grey12: #121212;
--grey19: #191919;
--grey77: #777777;
--grey24: #242424;
--grey22: #222222;
--grey9A: #9A9A9A;
--grey93: #939393;
--grey1F: #1F1F1F;
--grey27: #272727;
}
body {
padding: 0;
margin: 0;
font-size: 12px;
line-height: 120%;
font-family: 'Inter', sans-serif;
font-weight: 400;
box-sizing: border-box;
color: var(--textColor);
}
html {
scroll-behavior: smooth;
overflow-x: hidden;
}
p, h1, h2, h3 {
margin: 0;
padding: 0;
}
h1,
h2,
h3 {
font-weight: 700;
line-height: 120%;
}
* {
color: var(--textColor);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
a {
text-decoration: none;
}
input::placeholder {
white-space: pre-wrap;
}
button {
padding: 0;
border: 0;
background: transparent;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.main-header {
font-size: 65px;
line-height: 120%;
font-weight: 500;
}
#root {
height: 100%;
}
.background {
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 0;
}
/* slider */
.swiper-pagination-bullet {
margin: 0 2px !important;
height: 5px !important;
width: 5px !important;
background-color: var(--white) !important;
opacity: 1 !important;
}
.swiper-pagination-bullet-active {
width: 20px !important;
border-radius: 5px !important;
background: var(--gradientPrimary) !important;
}
.swiper-slide {
display: flex !important;
align-items: center;
justify-content: center;
}

View File

@ -0,0 +1,19 @@
export const indexTemplate = (content) => `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clicker</title>
<script src="/static/client.js" type="application/javascript"></script>
</head>
<body>
<div id="main_root">${content}</div>
<div id="modal_root"></div>
</body>
</html>
`;

View File

@ -0,0 +1,20 @@
import express from "express";
import ReactDOM from "react-dom/server";
import { App } from "../App";
import { indexTemplate } from "./indexTemplate";
import compression from 'compression';
const app = express();
app.use(compression());
app.use("/static", express.static("./dist/client"));
app.get("*", (req, res) => {
res.send(indexTemplate(ReactDOM.renderToString(App())));
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`server started on port ${port}`);
});

View File

@ -0,0 +1,79 @@
import React, { useState } from 'react';
import styles from './auctioncard.module.css';
import { ETextStyles } from '../../texts';
import { PointsBlock } from '../../Elements/PointsBlock';
import { Button } from '../../Button';
import { EIcons } from '../../Icons';
import { Timer } from '../Timer';
import { formatNumber } from '../../../utils/formatNumber';
import { Slider } from '../../Elements/Slider';
import { ModalWindow } from '../../ModalWindow';
import { AuctionPopup } from '../AuctionPopup';
import { ResultAuctionPopup } from '../ResultAuctionPopup';
import { DevPopup } from '../../Elements/DevPopup';
import { useAppSelector } from '../../hooks/useAppSelector';
interface IAuctionCard {
auctionId: string,
name: string,
imgs: Array<string>,
users: number,
prevBet: string,
myBetInit: string,
time: number,
isLead: boolean,
commission: number,
className ?: string
}
export function AuctionCard({ name, imgs, users, prevBet, myBetInit, time, isLead, commission, auctionId, className }: IAuctionCard) {
const [myBet, setBet] = useState(Number(myBetInit));
const [myNewBet, setMyNewBet] = useState(0);
const [initPrevBet, setPrevBet] = useState(prevBet);
const [initLead, setLead] = useState(isLead);
const [closeWindow, setClose] = useState(true);
const [closeAnim, setCloseAnim] = useState(false);
const [closeresultPopup, setCloseResultPopup] = useState(true);
const styleIndex = Number(localStorage.getItem('selectedStyle'));
const [closeErrorBet, setCloseErrorBet] = useState(true);
return (
<div className={`${styles.container} ${className} ${styleIndex===0 ? styles.darkContainer : styles.opacityContainer}`}>
<Slider className={styles.slider} imgs={imgs}/>
<h2 style={ETextStyles.InBd18120} className={styles.title}>{name}</h2>
<h3 style={ETextStyles.RwSb16120} className={styles.title2}>Подробности аукциона</h3>
<div className={`${styles.card} ${styles.cardFlex} ${styles.card1}`}>
<p style={ETextStyles.RwRg14100}>Минимальная ставка</p>
<PointsBlock points={initPrevBet} sizeIcon={20}/>
</div>
<div className={`${styles.card} ${styles.cardFlex} ${styles.card2}`}>
<p style={ETextStyles.RwRg14100}>Количество победителей</p>
<div className={styles.winnersNumber}>{users}</div>
</div>
<div className={`${styles.card} ${initLead && styles.leadCard}`}>
<div className={styles.cardTop}>
<div className={styles.cardLeft} style={ETextStyles.RwSb14120}>{initLead ? <p><span>Ты в числе победителей! </span>Но все может поменяться</p>
: <p>{myBet > 0 ? 'Вашу ставку перебили, повысьте ее, чтобы сохранить лидерство' : 'Успей сделать ставку! До конца осталось:' }</p> }</div>
<Timer initTime={time}/>
</div>
<Button onClick={() => setClose(false)} text={myBet === 0 ? 'Сделать первую ставку' : <div className={styles.newBtn}>
<p>Увеличить ставку</p>
<div className={styles.prevText}>
<p style={ETextStyles.InRg12140}>{`Предыдущая ставка — ${formatNumber(myBet)}`}</p>
<div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')"}}></div>
</div>
</div>}
icon={EIcons.UpPriceIcon}/>
</div>
{!closeWindow && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setClose} removeBtn={true} modalBlock={
<AuctionPopup myBet={myBet} setCloseErrorBet={setCloseErrorBet} auctionId={auctionId} commission={commission} setLead={setLead} setClose={setCloseAnim} img={imgs[0]} name={name} prevBet={initPrevBet} prevUserImg={''} setBet={setMyNewBet} setCloseResultPopup={setCloseResultPopup}/>
} />}
{!closeresultPopup && closeErrorBet && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setCloseResultPopup} removeBtn={true} modalBlock={
<ResultAuctionPopup prevBet={initPrevBet} prevMyBet={myBet} newBet={myNewBet} setBet={setBet} setClose={setCloseAnim} setCloseBetWindow={setClose} setPrevBet={setPrevBet}/>
} />}
{!closeErrorBet && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setCloseErrorBet} removeBtn={true} modalBlock={
<DevPopup setClose={setCloseAnim} title='Возникла ошибка' text='Не получилось сделать ставку. Но мы скоро всё починим.' />
} />}
</div>
);
}

View File

@ -0,0 +1,126 @@
.container {
padding: 6px;
border-radius: 20px;
box-shadow: 0px 0px 130px 0px rgba(124, 173, 216, 0.20);
}
.darkContainer {
background-color: var(--grey22);
}
.darkContainer .card {
background-color: var(--grey12);
}
.opacityContainer {
background: rgba(0, 0, 0, 0.4);
}
.opacityContainer .card {
background: rgba(0, 0, 0, 0.5);
}
.title {
margin-bottom: 18px;
}
.title2 {
margin-bottom: 8px;
}
.card {
padding: 15px 8px;
border-radius: 16px;
border: 1px solid var(--grey35);
}
.cardFlex {
display: flex;
gap: 5px;
justify-content: space-between;
align-items: center;
}
.card1 {
margin-bottom: 4px;
}
.card2 {
margin-bottom: 24px;
}
.cardTop {
margin-bottom: 25px;
display: flex;
align-items: center;
justify-content: space-between;
}
.cardLeft {
max-width: 187px;
}
.cardLeft p span {
color: var(--primary);
}
.usersBlock {
display: flex;
align-items: center;
}
.userCount {
margin-left: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--grey35);
border-radius: 50%;
width: 25px;
height: 25px;
}
.newBtn {
text-align: left;
}
.newBtn p {
color: var(--grey35);
}
.prevText {
display: flex;
align-items: center;
gap: 2px;
}
.icon {
width: 16px;
height: 16px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.leadCard {
box-shadow: 0px 0px 15px 0px rgba(122, 170, 214, 0.48);
border: 1px solid var(--primary);
}
.slider {
margin-bottom: 12px;
width: 100%;
aspect-ratio: 340/237;
border-radius: 15px;
overflow: hidden;
}
.winnersNumber {
display: flex;
align-items: center;
justify-content: center;
width: 27px;
height: 27px;
border-radius: 50%;
background-color: var(--grey34);
}

View File

@ -0,0 +1 @@
export * from './AuctionCard';

View File

@ -0,0 +1,39 @@
import React from 'react';
import styles from './auctionlosepopup.module.css';
import { useNavigate } from 'react-router-dom';
import { ETextStyles } from '../../texts';
import { ProductCard } from '../ProductCard';
import { Button } from '../../Button';
import { generateRandomString } from '../../../utils/generateRandom';
interface IProduct {
name: string,
img: string,
bet: string
}
interface IAuctionLosePopup {
items: Array<IProduct>,
setClose(a: boolean): void
}
export function AuctionLosePopup({ items, setClose }: IAuctionLosePopup) {
const navigate = useNavigate();
return (
<div>
<div className={styles.iconBlock}>
<div className={styles.icon} style={{ backgroundImage: "url('assets/Angry.png')" }}></div>
</div>
<h2 className={styles.title} style={ETextStyles.RwSb24100}>Вы больше не в топе...</h2>
<p className={styles.descr} style={ETextStyles.RwSb14120}>Чтобы сохранить лидерство, повысьте свою ставку в&nbsp;аукционе.</p>
<h3 className={styles.title2} style={ETextStyles.RwSb18120}>Аукционы, в&nbsp;которых нужно увеличить ставку:</h3>
<div className={styles.cards}>
{items.map(item => {
return <ProductCard key={ generateRandomString() } name={item.name} img={item.img} bet={item.bet} />
})}
</div>
<Button text='Увеличить ставку' onClick={() => { navigate('/auction'); setClose(true) }} />
</div>
);
}

View File

@ -0,0 +1,38 @@
.iconBlock {
margin-top: 20px;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.icon {
width: 64px;
height: 64px;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
.title {
margin-bottom: 12px;
text-align: center;
color: var(--primary);
}
.descr {
text-align: center;
margin-bottom: 32px;
}
.title2 {
margin-bottom: 12px;
text-align: center;
}
.cards {
margin-bottom: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}

View File

@ -0,0 +1 @@
export * from './AuctionLosePopup';

View File

@ -0,0 +1,96 @@
import React, { useEffect, useState } from 'react';
import styles from './auctionmainpopups.module.css';
import { ModalWindow } from '../../ModalWindow';
import { AuctionWinPopup } from '../AuctionWinPopup';
import { AuctionTopPopup } from '../AuctionTopPopup';
import { AuctionLosePopup } from '../AuctionLosePopup';
import { useAppSelector } from '../../hooks/useAppSelector';
import { IAuctionItem } from '../../../store/me/actions';
export function AuctionMainPopups() {
const [closeWin, setCloseWin] = useState(true);
const [closeTop, setCloseTop] = useState(true);
const [closeLose, setCloseLose] = useState(true);
const [closeAnim, setCloseAnim] = useState(false);
const topAuctions = useAppSelector<Array<IAuctionItem> | undefined>(state=>state.me.data.topAuctions);
const loseAuctions = useAppSelector<Array<IAuctionItem> | undefined>(state => state.me.data.loseAuctions);
const winAuctions = useAppSelector<Array<IAuctionItem> | undefined>(state => state.me.data.winAuctions);
const [winInfo, setWinInfo] = useState<IAuctionItem>();
useEffect(() => {
let showWindow = false;
if (winAuctions && winAuctions.length != 0) {
for (let i = 0; i < winAuctions.length; i++) {
const winShow = localStorage.getItem('wS');
if (winShow) {
const winArray = JSON.parse(winShow);
if (winArray && winArray.length != 0) {
let isExist = false;
for (let k = 0; k < winArray.length; k++) {
if (Number(winArray[k]) === Number(winAuctions[i].id)) {
isExist = true;
}
}
if(!isExist) {
winArray.push(winAuctions[i].id);
localStorage.setItem('wS', JSON.stringify(winArray));
showWindow = true;
setWinInfo(winAuctions[i]);
}
} else {
const newArray = [];
newArray.push(winAuctions[i].id);
localStorage.setItem('wS', JSON.stringify(newArray));
showWindow = true;
setWinInfo(winAuctions[i]);
}
} else {
const newArray = [];
newArray.push(winAuctions[i].id);
localStorage.setItem('wS', JSON.stringify(newArray));
showWindow = true;
setWinInfo(winAuctions[i]);
}
}
}
if(showWindow) {
setCloseWin(false);
}
}, [winAuctions]);
useEffect(() => {
const show = sessionStorage.getItem('shT');
if (show === 't' && closeTop) {
if (topAuctions && topAuctions.length != 0) {
sessionStorage.setItem('shT', 'f');
setCloseTop(false);
}
}
}, [topAuctions]);
useEffect(() => {
const show = sessionStorage.getItem('shL');
if (show === 't' && closeLose) {
if (loseAuctions && loseAuctions.length != 0) {
sessionStorage.setItem('shL', 'f');
setCloseLose(false);
}
}
}, [loseAuctions]);
return (
<div>
{!closeWin && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setCloseWin} removeBtn={true} modalBlock={
<AuctionWinPopup name={winInfo?.name ? winInfo?.name : ''} img={winInfo?.img ? winInfo?.img : ''} setClose={setCloseAnim}/>
} />}
{!closeTop && topAuctions != undefined && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setCloseTop} removeBtn={true} modalBlock={
<AuctionTopPopup items={topAuctions} setClose={setCloseAnim}/>
} />}
{!closeLose && loseAuctions != undefined && <ModalWindow closeAnimOut={closeAnim} setCloseAnimOut={setCloseAnim} setClose={setCloseLose} removeBtn={true} modalBlock={
<AuctionLosePopup items={loseAuctions} setClose={setCloseAnim} />
} />}
</div>
);
}

View File

@ -0,0 +1 @@
export * from './AuctionMainPopups';

View File

@ -0,0 +1,115 @@
import React, { useState } from 'react';
import styles from './auctionpopup.module.css';
import { ETextStyles } from '../../texts';
import { Button } from '../../Button';
import { EIcons } from '../../Icons';
import { declension } from '../../../utils/declension';
import { useNavigate } from 'react-router-dom';
import { ProductCard } from '../ProductCard';
import { useAppSelector } from '../../hooks/useAppSelector';
import { useDispatch } from 'react-redux';
import axios from 'axios';
import { updatePointsRequestAsync } from '../../../store/me/actions';
import { updateAuction } from '../../../store/auction/actions';
interface IAuctionPopup {
auctionId: string,
setClose(a: boolean): void,
setLead(a: boolean): void,
img: string,
name: string,
prevBet: string,
prevUserImg: string,
setBet(a: number): void,
setCloseResultPopup(a: boolean): void,
commission: number,
setCloseErrorBet(a: boolean): void,
myBet: number
}
export function AuctionPopup({ setClose, setCloseErrorBet, auctionId, img, name, prevBet, prevUserImg, setBet, setLead, setCloseResultPopup, commission, myBet }: IAuctionPopup) {
const [value, setValue] = useState<string>('');
const [disabled, setDis] = useState(true);
const [autoBet, setAutoBet] = useState(false);
const navigate = useNavigate();
const [percent, setPercent] = useState(commission);
const userPoints = Number(useAppSelector<string | undefined>(state=>state.me.data.points));
const URL = useAppSelector<string>(state=>state.url);
const token = useAppSelector<string>(state => state.token);
const dispatch = useDispatch();
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
let newValue = event.target.value;
newValue = newValue.replace(/[^0-9]/g, '');
setValue(newValue);
if (newValue.length != 0) {
setDis(false);
} else {
setDis(true);
}
}
const newBet = () => {
const bet = Number(value);
setClose(true);
if (token) {
axios.post(`${URL}/api/v1/auction/auction/${auctionId}/place-bet/?value=${bet}`, {},
{
headers: {
"Authorization": `TelegramToken ${token}`
}
}
).then(resp => {
const data = resp.data;
dispatch<any>(updatePointsRequestAsync());
dispatch<any>(updateAuction(auctionId));
setBet(bet);
//setLead(true);
const timer = setInterval(() => {
setCloseResultPopup(false);
clearTimeout(timer);
}, 400);
}).catch(err => {
setCloseErrorBet(false);
})
}
};
return (
<div>
<h2 className={styles.title} style={ETextStyles.RwSb24100}>Сделать ставку</h2>
<ProductCard name={name} img={img} bet={prevBet} personImg={prevUserImg} className={styles.card} />
{!autoBet ? <Button onClick={() => { setAutoBet(true), setValue(Number(Number((1 + percent / 100) * Number(prevBet)).toFixed(2)).toString()), setDis(false) }} text='Сразу перебить ставку' className={styles.btnFirst} icon={<div className={styles.icon} style={{ backgroundImage: "url('assets/Rocket.png')" }}></div>} /> :
<button style={ETextStyles.InBd14120} className={styles.btnCancel} onClick={() => setClose(true)}>Не перебивать</button>
}
<p className={styles.descr} style={ETextStyles.RwRg10140}>Наши алгоритмы автоматически рассчитают стоимость, чтобы ваша ставка стала самой высокой</p>
<h3 className={styles.title2} style={ETextStyles.InSb14120}>{!autoBet ? 'Ввести свою цену' : 'Цена, чтобы перебить ставку'}</h3>
<input style={ETextStyles.InSb14120} className={styles.input} value={value} type="text" onChange={handleChange} inputMode="numeric" />
{(Number(Number((1 + percent / 100) * Number(value))) - myBet < userPoints) ? ((Number(value) < Number(prevBet) && value.length > 0) ?
<button className={styles.btnForbidden}>
<p style={ETextStyles.InBd14120}>Ставка должна быть больше</p>
<p style={ETextStyles.InRg12140} className={styles.textForbidden}>Нельзя сделать ставку меньше предыдущей</p>
</button>
: <Button onClick={() => newBet()} disabled={disabled} text={disabled ? 'Перебить ставку' : <div className={styles.newBtn}>
<p>Перебить ставку</p>
<div className={styles.btnText}>
<p style={ETextStyles.InRg12140}>{`${declension(value, 'коин', 'коина', 'коинов', true)} + ${percent}% = ${declension(Number(Number((1 + percent / 100) * Number(value)).toFixed(2)), 'коин', 'коина', 'коинов', true)}`}</p>
<div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')" }}></div>
</div>
</div>}
icon={EIcons.UpPriceIcon} />
) :
<button className={styles.btnForbidden} onClick={() => navigate('/')}>
<p style={ETextStyles.InBd14120}>Тебе не хватает очков</p>
<div className={styles.btnText}>
<p style={ETextStyles.InRg12140} className={styles.textForbidden}>Нажми сюда, чтобы накопить еще</p>
<div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')" }}></div>
</div>
</button>
}
</div>
);
}

View File

@ -0,0 +1,95 @@
.title {
margin-bottom: 24px;
}
.card {
margin-bottom: 12px;
}
.btnFirst {
margin-bottom: 8px;
}
.icon {
width: 28px;
height: 28px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.descr {
margin-bottom: 24px;
color: var(--grey9A)
}
.title2 {
margin-bottom: 16px;
}
.input {
margin-bottom:16px;
padding: 6px;
width: 100%;
background-color: var(--grey22);
border: none;
border-bottom: 1px solid var(--grey93);
color: var(--white);
}
.input:focus {
outline: none;
border-bottom: 1px solid var(--primary);
}
.btnText {
text-align: left;
}
.newBtn {
text-align: left;
}
.newBtn p {
color: var(--grey35);
}
.btnText {
display: flex;
align-items: center;
gap: 2px;
}
.icon {
width: 16px;
height: 16px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.btnForbidden {
padding: 8px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 30px;
border: 2px solid var(--red);
}
.textForbidden {
opacity: 0.7;
}
.btnCancel {
margin-bottom: 8px;
padding: 14px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 30px;
border: 2px solid var(--primary);
}

View File

@ -0,0 +1 @@
export * from './AuctionPopup';

View File

@ -0,0 +1,39 @@
import React from 'react';
import styles from './auctiontoppopup.module.css';
import { ETextStyles } from '../../texts';
import { ProductCard } from '../ProductCard';
import { Button } from '../../Button';
import { useNavigate } from 'react-router-dom';
import { generateRandomString } from '../../../utils/generateRandom';
interface IProduct {
name: string,
img: string,
bet: string
}
interface IAuctionTopPopup {
items: Array<IProduct>,
setClose(a: boolean): void
}
export function AuctionTopPopup({ items, setClose }: IAuctionTopPopup) {
const navigate = useNavigate();
return (
<div className='top'>
<div className={styles.iconBlock}>
<div className={styles.icon} style={{ backgroundImage: "url('assets/Fire.png')" }}></div>
</div>
<h2 className={styles.title} style={ETextStyles.RwSb24100}>Вы в топе</h2>
<p className={styles.descr} style={ETextStyles.RwSb14120}>Кликайте, чтобы заработать больше очков и&nbsp;потратить их&nbsp;на&nbsp;ставку в&nbsp;аукционе.</p>
<h3 className={styles.title2} style={ETextStyles.RwSb18120}>Аукционы, в которых вы лидируете:</h3>
<div className={styles.cards}>
{items.map(item => {
return <ProductCard key={ generateRandomString() } name={item.name} img={item.img} bet={item.bet} />
})}
</div>
<Button text='Продолжить кликать' onClick={() => { navigate('/'); setClose(true)}}/>
</div>
);
}

View File

@ -0,0 +1,38 @@
.iconBlock {
margin-top: 20px;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.icon {
width: 64px;
height: 64px;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
.title {
margin-bottom: 12px;
text-align: center;
color: var(--primary);
}
.descr {
text-align: center;
margin-bottom: 32px;
}
.title2 {
margin-bottom: 12px;
text-align: center;
}
.cards {
margin-bottom: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}

Some files were not shown because too many files have changed in this diff Show More