full app with server + data.json database
This commit is contained in:
parent
78be3fc67f
commit
cf45d0a038
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
.vibe42-run.log
|
||||||
|
.vibe42-run.pid
|
||||||
26
data.json
Normal file
26
data.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"firstName": "Алишер",
|
||||||
|
"lastName": "Нурсултанов",
|
||||||
|
"phone": "+7 (777) 123-45-67",
|
||||||
|
"telegram": "@alisher_ns",
|
||||||
|
"registeredAt": "2026-09-12T10:30:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"firstName": "Динара",
|
||||||
|
"lastName": "Кенжегулова",
|
||||||
|
"phone": "+7 (701) 234-56-78",
|
||||||
|
"telegram": "@dinara_kz",
|
||||||
|
"registeredAt": "2026-09-13T14:15:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"firstName": "Максат",
|
||||||
|
"lastName": "Ибрагимов",
|
||||||
|
"phone": "+7 (778) 345-67-89",
|
||||||
|
"telegram": "@maxat_dev",
|
||||||
|
"registeredAt": "2026-09-13T16:45:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
43
index.html
43
index.html
@ -75,6 +75,14 @@
|
|||||||
font-size: 48px;
|
font-size: 48px;
|
||||||
margin-bottom: var(--kt-ai-space-3);
|
margin-bottom: var(--kt-ai-space-3);
|
||||||
}
|
}
|
||||||
|
.error-message {
|
||||||
|
background: var(--kt-ai-bg-soft);
|
||||||
|
border: 1px solid var(--kt-ai-border);
|
||||||
|
border-radius: var(--kt-ai-radius-xl);
|
||||||
|
padding: var(--kt-ai-space-4);
|
||||||
|
color: var(--kt-ai-fg-muted);
|
||||||
|
margin-top: var(--kt-ai-space-4);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="kt-ai-app" data-kt-data-mode="demo">
|
<body class="kt-ai-app" data-kt-data-mode="demo">
|
||||||
@ -100,7 +108,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="kt-ai-hero-actions" style="justify-content:center">
|
<div class="kt-ai-hero-actions" style="justify-content:center">
|
||||||
<a class="kt-ai-btn" data-variant="primary" data-size="lg" href="#register">Зарегистрироваться</a>
|
<a class="kt-ai-btn" data-variant="primary" data-size="lg" href="#register">Зарегистрироваться</a>
|
||||||
<a class="kt-ai-btn" data-size="lg" href="#participants">Участники</a>
|
<a class="kt-ai-btn" data-size="lg" href="#about">О хакатоне</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -175,9 +183,10 @@
|
|||||||
<h3 style="margin-bottom:var(--kt-ai-space-2)">Регистрация успешна!</h3>
|
<h3 style="margin-bottom:var(--kt-ai-space-2)">Регистрация успешна!</h3>
|
||||||
<p style="color:var(--kt-ai-fg-muted)">Добро пожаловать на Hackathon Open 2026!</p>
|
<p style="color:var(--kt-ai-fg-muted)">Добро пожаловать на Hackathon Open 2026!</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="errorMessage" class="error-message" style="display:none"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;flex-direction:column;gap:var(--kt-ai-space-6)">
|
<div id="about" style="display:flex;flex-direction:column;gap:var(--kt-ai-space-6)">
|
||||||
<article class="kt-ai-tile">
|
<article class="kt-ai-tile">
|
||||||
<h3 style="margin-bottom:var(--kt-ai-space-2)">📅 Когда</h3>
|
<h3 style="margin-bottom:var(--kt-ai-space-2)">📅 Когда</h3>
|
||||||
<p style="color:var(--kt-ai-fg-muted)">15-17 ноября 2026, старт в пятницу 18:00</p>
|
<p style="color:var(--kt-ai-fg-muted)">15-17 ноября 2026, старт в пятницу 18:00</p>
|
||||||
@ -199,8 +208,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
||||||
<span>© 2026 Hackathon Open — Казахтелеком</span>
|
<span>© 2026 Hackathon Open — Казахтелеком</span>
|
||||||
<span class="kt-ai-spacer"></span>
|
<span class="kt-ai-spacer"></span>
|
||||||
@ -209,15 +216,14 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('registrationForm').addEventListener('submit', function(e) {
|
document.getElementById('registrationForm').addEventListener('submit', async function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
firstName: document.getElementById('firstName').value.trim(),
|
firstName: document.getElementById('firstName').value.trim(),
|
||||||
lastName: document.getElementById('lastName').value.trim(),
|
lastName: document.getElementById('lastName').value.trim(),
|
||||||
phone: document.getElementById('phone').value.trim(),
|
phone: document.getElementById('phone').value.trim(),
|
||||||
telegram: document.getElementById('telegram').value.trim(),
|
telegram: document.getElementById('telegram').value.trim()
|
||||||
registeredAt: new Date().toISOString()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!formData.firstName || !formData.lastName || !formData.phone || !formData.telegram) {
|
if (!formData.firstName || !formData.lastName || !formData.phone || !formData.telegram) {
|
||||||
@ -228,13 +234,28 @@ document.getElementById('registrationForm').addEventListener('submit', function(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const submissions = JSON.parse(localStorage.getItem('hackathonSubmissions') || '[]');
|
const successMsg = document.getElementById('successMessage');
|
||||||
submissions.push(formData);
|
const errorMsg = document.getElementById('errorMessage');
|
||||||
localStorage.setItem('hackathonSubmissions', JSON.stringify(submissions));
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/registrations', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка сервера: ' + response.status);
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('registrationForm').reset();
|
document.getElementById('registrationForm').reset();
|
||||||
document.getElementById('registrationForm').style.display = 'none';
|
document.getElementById('registrationForm').style.display = 'none';
|
||||||
document.getElementById('successMessage').style.display = 'block';
|
successMsg.style.display = 'block';
|
||||||
|
errorMsg.style.display = 'none';
|
||||||
|
} catch (err) {
|
||||||
|
errorMsg.textContent = 'Ошибка: ' + err.message + '. Попробуйте ещё раз.';
|
||||||
|
errorMsg.style.display = 'block';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
10
package-lock.json
generated
Normal file
10
package-lock.json
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "hackathon-registration",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "hackathon-registration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
package.json
Normal file
6
package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "hackathon-registration",
|
||||||
|
"private": true,
|
||||||
|
"type": "commonjs",
|
||||||
|
"scripts": { "start": "node server.js" }
|
||||||
|
}
|
||||||
65
server.js
Normal file
65
server.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
const http = require("http");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
const DATA = path.join(__dirname, "data.json");
|
||||||
|
|
||||||
|
function readItems() {
|
||||||
|
try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return []; }
|
||||||
|
}
|
||||||
|
function writeItems(items) {
|
||||||
|
fs.writeFileSync(DATA, JSON.stringify(items, null, 2));
|
||||||
|
}
|
||||||
|
function body(req) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let s = "";
|
||||||
|
req.on("data", (c) => (s += c));
|
||||||
|
req.on("end", () => { try { resolve(JSON.parse(s || "{}")); } catch (_) { resolve({}); } });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
http.createServer(async (req, res) => {
|
||||||
|
const urlPath = req.url.split("?")[0];
|
||||||
|
|
||||||
|
if (req.method === "GET" && urlPath === "/api/registrations") {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
return res.end(JSON.stringify(readItems()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === "POST" && urlPath === "/api/registrations") {
|
||||||
|
const item = await body(req);
|
||||||
|
const items = readItems();
|
||||||
|
const newItem = {
|
||||||
|
id: Date.now(),
|
||||||
|
firstName: item.firstName || "",
|
||||||
|
lastName: item.lastName || "",
|
||||||
|
phone: item.phone || "",
|
||||||
|
telegram: item.telegram || "",
|
||||||
|
registeredAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
items.push(newItem);
|
||||||
|
writeItems(items);
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
return res.end(JSON.stringify({ ok: true, data: newItem }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === "GET" && urlPath === "/api/stats") {
|
||||||
|
const items = readItems();
|
||||||
|
const today = new Date().toDateString();
|
||||||
|
const todayCount = items.filter(p => new Date(p.registeredAt).toDateString() === today).length;
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
return res.end(JSON.stringify({ total: items.length, today: todayCount }));
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = req.url === "/" ? "/index.html" : urlPath;
|
||||||
|
const full = path.join(__dirname, file);
|
||||||
|
if (full.startsWith(__dirname) && fs.existsSync(full) && fs.statSync(full).isFile()) {
|
||||||
|
const ext = path.extname(full);
|
||||||
|
const type = ext === ".css" ? "text/css" : ext === ".js" ? "application/javascript" : "text/html";
|
||||||
|
res.writeHead(200, { "Content-Type": type + "; charset=utf-8" });
|
||||||
|
return res.end(fs.readFileSync(full));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(404); res.end("Not found");
|
||||||
|
}).listen(PORT, () => console.log("Сервер запущен на порту " + PORT));
|
||||||
Loading…
Reference in New Issue
Block a user