1062 lines
48 KiB
HTML
1062 lines
48 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||
<meta name="theme-color" content="#ff6b6b">
|
||
<meta name="description" content="PetMap — карта помощи бродячим и потерянным животным Казахстана. Найди, спаси, расскажи.">
|
||
<title>PetMap — карта помощи животным</title>
|
||
|
||
<!-- PWA -->
|
||
<link rel="manifest" href="manifest.webmanifest">
|
||
<link rel="icon" type="image/png" sizes="192x192" href="icon-192.png">
|
||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||
<meta name="apple-mobile-web-app-title" content="PetMap">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||
<style>
|
||
:root {
|
||
--primary: #ff6b6b;
|
||
--primary-dark: #ee5a5a;
|
||
--secondary: #4ecdc4;
|
||
--accent: #ffe66d;
|
||
--text: #1a202c;
|
||
--text-muted: #718096;
|
||
--bg: #f5f7fa;
|
||
--card-bg: #ffffff;
|
||
--success: #2a9d8f;
|
||
--warning: #f4a261;
|
||
--danger: #e63946;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); overflow: hidden; }
|
||
|
||
/* TOP BAR */
|
||
.topbar {
|
||
position: fixed; top: 0; left: 0; right: 0;
|
||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||
color: white; padding: 12px 16px; z-index: 1000;
|
||
display: flex; align-items: center; gap: 16px;
|
||
box-shadow: 0 2px 12px rgba(0,0,0,0.15);
|
||
}
|
||
.logo { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 18px; }
|
||
.logo-emoji { font-size: 28px; }
|
||
.logo-text div:first-child { font-size: 18px; font-weight: 800; }
|
||
.logo-text .tagline { font-size: 11px; opacity: 0.9; font-weight: 400; }
|
||
|
||
.rescued-btn, .memorial-btn {
|
||
background: rgba(255,255,255,0.15); border: none; color: white;
|
||
padding: 8px 12px; border-radius: 10px; cursor: pointer;
|
||
display: flex; align-items: center; gap: 6px; font-size: 12px;
|
||
transition: all 0.3s;
|
||
}
|
||
.rescued-btn:hover, .memorial-btn:hover { background: rgba(255,255,255,0.25); transform: translateY(-1px); }
|
||
.rb-count, .mb-count {
|
||
background: var(--success); color: white;
|
||
padding: 2px 6px; border-radius: 10px; font-size: 11px; font-weight: 700;
|
||
}
|
||
.mb-count { background: #6c757d; }
|
||
|
||
.menu-btn {
|
||
margin-left: auto; background: rgba(255,255,255,0.15);
|
||
border: none; color: white; width: 40px; height: 40px;
|
||
border-radius: 10px; cursor: pointer; font-size: 20px;
|
||
transition: background 0.3s;
|
||
}
|
||
.menu-btn:hover { background: rgba(255,255,255,0.25); }
|
||
|
||
.city-selector {
|
||
background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3);
|
||
color: white; padding: 8px 12px; border-radius: 8px;
|
||
font-size: 13px; cursor: pointer; outline: none;
|
||
}
|
||
.city-selector option { background: var(--text); color: white; }
|
||
|
||
/* FILTER BAR */
|
||
.filter-bar {
|
||
position: fixed; top: 70px; left: 0; right: 0;
|
||
background: white; padding: 10px 12px; z-index: 999;
|
||
display: flex; gap: 6px; overflow-x: auto;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
}
|
||
.chip {
|
||
background: #f0f0f0; border: none; padding: 6px 12px;
|
||
border-radius: 16px; font-size: 12px; cursor: pointer;
|
||
white-space: nowrap; transition: all 0.3s;
|
||
display: flex; align-items: center; gap: 5px;
|
||
}
|
||
.chip:hover { background: #e0e0e0; }
|
||
.chip.active { background: var(--primary); color: white; }
|
||
.chip .dot {
|
||
width: 7px; height: 7px; border-radius: 50%;
|
||
background: var(--primary);
|
||
}
|
||
.chip .dot-vet { background: var(--success); }
|
||
.chip .dot-shelter { background: var(--warning); }
|
||
.chip .dot-petshop { background: var(--danger); }
|
||
|
||
/* MAP */
|
||
#map { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; }
|
||
|
||
/* FAB */
|
||
.fab {
|
||
position: fixed; bottom: 30px; right: 30px;
|
||
width: 60px; height: 60px; border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||
color: white; border: none; font-size: 30px;
|
||
cursor: pointer; box-shadow: 0 4px 20px rgba(255,107,107,0.4);
|
||
z-index: 1000; transition: all 0.3s;
|
||
}
|
||
.fab:hover { transform: scale(1.1); box-shadow: 0 6px 30px rgba(255,107,107,0.5); }
|
||
|
||
/* AI FAB */
|
||
.ai-fab {
|
||
position: fixed; bottom: 100px; right: 30px;
|
||
width: 48px; height: 48px; border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--secondary) 0%, #3dbdb5 100%);
|
||
color: white; border: none; font-size: 22px;
|
||
cursor: pointer; box-shadow: 0 4px 15px rgba(78,205,196,0.3);
|
||
z-index: 1000; transition: all 0.3s;
|
||
}
|
||
.ai-fab:hover { transform: scale(1.05); }
|
||
|
||
/* AI PANEL */
|
||
.ai-panel {
|
||
position: fixed; bottom: 160px; right: 30px;
|
||
width: 320px; max-height: 420px;
|
||
background: white; border-radius: 14px;
|
||
box-shadow: 0 8px 35px rgba(0,0,0,0.15);
|
||
z-index: 1001; display: flex; flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
.ai-panel[hidden] { display: none; }
|
||
.ai-panel-head {
|
||
background: linear-gradient(135deg, var(--secondary), #3dbdb5);
|
||
color: white; padding: 12px 14px;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
}
|
||
.ai-head-info { display: flex; align-items: center; gap: 8px; }
|
||
.ai-head-avatar { font-size: 20px; }
|
||
.ai-head-name { font-weight: 700; font-size: 13px; }
|
||
.ai-head-status { font-size: 10px; opacity: 0.9; }
|
||
.ai-panel-close {
|
||
background: rgba(255,255,255,0.2); border: none;
|
||
color: white; width: 26px; height: 26px; border-radius: 50%;
|
||
cursor: pointer; font-size: 16px;
|
||
}
|
||
.ai-messages {
|
||
flex: 1; overflow-y: auto; padding: 12px;
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
background: #f8f9fa;
|
||
}
|
||
.ai-msg {
|
||
max-width: 85%; padding: 10px 13px; border-radius: 12px;
|
||
font-size: 13px; line-height: 1.5;
|
||
}
|
||
.ai-msg.ai {
|
||
background: white; align-self: flex-start;
|
||
border-bottom-left-radius: 4px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||
}
|
||
.ai-msg.user {
|
||
background: var(--secondary); color: white;
|
||
align-self: flex-end; border-bottom-right-radius: 4px;
|
||
}
|
||
.ai-suggestions {
|
||
display: flex; gap: 6px; padding: 10px 12px;
|
||
overflow-x: auto; background: white;
|
||
border-top: 1px solid #e2e8f0;
|
||
}
|
||
.ai-sug {
|
||
background: #e8f8f6; color: var(--secondary);
|
||
padding: 6px 11px; border-radius: 16px;
|
||
font-size: 11px; white-space: nowrap; cursor: pointer;
|
||
border: 1px solid transparent; transition: all 0.3s;
|
||
}
|
||
.ai-sug:hover { border-color: var(--secondary); background: white; }
|
||
.ai-input-row {
|
||
display: flex; gap: 6px; padding: 10px 12px;
|
||
background: white; border-top: 1px solid #e2e8f0;
|
||
}
|
||
.ai-input-row input {
|
||
flex: 1; padding: 8px 12px; border: 1px solid #e2e8f0;
|
||
border-radius: 20px; font-size: 13px; outline: none;
|
||
}
|
||
.ai-input-row input:focus { border-color: var(--secondary); }
|
||
.ai-input-row button {
|
||
background: var(--secondary); color: white; border: none;
|
||
width: 36px; height: 36px; border-radius: 50%;
|
||
cursor: pointer; font-size: 14px;
|
||
}
|
||
.ai-input-row button:hover { background: #3dbdb5; }
|
||
|
||
/* DRAWER */
|
||
.drawer { position: fixed; inset: 0; z-index: 2000; }
|
||
.drawer[hidden] { display: none; }
|
||
.drawer-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.5); }
|
||
.drawer-panel {
|
||
position: absolute; left: 0; top: 0; bottom: 0;
|
||
width: 280px; background: white;
|
||
box-shadow: 4px 0 20px rgba(0,0,0,0.15);
|
||
display: flex; flex-direction: column;
|
||
}
|
||
.drawer-head {
|
||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||
color: white; padding: 18px 14px;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
}
|
||
.drawer-logo { font-size: 18px; font-weight: 800; }
|
||
.drawer-close {
|
||
background: rgba(255,255,255,0.2); border: none;
|
||
color: white; width: 30px; height: 30px; border-radius: 50%;
|
||
cursor: pointer; font-size: 18px;
|
||
}
|
||
.drawer-nav { flex: 1; padding: 14px 0; }
|
||
.drawer-nav button {
|
||
width: 100%; padding: 12px 16px; text-align: left;
|
||
background: none; border: none; font-size: 14px;
|
||
color: var(--text); cursor: pointer;
|
||
display: flex; align-items: center; gap: 10px;
|
||
transition: background 0.2s;
|
||
}
|
||
.drawer-nav button:hover { background: #f0f8f7; }
|
||
.drawer-footer {
|
||
padding: 16px; border-top: 1px solid #e2e8f0;
|
||
font-size: 11px; color: var(--text-muted); text-align: center;
|
||
}
|
||
|
||
/* PREVIEW BOTTOM SHEET */
|
||
.preview {
|
||
position: fixed; bottom: 0; left: 0; right: 0;
|
||
background: white; border-radius: 20px 20px 0 0;
|
||
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
|
||
z-index: 1002; transform: translateY(100%);
|
||
transition: transform 0.3s;
|
||
max-height: 65vh; overflow-y: auto;
|
||
}
|
||
.preview.open { transform: translateY(0); }
|
||
.preview-handle {
|
||
width: 40px; height: 4px; background: #cbd5e0;
|
||
border-radius: 2px; margin: 12px auto;
|
||
}
|
||
.preview-content { padding: 0 18px 18px; }
|
||
.preview-title { font-size: 18px; font-weight: 700; margin-bottom: 10px; }
|
||
.preview-meta { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
|
||
.preview-tag {
|
||
background: #f0f8f7; color: var(--secondary);
|
||
padding: 3px 9px; border-radius: 10px; font-size: 11px;
|
||
}
|
||
.preview-tag.cat-lost { background: #ffe5e5; color: var(--danger); }
|
||
.preview-tag.cat-found { background: #e8f8f6; color: var(--success); }
|
||
.preview-tag.cat-help { background: #fff4e5; color: var(--warning); }
|
||
.preview-desc { color: var(--text-muted); line-height: 1.6; margin-bottom: 14px; font-size: 14px; }
|
||
.preview-image {
|
||
width: 100%; height: 180px; background: #f0f0f0;
|
||
border-radius: 12px; margin-bottom: 14px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 60px;
|
||
}
|
||
.preview-contact {
|
||
background: #f8f9fa; padding: 10px; border-radius: 8px;
|
||
margin-bottom: 14px; font-size: 13px;
|
||
}
|
||
.preview-actions { display: flex; gap: 10px; }
|
||
.preview-btn {
|
||
flex: 1; padding: 11px; border: none; border-radius: 9px;
|
||
font-weight: 600; cursor: pointer; transition: all 0.3s;
|
||
font-size: 13px;
|
||
}
|
||
.preview-btn.primary { background: var(--primary); color: white; }
|
||
.preview-btn.primary:hover { background: var(--primary-dark); }
|
||
.preview-btn.secondary { background: #f0f0f0; color: var(--text); }
|
||
.preview-btn.secondary:hover { background: #e0e0e0; }
|
||
.preview-btn.call { background: var(--success); color: white; }
|
||
.preview-btn.call:hover { background: #218c7e; }
|
||
|
||
/* MODAL */
|
||
.modal {
|
||
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
|
||
z-index: 3000; display: none; justify-content: center;
|
||
align-items: center; padding: 20px;
|
||
}
|
||
.modal.open { display: flex; }
|
||
.modal-content {
|
||
background: white; border-radius: 14px;
|
||
max-width: 480px; width: 100%; max-height: 90vh;
|
||
overflow-y: auto; padding: 20px;
|
||
}
|
||
.modal-title { font-size: 20px; font-weight: 700; margin-bottom: 18px; }
|
||
.form-group { margin-bottom: 14px; }
|
||
.form-group label {
|
||
display: block; margin-bottom: 5px; font-weight: 600;
|
||
font-size: 13px; color: var(--text-muted);
|
||
}
|
||
.form-group input, .form-group textarea, .form-group select {
|
||
width: 100%; padding: 10px; border: 1px solid #e2e8f0;
|
||
border-radius: 8px; font-size: 13px; outline: none;
|
||
}
|
||
.form-group input:focus, .form-group textarea:focus { border-color: var(--primary); }
|
||
.form-actions { display: flex; gap: 10px; margin-top: 18px; }
|
||
|
||
/* TOAST */
|
||
.toast {
|
||
position: fixed; bottom: 30px; left: 50%;
|
||
transform: translateX(-50%) translateY(100px);
|
||
background: var(--text); color: white;
|
||
padding: 12px 20px; border-radius: 8px;
|
||
z-index: 5000; transition: transform 0.3s;
|
||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||
font-size: 14px;
|
||
}
|
||
.toast.show { transform: translateX(-50%) translateY(0); }
|
||
|
||
/* MASCOT - КОТЁНОК */
|
||
.mascot {
|
||
position: fixed; bottom: 15px; left: 15px;
|
||
width: 90px; height: 90px; z-index: 999;
|
||
cursor: pointer; transition: transform 0.3s;
|
||
}
|
||
.mascot:hover { transform: scale(1.05); }
|
||
.mascot-bubble {
|
||
position: absolute; bottom: 90px; left: 50%;
|
||
transform: translateX(-50%);
|
||
background: white; padding: 10px 14px;
|
||
border-radius: 14px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||
font-size: 12px; white-space: nowrap;
|
||
opacity: 0; transition: opacity 0.3s;
|
||
pointer-events: none;
|
||
}
|
||
.mascot:hover .mascot-bubble { opacity: 1; }
|
||
|
||
/* RUNNING CAT ANIMATION */
|
||
.kitty-runner {
|
||
position: fixed; width: 100px; height: 80px;
|
||
z-index: 998; pointer-events: none;
|
||
animation: runAcross 8s linear infinite;
|
||
}
|
||
.kitty-runner svg { width: 100%; height: 100%; }
|
||
@keyframes runAcross {
|
||
0% { left: -100px; bottom: 20px; transform: scaleX(1); }
|
||
45% { bottom: 20px; }
|
||
50% { left: 50%; bottom: 100px; transform: scaleX(1); }
|
||
55% { transform: scaleX(-1); }
|
||
100% { left: 100%; bottom: 20px; transform: scaleX(-1); }
|
||
}
|
||
|
||
/* RESCUED BOARD */
|
||
.rescued-board {
|
||
position: fixed; inset: 0; background: var(--bg);
|
||
z-index: 4000; overflow-y: auto;
|
||
display: none;
|
||
}
|
||
.rescued-board.open { display: block; }
|
||
.rb-header {
|
||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||
color: white; padding: 16px; position: sticky; top: 0;
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
.rb-back {
|
||
background: rgba(255,255,255,0.2); border: none;
|
||
color: white; width: 36px; height: 36px; border-radius: 50%;
|
||
cursor: pointer; font-size: 18px;
|
||
}
|
||
.rb-title { flex: 1; }
|
||
.rb-title-main { font-weight: 800; font-size: 16px; }
|
||
.rb-title-sub { font-size: 11px; opacity: 0.9; }
|
||
.rb-stats {
|
||
display: flex; gap: 12px; padding: 16px; overflow-x: auto;
|
||
}
|
||
.rb-stat {
|
||
background: white; padding: 14px 18px; border-radius: 12px;
|
||
text-align: center; min-width: 100px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
}
|
||
.rb-stat-num { font-size: 24px; font-weight: 800; color: var(--primary); }
|
||
.rb-stat-label { font-size: 11px; color: var(--text-muted); }
|
||
.rb-filters {
|
||
display: flex; gap: 8px; padding: 0 16px 12px; overflow-x: auto;
|
||
}
|
||
.rb-chip {
|
||
background: white; border: 1px solid #e2e8f0;
|
||
padding: 6px 12px; border-radius: 16px;
|
||
font-size: 12px; cursor: pointer; white-space: nowrap;
|
||
}
|
||
.rb-chip.active { background: var(--primary); color: white; border-color: var(--primary); }
|
||
.rb-grid {
|
||
display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 16px; padding: 16px;
|
||
}
|
||
.rb-card {
|
||
background: white; border-radius: 12px; overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||
transition: transform 0.3s;
|
||
}
|
||
.rb-card:hover { transform: translateY(-4px); }
|
||
.rb-card-img {
|
||
height: 160px; background: linear-gradient(135deg, #ffe5e5, #fff);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 60px;
|
||
}
|
||
.rb-card-body { padding: 14px; }
|
||
.rb-card-title { font-weight: 700; font-size: 15px; margin-bottom: 6px; }
|
||
.rb-card-desc { font-size: 13px; color: var(--text-muted); line-height: 1.5; }
|
||
|
||
/* MEMORIAL BOARD */
|
||
.memorial-page {
|
||
position: fixed; inset: 0; background: linear-gradient(180deg, #1a1a2e 0%, #2d2d44 100%);
|
||
z-index: 4000; overflow-y: auto; color: white;
|
||
display: none;
|
||
}
|
||
.memorial-page.open { display: block; }
|
||
.memorial-header {
|
||
padding: 16px; position: sticky; top: 0;
|
||
display: flex; align-items: center; gap: 12px;
|
||
background: rgba(26,26,46,0.95);
|
||
}
|
||
.memorial-new-btn {
|
||
margin-left: auto; background: rgba(255,255,255,0.15); border: none;
|
||
color: white; width: 36px; height: 36px; border-radius: 50%;
|
||
cursor: pointer; font-size: 20px;
|
||
}
|
||
.memorial-quote {
|
||
padding: 40px 20px; text-align: center;
|
||
background: rgba(255,255,255,0.05);
|
||
}
|
||
.memorial-candle { font-size: 48px; margin-bottom: 16px; }
|
||
.memorial-quote blockquote {
|
||
font-size: 18px; font-style: italic; margin-bottom: 12px;
|
||
opacity: 0.9;
|
||
}
|
||
.memorial-grid {
|
||
display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||
gap: 16px; padding: 16px;
|
||
}
|
||
.memorial-card {
|
||
background: rgba(255,255,255,0.08); border-radius: 12px;
|
||
padding: 16px; text-align: center;
|
||
}
|
||
.memorial-card-emoji { font-size: 50px; margin-bottom: 12px; }
|
||
.memorial-card-name { font-weight: 700; margin-bottom: 6px; }
|
||
.memorial-card-date { font-size: 12px; opacity: 0.7; }
|
||
|
||
@media (max-width: 768px) {
|
||
.topbar { padding: 10px 12px; }
|
||
.logo-text .tagline { display: none; }
|
||
.rescued-btn .rb-text, .memorial-btn .mb-text { display: none; }
|
||
.ai-panel { width: calc(100% - 30px); right: 15px; bottom: 160px; }
|
||
.fab { bottom: 20px; right: 20px; width: 54px; height: 54px; }
|
||
.ai-fab { bottom: 90px; right: 20px; }
|
||
.mascot { width: 70px; height: 70px; bottom: 12px; left: 12px; }
|
||
.rb-grid, .memorial-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- TOP BAR -->
|
||
<header class="topbar">
|
||
<div class="logo">
|
||
<span class="logo-emoji">🐾</span>
|
||
<div class="logo-text">
|
||
<div>PetMap</div>
|
||
<div class="tagline">Спасаем жизни животных</div>
|
||
</div>
|
||
</div>
|
||
<button class="rescued-btn" id="open-rescued">
|
||
<span class="rb-icon">🏆</span>
|
||
<span class="rb-text">Спасённые</span>
|
||
<span class="rb-count" id="rescued-count">0</span>
|
||
</button>
|
||
<button class="memorial-btn" id="open-memorial">
|
||
<span class="mb-icon">🕯</span>
|
||
<span class="mb-text">Память</span>
|
||
<span class="mb-count" id="memorial-count">0</span>
|
||
</button>
|
||
<select class="city-selector" id="city">
|
||
<option value="astana">Астана</option>
|
||
<option value="almaty">Алматы</option>
|
||
<option value="shymkent">Шымкент</option>
|
||
<option value="karaganda">Караганда</option>
|
||
<option value="aktobe">Актобе</option>
|
||
<option value="taraz">Тараз</option>
|
||
<option value="pavlodar">Павлодар</option>
|
||
<option value="uk">Усть-Каменогорск</option>
|
||
<option value="semey">Семей</option>
|
||
<option value="kostanay">Костанай</option>
|
||
</select>
|
||
<button class="menu-btn" id="open-menu" title="Меню">☰</button>
|
||
</header>
|
||
|
||
<!-- FILTER BAR -->
|
||
<div class="filter-bar">
|
||
<button class="chip chip-all active">Все</button>
|
||
<button class="chip" data-cat="lost"><span class="dot"></span>Потерян</button>
|
||
<button class="chip" data-cat="found"><span class="dot"></span>Нашёлся</button>
|
||
<button class="chip" data-cat="help"><span class="dot"></span>Нужна помощь</button>
|
||
<button class="chip" data-cat="home"><span class="dot"></span>Ищет дом</button>
|
||
<button class="chip" data-poi="vet"><span class="dot dot-vet"></span>Ветклиники</button>
|
||
<button class="chip" data-poi="shelter"><span class="dot dot-shelter"></span>Приюты</button>
|
||
<button class="chip" data-poi="petshop"><span class="dot dot-petshop"></span>Зоомагазины</button>
|
||
</div>
|
||
|
||
<!-- MAP -->
|
||
<div id="map"></div>
|
||
|
||
<!-- FAB -->
|
||
<button class="fab" id="fab" title="Добавить объявление">+</button>
|
||
|
||
<!-- AI FAB -->
|
||
<button class="ai-fab" id="ai-fab" title="ИИ-помощник">💬</button>
|
||
|
||
<!-- AI PANEL -->
|
||
<div class="ai-panel" id="ai-panel" hidden>
|
||
<header class="ai-panel-head">
|
||
<div class="ai-head-info">
|
||
<span class="ai-head-avatar">🐾</span>
|
||
<div>
|
||
<div class="ai-head-name">PetMap Помощник</div>
|
||
<div class="ai-head-status">Онлайн · отвечу за секунду</div>
|
||
</div>
|
||
</div>
|
||
<button class="ai-panel-close" id="ai-panel-close">×</button>
|
||
</header>
|
||
<div class="ai-messages" id="ai-messages">
|
||
<div class="ai-msg ai">
|
||
👋 Привет! Я помощник PetMap. Спроси меня:
|
||
<br>• Как помочь бездомному?
|
||
<br>• Где ближайшая ветклиника?
|
||
<br>• Как найти хозяина?
|
||
</div>
|
||
</div>
|
||
<div class="ai-suggestions" id="ai-suggestions">
|
||
<button class="ai-sug" data-sug="Как помочь бездомному?">Как помочь?</button>
|
||
<button class="ai-sug" data-sug="Где ветклиника?">Ветклиники</button>
|
||
<button class="ai-sug" data-sug="Как найти хозяина?">Найти дом</button>
|
||
</div>
|
||
<form class="ai-input-row" id="ai-form">
|
||
<input type="text" id="ai-input" placeholder="Спросите что угодно..." autocomplete="off">
|
||
<button type="submit">➤</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- DRAWER -->
|
||
<div class="drawer" id="drawer" hidden>
|
||
<div class="drawer-backdrop" id="drawer-backdrop"></div>
|
||
<aside class="drawer-panel">
|
||
<div class="drawer-head">
|
||
<div class="drawer-logo">🐾 PetMap</div>
|
||
<button class="drawer-close" id="drawer-close">×</button>
|
||
</div>
|
||
<nav class="drawer-nav">
|
||
<button data-nav="map">🗺️ Карта</button>
|
||
<button data-nav="rescued">🏆 Доска спасённых</button>
|
||
<button data-nav="memorial">🕯 Доска памяти</button>
|
||
<button data-nav="about">ℹ️ О приложении</button>
|
||
<button data-nav="install">📲 Установить</button>
|
||
</nav>
|
||
<div class="drawer-footer">
|
||
<div>PetMap © 2025</div>
|
||
<div>Сделано с ❤️ для животных Казахстана</div>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
|
||
<!-- PREVIEW BOTTOM SHEET -->
|
||
<div class="preview" id="preview">
|
||
<div class="preview-handle"></div>
|
||
<div class="preview-content">
|
||
<div class="preview-image" id="preview-image">🐕</div>
|
||
<h3 class="preview-title" id="preview-title">Название</h3>
|
||
<div class="preview-meta" id="preview-meta"></div>
|
||
<p class="preview-desc" id="preview-desc">Описание</p>
|
||
<div class="preview-contact" id="preview-contact"></div>
|
||
<div class="preview-actions">
|
||
<button class="preview-btn call" id="preview-whatsapp">💬 WhatsApp</button>
|
||
<button class="preview-btn primary" id="preview-share">📤 Поделиться</button>
|
||
<button class="preview-btn secondary" id="preview-close">Закрыть</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MODAL -->
|
||
<div class="modal" id="modal">
|
||
<div class="modal-content">
|
||
<h3 class="modal-title">➕ Добавить объявление</h3>
|
||
<form id="animal-form">
|
||
<div class="form-group">
|
||
<label>Тип объявления</label>
|
||
<select name="type">
|
||
<option value="lost">🔍 Потерян питомец</option>
|
||
<option value="found">👀 Нашёлся на улице</option>
|
||
<option value="help">🆘 Нужна помощь</option>
|
||
<option value="home">🏠 Ищет дом</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Вид животного</label>
|
||
<select name="animal">
|
||
<option value="dog">🐕 Собака</option>
|
||
<option value="cat">🐈 Кошка</option>
|
||
<option value="bird">🐦 Птица</option>
|
||
<option value="other">🐾 Другое</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Город</label>
|
||
<select name="city">
|
||
<option value="astana">Астана</option>
|
||
<option value="almaty">Алматы</option>
|
||
<option value="shymkent">Шымкент</option>
|
||
<option value="karaganda">Караганда</option>
|
||
<option value="aktobe">Актобе</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Кличка/Описание</label>
|
||
<input type="text" name="title" required placeholder="Бобик, рыжий, средний">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Где найден/потерян</label>
|
||
<input type="text" name="location" placeholder="Улица, район, ориентир">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Описание</label>
|
||
<textarea name="description" rows="3" placeholder="Окрас, возраст, особенности..."></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Контакт (телефон)</label>
|
||
<input type="tel" name="contact" required placeholder="+7 777 123 45 67">
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="button" class="preview-btn secondary" id="modal-close">Отмена</button>
|
||
<button type="submit" class="preview-btn primary">Опубликовать</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- RESCUED BOARD -->
|
||
<div class="rescued-board" id="rescued-board">
|
||
<header class="rb-header">
|
||
<button class="rb-back" id="rescued-back">←</button>
|
||
<div class="rb-title">
|
||
<div class="rb-title-main">🏆 Доска спасённых</div>
|
||
<div class="rb-title-sub">Истории успеха через PetMap</div>
|
||
</div>
|
||
</header>
|
||
<div class="rb-stats" id="rb-stats"></div>
|
||
<div class="rb-filters" id="rb-filters">
|
||
<button class="rb-chip active" data-filter="all">Все</button>
|
||
<button class="rb-chip" data-filter="home_found">🏠 Новый дом</button>
|
||
<button class="rb-chip" data-filter="owner_found">💚 К хозяину</button>
|
||
<button class="rb-chip" data-filter="treated">🩹 Вылечены</button>
|
||
</div>
|
||
<div class="rb-grid" id="rb-grid"></div>
|
||
</div>
|
||
|
||
<!-- MEMORIAL BOARD -->
|
||
<div class="memorial-page" id="memorial-page">
|
||
<header class="memorial-header">
|
||
<button class="rb-back" id="memorial-back">←</button>
|
||
<div class="rb-title">
|
||
<div class="rb-title-main">🕯 Доска памяти</div>
|
||
<div class="rb-title-sub">Тем, кого не успели спасти</div>
|
||
</div>
|
||
<button class="memorial-new-btn" id="memorial-new-btn">+</button>
|
||
</header>
|
||
<section class="memorial-quote">
|
||
<div class="memorial-candle">🕯</div>
|
||
<blockquote>«Зачем кому-то умирать, чтобы он нами был замечен?»</blockquote>
|
||
<p>Каждая история — напоминание не быть безразличным.</p>
|
||
</section>
|
||
<div class="memorial-grid" id="memorial-grid"></div>
|
||
</div>
|
||
|
||
<!-- TOAST -->
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<!-- MASCOT - КОТЁНОК -->
|
||
<div class="mascot" id="mascot" title="Котёнок-маскот">
|
||
<div class="mascot-bubble" id="mascot-bubble">Нажми + чтобы помочь! 🐾</div>
|
||
<svg viewBox="0 0 100 100">
|
||
<defs>
|
||
<radialGradient id="mg" cx="0.4" cy="0.4" r="0.7">
|
||
<stop offset="0%" stop-color="#ffb070"/>
|
||
<stop offset="100%" stop-color="#ff9a3c"/>
|
||
</radialGradient>
|
||
</defs>
|
||
<circle cx="50" cy="50" r="45" fill="url(#mg)"/>
|
||
<!-- Уши -->
|
||
<path d="M20 25 L25 10 L35 28 Z" fill="#ff9a3c"/>
|
||
<path d="M65 28 L75 10 L80 25 Z" fill="#ff9a3c"/>
|
||
<path d="M24 24 L27 16 L32 26 Z" fill="#ffb8c5"/>
|
||
<path d="M68 26 L73 16 L76 24 Z" fill="#ffb8c5"/>
|
||
<!-- Глаза -->
|
||
<ellipse cx="35" cy="45" rx="8" ry="10" fill="white"/>
|
||
<ellipse cx="65" cy="45" rx="8" ry="10" fill="white"/>
|
||
<ellipse cx="35" cy="46" rx="5" ry="7" fill="#1a202c"/>
|
||
<ellipse cx="65" cy="46" rx="5" ry="7" fill="#1a202c"/>
|
||
<circle cx="37" cy="43" r="2" fill="white"/>
|
||
<circle cx="67" cy="43" r="2" fill="white"/>
|
||
<!-- Нос -->
|
||
<path d="M47 58 Q50 61 53 58 Q53 56 50 55.5 Q47 56 47 58 Z" fill="#ff6b6b"/>
|
||
<!-- Рот -->
|
||
<path d="M50 60 Q47 63 44 62 M50 60 Q53 63 56 62" stroke="#1a202c" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
||
<!-- Щёки -->
|
||
<circle cx="25" cy="55" r="5" fill="#ff6b6b" opacity="0.3"/>
|
||
<circle cx="75" cy="55" r="5" fill="#ff6b6b" opacity="0.3"/>
|
||
<!-- Усы -->
|
||
<line x1="15" y1="52" x2="28" y2="53" stroke="#666" stroke-width="0.8"/>
|
||
<line x1="15" y1="56" x2="28" y2="56" stroke="#666" stroke-width="0.8"/>
|
||
<line x1="72" y1="53" x2="85" y2="52" stroke="#666" stroke-width="0.8"/>
|
||
<line x1="72" y1="56" x2="85" y2="56" stroke="#666" stroke-width="0.8"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- RUNNING CAT -->
|
||
<div class="kitty-runner" id="kitty-runner" hidden>
|
||
<svg viewBox="0 0 120 100">
|
||
<defs>
|
||
<radialGradient id="rfk-fur" cx="0.4" cy="0.4" r="0.75">
|
||
<stop offset="0%" stop-color="#ffd9b3"/>
|
||
<stop offset="65%" stop-color="#ffa755"/>
|
||
<stop offset="100%" stop-color="#ff7e1f"/>
|
||
</radialGradient>
|
||
</defs>
|
||
<!-- Тень -->
|
||
<ellipse cx="60" cy="94" rx="38" ry="3.5" fill="#000" opacity="0.18"/>
|
||
<!-- Хвост -->
|
||
<path d="M95 60 Q112 42 108 20" stroke="url(#rfk-fur)" stroke-width="12" fill="none" stroke-linecap="round"/>
|
||
<!-- Тело -->
|
||
<ellipse cx="62" cy="68" rx="26" ry="16" fill="url(#rfk-fur)"/>
|
||
<ellipse cx="62" cy="74" rx="18" ry="9" fill="#fff5e2"/>
|
||
<!-- Лапки -->
|
||
<ellipse cx="78" cy="86" rx="6" ry="5" fill="#ff9a3c"/>
|
||
<ellipse cx="68" cy="86" rx="6" ry="5" fill="#ff8a30"/>
|
||
<ellipse cx="44" cy="86" rx="6" ry="5" fill="#ff9a3c"/>
|
||
<ellipse cx="34" cy="86" rx="6" ry="5" fill="#ff8a30"/>
|
||
<!-- Голова -->
|
||
<path d="M6 36 L13 8 L24 32 Z" fill="url(#rfk-fur)"/>
|
||
<path d="M34 32 L42 8 L48 36 Z" fill="url(#rfk-fur)"/>
|
||
<circle cx="27" cy="48" r="24" fill="url(#rfk-fur)"/>
|
||
<!-- Глаза -->
|
||
<ellipse cx="17" cy="48" rx="6.5" ry="8" fill="white"/>
|
||
<ellipse cx="37" cy="48" rx="6.5" ry="8" fill="white"/>
|
||
<ellipse cx="17" cy="49" rx="4.5" ry="6" fill="#2a9d8f"/>
|
||
<ellipse cx="37" cy="49" rx="4.5" ry="6" fill="#2a9d8f"/>
|
||
<ellipse cx="17" cy="49" rx="2" ry="5" fill="#1a1a2e"/>
|
||
<ellipse cx="37" cy="49" rx="2" ry="5" fill="#1a1a2e"/>
|
||
<circle cx="18.5" cy="45" r="2" fill="white"/>
|
||
<circle cx="38.5" cy="45" r="2" fill="white"/>
|
||
<!-- Нос и рот -->
|
||
<path d="M24 58 Q27 61 30 58 Q30 56 27 56.5 Q24 56 24 58 Z" fill="#ff6b6b"/>
|
||
<path d="M27 60 Q24 63 21 62 M27 60 Q30 63 33 62" stroke="#1a1a2e" stroke-width="1.1" fill="none" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||
<script>
|
||
// ДАННЫЕ ЖИВОТНЫХ
|
||
const ANIMALS = [
|
||
{ id: 1, type: 'lost', animal: 'dog', city: 'astana', title: 'Бобик, лабрадор', location: 'район Арбата', description: 'Пропал 5 августа, ошейник синий', contact: '+7 777 111 22 33', emoji: '🐕' },
|
||
{ id: 2, type: 'found', animal: 'cat', city: 'almaty', title: 'Рыжий котёнок', location: 'ул. Абая, 45', description: 'Нашёлся у подъезда, около 3 месяцев', contact: '+7 701 234 56 78', emoji: '🐈' },
|
||
{ id: 3, type: 'help', animal: 'dog', city: 'astana', title: 'Собака ранена', location: 'проспект Туран', description: 'Нужна срочная помощь, не ходит на лапу', contact: '+7 778 999 88 77', emoji: '🆘' },
|
||
{ id: 4, type: 'home', animal: 'cat', city: 'shymkent', title: 'Мурка ищет дом', location: 'приют "Достык"', description: 'Стерилизована, привита, ласковая', contact: '+7 775 444 33 22', emoji: '🏠' },
|
||
{ id: 5, type: 'lost', animal: 'bird', city: 'karaganda', title: 'Попугай Кеша', location: 'микрорайон Юго-Восток', description: 'Говорит "привет", зелёный', contact: '+7 702 555 66 77', emoji: '🐦' },
|
||
];
|
||
|
||
// СПАСЁННЫЕ
|
||
const RESCUED = [
|
||
{ id: 1, title: 'Бобик нашёл дом', emoji: '🐕', outcome: 'home_found', desc: 'Забрала семья из Астаны' },
|
||
{ id: 2, title: 'Рыжик вернулся к хозяину', emoji: '🐈', outcome: 'owner_found', desc: 'Нашли по чипу' },
|
||
{ id: 3, title: 'Каспер вылечен', emoji: '🩹', outcome: 'treated', desc: 'Операция прошла успешно' },
|
||
{ id: 4, title: 'Луна в новой семье', emoji: '🐕', outcome: 'home_found', desc: 'Уехала в Алматы' },
|
||
];
|
||
|
||
// МЕМОРИАЛ
|
||
const MEMORIAL = [
|
||
{ id: 1, name: 'Рекс', emoji: '🐕', date: '2025' },
|
||
{ id: 2, name: 'Барсик', emoji: '🐈', date: '2024' },
|
||
{ id: 3, name: 'Кеша', emoji: '🐦', date: '2025' },
|
||
];
|
||
|
||
// ВЕТКЛИНИКИ, ПРИЮТЫ, ЗООМАГАЗИНЫ
|
||
const POIS = [
|
||
// АСТАНА
|
||
{ id: 101, type: 'vet', city: 'astana', title: 'Ветклиника "Айболит"', location: 'ул. Кенесары, 35', contact: '+7 7172 55 66 77', emoji: '🏥' },
|
||
{ id: 102, type: 'vet', city: 'astana', title: 'Ветцентр "ЗооДоктор"', location: 'пр. Туран, 42', contact: '+7 7172 66 77 88', emoji: '🏥' },
|
||
{ id: 103, type: 'shelter', city: 'astana', title: 'Приют "Достык"', location: 'п. Коктал-2', contact: '+7 777 888 99 00', emoji: '🏠' },
|
||
{ id: 104, type: 'petshop', city: 'astana', title: 'Зоомагазин "Лапки"', location: 'ЖК Нуржол, 12', contact: '+7 701 222 33 44', emoji: '🛒' },
|
||
|
||
// АЛМАТЫ
|
||
{ id: 105, type: 'vet', city: 'almaty', title: 'Ветцентр "ЗооМир"', location: 'пр. Абая, 120', contact: '+7 727 333 44 55', emoji: '🏥' },
|
||
{ id: 106, type: 'vet', city: 'almaty', title: 'Клиника "Верный друг"', location: 'ул. Сатпаева, 28', contact: '+7 727 444 55 66', emoji: '🏥' },
|
||
{ id: 107, type: 'shelter', city: 'almaty', title: 'Приют "Бауыр"', location: 'с. Байсерке', contact: '+7 701 222 33 44', emoji: '🏠' },
|
||
{ id: 108, type: 'petshop', city: 'almaty', title: 'Зообазар "4 Лапы"', location: 'ул. Толе би, 100', contact: '+7 777 333 22 11', emoji: '🛒' },
|
||
|
||
// ШЫМКЕНТ
|
||
{ id: 109, type: 'vet', city: 'shymkent', title: 'Ветклиника "Айболит-Юг"', location: 'ул. Калбекова, 15', contact: '+7 7252 55 66 77', emoji: '🏥' },
|
||
{ id: 110, type: 'shelter', city: 'shymkent', title: 'Приют "Мейрим"', location: 'мкр. Самал', contact: '+7 777 444 55 66', emoji: '🏠' },
|
||
{ id: 111, type: 'petshop', city: 'shymkent', title: 'Зоомагазин "Барбос"', location: 'ул. Рыскулова, 88', contact: '+7 778 555 44 33', emoji: '🛒' },
|
||
|
||
// КАРАГАНДА
|
||
{ id: 112, type: 'vet', city: 'karaganda', title: 'Ветклиника "Доктор Вет"', location: 'микрорайон Юго-Восток, 56/8', contact: '+7 7212 55 66 77', emoji: '🏥' },
|
||
{ id: 113, type: 'petshop', city: 'karaganda', title: 'Зоомагазин "Пушок"', location: 'ул. Ермекова, 30', contact: '+7 701 555 66 77', emoji: '🛒' },
|
||
|
||
// АКТОБЕ
|
||
{ id: 114, type: 'vet', city: 'aktobe', title: 'Ветцентр "Актобе Вет"', location: 'ул. Шерниязова, 22', contact: '+7 7132 55 66 77', emoji: '🏥' },
|
||
{ id: 115, type: 'shelter', city: 'aktobe', title: 'Приют "Үміт"', location: 'ул. Айтеке би, 45', contact: '+7 777 666 77 88', emoji: '🏠' },
|
||
|
||
// ТАРАЗ
|
||
{ id: 116, type: 'vet', city: 'taraz', title: 'Ветклиника "Тараз Вет"', location: 'ул. Байтурсынова, 18', contact: '+7 7262 55 66 77', emoji: '🏥' },
|
||
|
||
// ПАВЛОДАР
|
||
{ id: 117, type: 'vet', city: 'pavlodar', title: 'Ветклиника "Ертіс"', location: 'ул. Кривенко, 52', contact: '+7 7182 55 66 77', emoji: '🏥' },
|
||
{ id: 118, type: 'petshop', city: 'pavlodar', title: 'Зоомагазин "Хвостик"', location: 'пр. Ломова, 25', contact: '+7 701 111 22 33', emoji: '🛒' },
|
||
|
||
// УСТЬ-КАМЕНОГОРСК
|
||
{ id: 119, type: 'vet', city: 'uk', title: 'Ветклиника "Алтай Вет"', location: 'пр. Независимости, 62', contact: '+7 7232 55 66 77', emoji: '🏥' },
|
||
{ id: 120, type: 'shelter', city: 'uk', title: 'Приют "Сенім"', location: 'ул. Горького, 35', contact: '+7 777 888 99 00', emoji: '🏠' },
|
||
|
||
// СЕМЕЙ
|
||
{ id: 121, type: 'vet', city: 'semey', title: 'Ветклиника "Семей Вет"', location: 'ул. Абай, 45', contact: '+7 7222 55 66 77', emoji: '🏥' },
|
||
|
||
// КОСТАНАЙ
|
||
{ id: 122, type: 'vet', city: 'kostanay', title: 'Ветклиника "Қостанай Вет"', location: 'ул. Ауэзова, 28', contact: '+7 7142 55 66 77', emoji: '🏥' },
|
||
{ id: 123, type: 'petshop', city: 'kostanay', title: 'Зоомагазин "Барбос"', location: 'мкр. Восточный, 15', contact: '+7 701 777 88 99', emoji: '🛒' },
|
||
];
|
||
|
||
const CITIES = {
|
||
astana: { lat: 51.1694, lng: 71.4491, name: 'Астана' },
|
||
almaty: { lat: 43.2220, lng: 76.8512, name: 'Алматы' },
|
||
shymkent: { lat: 42.3000, lng: 69.6000, name: 'Шымкент' },
|
||
karaganda: { lat: 49.8047, lng: 73.1094, name: 'Караганда' },
|
||
aktobe: { lat: 50.2839, lng: 57.1670, name: 'Актобе' },
|
||
taraz: { lat: 42.9000, lng: 71.3667, name: 'Тараз' },
|
||
pavlodar: { lat: 52.2833, lng: 76.9667, name: 'Павлодар' },
|
||
uk: { lat: 49.9667, lng: 82.6000, name: 'Усть-Каменогорск' },
|
||
semey: { lat: 50.4111, lng: 80.2275, name: 'Семей' },
|
||
kostanay: { lat: 53.2144, lng: 63.6246, name: 'Костанай' }
|
||
};
|
||
|
||
// Карта
|
||
const map = L.map('map', { zoomControl: false }).setView([51.1694, 71.4491], 11);
|
||
L.control.zoom({ position: 'bottomright' }).addTo(map);
|
||
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
attribution: '© OpenStreetMap'
|
||
}).addTo(map);
|
||
|
||
const markers = [];
|
||
function addMarkers(filter = 'all') {
|
||
markers.forEach(m => map.removeLayer(m));
|
||
markers.length = 0;
|
||
|
||
const items = filter.startsWith('poi-') ? POIS : ANIMALS;
|
||
items.forEach(item => {
|
||
const itemType = item.type || item.cat;
|
||
if (filter !== 'all' && filter !== 'poi-all') {
|
||
if (filter.startsWith('poi-') && item.type !== filter.replace('poi-', '')) return;
|
||
if (!filter.startsWith('poi-') && itemType !== filter) return;
|
||
}
|
||
const city = CITIES[item.city];
|
||
if (!city) return;
|
||
|
||
const marker = L.marker([city.lat, city.lng]).addTo(map);
|
||
marker.bindPopup(`<b>${item.emoji} ${item.title}</b><br>${item.location}`);
|
||
marker.on('click', () => showPreview(item));
|
||
markers.push(marker);
|
||
});
|
||
|
||
document.getElementById('rescued-count').textContent = RESCUED.length;
|
||
document.getElementById('memorial-count').textContent = MEMORIAL.length;
|
||
}
|
||
|
||
addMarkers();
|
||
|
||
// Фильтры
|
||
document.querySelectorAll('.chip').forEach(chip => {
|
||
chip.addEventListener('click', () => {
|
||
document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
|
||
chip.classList.add('active');
|
||
const filter = chip.dataset.cat || chip.dataset.poi || 'all';
|
||
addMarkers(filter);
|
||
});
|
||
});
|
||
|
||
// Preview
|
||
const preview = document.getElementById('preview');
|
||
function showPreview(item) {
|
||
document.getElementById('preview-image').textContent = item.emoji;
|
||
document.getElementById('preview-title').textContent = item.title;
|
||
document.getElementById('preview-meta').innerHTML = `
|
||
<span class="preview-tag cat-${item.type}">${getTypeName(item.type)}</span>
|
||
<span class="preview-tag">${CITIES[item.city]?.name}</span>
|
||
`;
|
||
document.getElementById('preview-desc').textContent = item.description;
|
||
document.getElementById('preview-contact').textContent = `📞 ${item.contact}`;
|
||
|
||
// WhatsApp — сразу в чат
|
||
document.getElementById('preview-whatsapp').onclick = () => {
|
||
const phone = item.contact.replace(/[^0-9]/g, '');
|
||
const msg = encodeURIComponent(`Здравствуйте! По объявлению: ${item.title}`);
|
||
window.open(`https://wa.me/${phone}?text=${msg}`, '_blank');
|
||
};
|
||
|
||
document.getElementById('preview-share').onclick = () => {
|
||
if (navigator.share) {
|
||
navigator.share({ title: item.title, text: item.description });
|
||
} else {
|
||
showToast('📋 Скопировано в буфер');
|
||
}
|
||
};
|
||
preview.classList.add('open');
|
||
}
|
||
document.getElementById('preview-close').onclick = () => preview.classList.remove('open');
|
||
|
||
function getTypeName(type) {
|
||
const names = { lost: 'Потерян', found: 'Нашёлся', help: 'Нужна помощь', home: 'Ищет дом', vet: 'Ветклиника', shelter: 'Приют' };
|
||
return names[type] || type;
|
||
}
|
||
|
||
// Modal
|
||
const modal = document.getElementById('modal');
|
||
document.getElementById('fab').onclick = () => modal.classList.add('open');
|
||
document.getElementById('modal-close').onclick = () => modal.classList.remove('open');
|
||
document.getElementById('animal-form').onsubmit = (e) => {
|
||
e.preventDefault();
|
||
showToast('✅ Объявление опубликовано!');
|
||
modal.classList.remove('open');
|
||
};
|
||
|
||
// Drawer
|
||
const drawer = document.getElementById('drawer');
|
||
document.getElementById('open-menu').onclick = () => drawer.hidden = false;
|
||
document.getElementById('drawer-close').onclick = () => drawer.hidden = true;
|
||
document.getElementById('drawer-backdrop').onclick = () => drawer.hidden = true;
|
||
|
||
// AI Panel
|
||
const aiPanel = document.getElementById('ai-panel');
|
||
document.getElementById('ai-fab').onclick = () => aiPanel.hidden = false;
|
||
document.getElementById('ai-panel-close').onclick = () => aiPanel.hidden = true;
|
||
|
||
const aiMessages = document.getElementById('ai-messages');
|
||
const aiForm = document.getElementById('ai-form');
|
||
const aiInput = document.getElementById('ai-input');
|
||
|
||
aiForm.onsubmit = (e) => {
|
||
e.preventDefault();
|
||
const msg = aiInput.value.trim();
|
||
if (!msg) return;
|
||
aiMessages.innerHTML += `<div class="ai-msg user">${msg}</div>`;
|
||
aiInput.value = '';
|
||
setTimeout(() => {
|
||
const responses = {
|
||
'Как помочь бездомному?': '🐾 1. Покормите 2. Проверьте чип 3. Позвоните в приют 4. Добавьте объявление на карту',
|
||
'Где ветклиника?': '🏥 Выберите фильтр "Ветклиники" на карте — увидите все ближайшие клиники!',
|
||
'Как найти хозяина?': '🏠 1. Разместите объявление 2. Проверьте потерянных 3. Обратитесь в приюты 4. Используйте соцсети'
|
||
};
|
||
const answer = responses[msg] || 'Спасибо за вопрос! Я учусь. Попробуйте выбрать вариант из подсказок.';
|
||
aiMessages.innerHTML += `<div class="ai-msg ai">${answer}</div>`;
|
||
aiMessages.scrollTop = aiMessages.scrollHeight;
|
||
}, 500);
|
||
};
|
||
|
||
document.querySelectorAll('.ai-sug').forEach(btn => {
|
||
btn.onclick = () => { aiInput.value = btn.dataset.sug; aiForm.requestSubmit(); };
|
||
});
|
||
|
||
// Mascot
|
||
const mascot = document.getElementById('mascot');
|
||
const mascotBubble = document.getElementById('mascot-bubble');
|
||
let clickCount = 0;
|
||
mascot.onclick = () => {
|
||
clickCount++;
|
||
const messages = ['Нажми + чтобы помочь! 🐾', 'Каждое объявление спасает жизнь!', 'Спасибо, что неравнодушен! ❤️', 'Вместе мы можем больше! 🐾'];
|
||
mascotBubble.textContent = messages[clickCount % messages.length];
|
||
};
|
||
|
||
// Running cat occasionally
|
||
const kittyRunner = document.getElementById('kitty-runner');
|
||
setInterval(() => {
|
||
if (Math.random() > 0.7) {
|
||
kittyRunner.hidden = false;
|
||
setTimeout(() => { kittyRunner.hidden = true; }, 8000);
|
||
}
|
||
}, 30000);
|
||
|
||
// Toast
|
||
function showToast(msg) {
|
||
const toast = document.getElementById('toast');
|
||
toast.textContent = msg;
|
||
toast.classList.add('show');
|
||
setTimeout(() => toast.classList.remove('show'), 3000);
|
||
}
|
||
|
||
// Rescued Board
|
||
const rescuedBoard = document.getElementById('rescued-board');
|
||
document.getElementById('open-rescued').onclick = () => {
|
||
rescuedBoard.classList.add('open');
|
||
renderRescued('all');
|
||
};
|
||
document.getElementById('rescued-back').onclick = () => rescuedBoard.classList.remove('open');
|
||
|
||
function renderRescued(filter) {
|
||
const grid = document.getElementById('rb-grid');
|
||
const filtered = filter === 'all' ? RESCUED : RESCUED.filter(r => r.outcome === filter);
|
||
grid.innerHTML = filtered.map(r => `
|
||
<div class="rb-card">
|
||
<div class="rb-card-img">${r.emoji}</div>
|
||
<div class="rb-card-body">
|
||
<div class="rb-card-title">${r.title}</div>
|
||
<div class="rb-card-desc">${r.desc}</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
document.getElementById('rb-stats').innerHTML = `
|
||
<div class="rb-stat"><div class="rb-stat-num">${RESCUED.length}</div><div class="rb-stat-label">Всего спасено</div></div>
|
||
<div class="rb-stat"><div class="rb-stat-num">${RESCUED.filter(r => r.outcome === 'home_found').length}</div><div class="rb-stat-label">Нашли дом</div></div>
|
||
<div class="rb-stat"><div class="rb-stat-num">${RESCUED.filter(r => r.outcome === 'owner_found').length}</div><div class="rb-stat-label">К хозяину</div></div>
|
||
<div class="rb-stat"><div class="rb-stat-num">${RESCUED.filter(r => r.outcome === 'treated').length}</div><div class="rb-stat-label">Вылечены</div></div>
|
||
`;
|
||
}
|
||
|
||
document.querySelectorAll('.rb-chip').forEach(chip => {
|
||
chip.onclick = () => {
|
||
document.querySelectorAll('.rb-chip').forEach(c => c.classList.remove('active'));
|
||
chip.classList.add('active');
|
||
renderRescued(chip.dataset.filter);
|
||
};
|
||
});
|
||
|
||
// Memorial Board
|
||
const memorialPage = document.getElementById('memorial-page');
|
||
document.getElementById('open-memorial').onclick = () => {
|
||
memorialPage.classList.add('open');
|
||
renderMemorial();
|
||
};
|
||
document.getElementById('memorial-back').onclick = () => memorialPage.classList.remove('open');
|
||
|
||
function renderMemorial() {
|
||
document.getElementById('memorial-grid').innerHTML = MEMORIAL.map(m => `
|
||
<div class="memorial-card">
|
||
<div class="memorial-card-emoji">${m.emoji}</div>
|
||
<div class="memorial-card-name">${m.name}</div>
|
||
<div class="memorial-card-date">${m.date}</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// City selector
|
||
document.getElementById('city').onchange = (e) => {
|
||
const city = CITIES[e.target.value];
|
||
if (city) map.flyTo([city.lat, city.lng], 11);
|
||
};
|
||
</script>
|
||
</body>
|
||
</html>
|