From 4058a1636d1b247b4fa03ee83551f1db0f0d4945 Mon Sep 17 00:00:00 2001 From: Dana Medical Developer Date: Wed, 14 Oct 2026 13:33:13 +0000 Subject: [PATCH] chore: initial commit of dana-medical production state --- .deploy/dana-medical.caddy | 74 + .deploy/remote-build-start-dana.sh | 33 + .deploy/remote-init-dana.sh | 38 + .deploy/remote-install-dana-caddy.sh | 40 + .deploy/remote-restore-dana.sh | 15 + .deploy/remote-secure-dana.sh | 12 + .deploy/remote-smoke-dana.sh | 49 + .deploy/remote-start-dana-db.sh | 21 + .deploy/remote-update-dana-caddy.sh | 35 + .deploy/remote-verify-dana-preview.sh | 34 + .dockerignore | 13 + .env.example | 19 + .gitignore | 24 + Dockerfile | 28 + cmd/server/main.go | 140 + deploy.env.example | 24 + docker-compose.prod.yml | 53 + docker-compose.yml | 44 + go.mod | 17 + go.sum | 30 + internal/api/admin.go | 318 +++ internal/api/admin_banners.go | 161 ++ internal/api/admin_banners_test.go | 41 + internal/api/admin_checkups.go | 147 + internal/api/admin_doctors.go | 206 ++ internal/api/admin_documents.go | 230 ++ internal/api/admin_documents_test.go | 65 + internal/api/admin_inbox.go | 198 ++ internal/api/admin_media.go | 120 + internal/api/admin_partners.go | 119 + internal/api/admin_partners_test.go | 55 + internal/api/admin_posts.go | 174 ++ internal/api/admin_posts_test.go | 87 + internal/api/admin_promos.go | 205 ++ internal/api/admin_promos_test.go | 92 + internal/api/admin_redirects.go | 143 + internal/api/admin_redirects_test.go | 23 + internal/api/admin_reports.go | 436 +++ internal/api/admin_services.go | 49 + internal/api/admin_site.go | 317 +++ internal/api/admin_site_test.go | 70 + internal/api/admin_specialties.go | 153 ++ internal/api/admin_specialties_test.go | 52 + internal/api/admin_stats.go | 67 + internal/api/admin_stats_test.go | 27 + internal/api/admin_vacancies.go | 124 + internal/api/admin_weekly.go | 281 ++ internal/api/adminui.go | 18 + internal/api/adminui/admin.css | 1275 +++++++++ internal/api/adminui/assets/README.txt | 4 + internal/api/adminui/assets/channels/2gis.png | Bin 0 -> 4236 bytes .../api/adminui/assets/channels/SOURCES.md | 19 + .../api/adminui/assets/channels/doctu.png | Bin 0 -> 6622 bytes .../api/adminui/assets/channels/doctu.svg | 28 + .../api/adminui/assets/channels/google.png | Bin 0 -> 1319 bytes .../api/adminui/assets/channels/instagram.png | Bin 0 -> 17025 bytes internal/api/adminui/assets/channels/max.png | Bin 0 -> 18115 bytes .../adminui/assets/channels/napopravku.png | Bin 0 -> 1251 bytes .../assets/channels/ochevidno-site.png | Bin 0 -> 10503 bytes .../adminui/assets/channels/prodoctorov.png | Bin 0 -> 1031 bytes internal/api/adminui/assets/channels/sber.png | Bin 0 -> 1506 bytes .../api/adminui/assets/channels/telegram.png | Bin 0 -> 4642 bytes internal/api/adminui/assets/channels/vk.png | Bin 0 -> 6400 bytes .../api/adminui/assets/channels/website.svg | 11 + .../api/adminui/assets/channels/yandex.png | Bin 0 -> 4300 bytes internal/api/adminui/assets/channels/zoon.png | Bin 0 -> 4787 bytes internal/api/adminui/assets/hall.webp | Bin 0 -> 221606 bytes internal/api/adminui/assets/heart.webp | Bin 0 -> 477438 bytes internal/api/adminui/assets/lobby.webp | Bin 0 -> 256800 bytes internal/api/adminui/banners.js | 189 ++ internal/api/adminui/clinic.js | 32 + internal/api/adminui/dashboard.js | 289 ++ internal/api/adminui/doctors-dash.js | 236 ++ internal/api/adminui/doctors.js | 366 +++ ...errat-alternates-cyrillic-300-normal.woff2 | Bin 0 -> 12948 bytes ...errat-alternates-cyrillic-400-normal.woff2 | Bin 0 -> 13200 bytes ...errat-alternates-cyrillic-500-normal.woff2 | Bin 0 -> 13368 bytes ...errat-alternates-cyrillic-600-normal.woff2 | Bin 0 -> 13356 bytes ...ntserrat-alternates-latin-300-normal.woff2 | Bin 0 -> 21604 bytes ...ntserrat-alternates-latin-400-normal.woff2 | Bin 0 -> 21916 bytes ...ntserrat-alternates-latin-500-normal.woff2 | Bin 0 -> 21824 bytes ...ntserrat-alternates-latin-600-normal.woff2 | Bin 0 -> 21972 bytes internal/api/adminui/index.html | 2427 +++++++++++++++++ internal/api/adminui/leads.css | 151 + internal/api/adminui/leads.js | 154 ++ internal/api/adminui/onec.css | 27 + internal/api/adminui/onec.js | 281 ++ internal/api/adminui/pbx.js | 453 +++ internal/api/adminui/reports-history.css | 134 + internal/api/adminui/reports-history.js | 158 ++ internal/api/adminui/reports.js | 515 ++++ internal/api/adminui/services.js | 278 ++ internal/api/adminui/share.html | 56 + internal/api/adminui/share.js | 60 + internal/api/adminui/shell.js | 316 +++ internal/api/adminui/stats.js | 225 ++ internal/api/adminui/weekly-experiment.css | 71 + internal/api/adminui/weekly-experiment.js | 102 + internal/api/adminui/weekly.js | 521 ++++ internal/api/api.go | 327 +++ internal/api/booking_confirm.go | 405 +++ internal/api/booking_confirm_test.go | 106 + internal/api/clientip_test.go | 37 + internal/api/clinics.go | 118 + internal/api/clinics_test.go | 116 + internal/api/handlers.go | 88 + internal/api/handlers_content.go | 143 + internal/api/handlers_leads.go | 207 ++ internal/api/handlers_leads_test.go | 32 + internal/api/handlers_site.go | 57 + internal/api/legacy.go | 160 ++ internal/api/media_webp.go | 62 + internal/api/media_webp_test.go | 49 + internal/api/notify.go | 38 + internal/api/onec.go | 644 +++++ internal/api/onec_webhook.go | 135 + internal/api/onec_webhook_test.go | 60 + internal/api/pbx.go | 669 +++++ internal/api/pbx_test.go | 112 + internal/api/phones_parse.go | 207 ++ internal/api/ratelimit.go | 71 + internal/api/redact.go | 17 + internal/api/report_bitrix.go | 114 + internal/api/report_partner_uploads.go | 235 ++ internal/api/report_partner_uploads_test.go | 181 ++ internal/api/report_phones.go | 758 +++++ internal/api/report_phones_test.go | 318 +++ internal/api/report_prodoctorov.go | 145 + .../api/report_prodoctorov_sample_test.go | 42 + internal/api/report_prodoctorov_test.go | 139 + internal/api/report_sberhealth.go | 141 + internal/api/report_sberhealth_large_test.go | 49 + internal/api/report_sberhealth_test.go | 148 + internal/api/security_pack_test.go | 127 + internal/api/share.go | 103 + internal/api/ssr.go | 400 +++ internal/api/ssr_test.go | 83 + internal/api/tables.go | 372 +++ internal/api/testdata/render-fixture.mjs | 7 + internal/api/video.go | 61 + internal/api/video_test.go | 34 + internal/api/waitcall.go | 343 +++ internal/api/waitcall.go.root-backup | 327 +++ internal/api/waitcall_test.go | 87 + internal/api/website.go | 435 +++ internal/api/website_test.go | 101 + internal/bitrix/client.go | 134 + internal/bitrix/leads.go | 153 ++ internal/bitrix/leads_test.go | 96 + internal/bitrix/redact_test.go | 25 + internal/config/config.go | 187 ++ internal/domain/content.go | 157 ++ internal/domain/domain.go | 120 + internal/domain/notice.go | 22 + internal/domain/site.go | 42 + internal/megafon/client.go | 226 ++ internal/megafon/client_test.go | 65 + internal/notify/email.go | 207 ++ internal/notify/email_test.go | 166 ++ internal/onec/client.go | 297 ++ internal/onec/client_test.go | 93 + internal/prodoctorov/client.go | 111 + internal/prodoctorov/client_test.go | 145 + internal/sberhealth/biff.go | 470 ++++ internal/sberhealth/biff_test.go | 290 ++ internal/sberhealth/client.go | 135 + internal/sberhealth/client_test.go | 157 ++ internal/sberhealth/xls.go | 34 + internal/storage/admin.go | 120 + internal/storage/admin_checkups.go | 159 ++ internal/storage/admin_content.go | 221 ++ internal/storage/admin_doctors.go | 248 ++ internal/storage/admin_documents.go | 94 + internal/storage/admin_faq.go | 79 + internal/storage/admin_inbox.go | 240 ++ internal/storage/admin_partners.go | 78 + internal/storage/admin_posts.go | 107 + internal/storage/admin_promos.go | 116 + internal/storage/admin_services.go | 45 + internal/storage/admin_site.go | 181 ++ internal/storage/admin_specialties.go | 124 + internal/storage/admin_vacancies.go | 90 + internal/storage/banners.go | 152 ++ internal/storage/booking_confirm.go | 179 ++ internal/storage/booking_confirm_test.go | 22 + internal/storage/conflict.go | 27 + internal/storage/content.go | 367 +++ internal/storage/dashboard.go | 142 + internal/storage/doctor_quality.go | 79 + internal/storage/doctors.go | 322 +++ internal/storage/lead_notice.go | 43 + internal/storage/leads.go | 106 + internal/storage/media.go | 33 + internal/storage/migrations/0001_core.sql | 203 ++ internal/storage/migrations/0002_content.sql | 150 + .../storage/migrations/0003_leads_admin.sql | 108 + .../storage/migrations/0004_seed_forms.sql | 51 + .../storage/migrations/0005_review_video.sql | 12 + internal/storage/migrations/0006_mockup.sql | 32 + internal/storage/migrations/0007_legal.sql | 23 + .../migrations/0008_legal_new_site.sql | 10 + .../migrations/0009_promo_checkup_admin.sql | 15 + .../migrations/0010_banner_layouts.sql | 10 + internal/storage/migrations/0011_company.sql | 166 ++ .../storage/migrations/0012_documents.sql | 33 + .../storage/migrations/0013_review_dates.sql | 63 + .../migrations/0014_doctor_bio_text.sql | 75 + .../storage/migrations/0015_patients_page.sql | 9 + .../storage/migrations/0016_checkup_kra.sql | 12 + internal/storage/migrations/0017_reports.sql | 24 + internal/storage/migrations/0018_pbx.sql | 41 + .../storage/migrations/0019_doctor_photos.sql | 69 + internal/storage/migrations/0020_onec.sql | 71 + .../migrations/0021_weekly_reports.sql | 18 + .../0022_doctor_rating_override.sql | 5 + .../migrations/0023_booking_confirm.sql | 16 + internal/storage/migrations/0024_clinics.sql | 20 + .../storage/migrations/0025_share_links.sql | 10 + internal/storage/onec.go | 589 ++++ internal/storage/pbx.go | 180 ++ internal/storage/price_sections.go | 32 + internal/storage/prices.go | 81 + internal/storage/public_paths.go | 32 + internal/storage/redirects.go | 122 + internal/storage/reports.go | 125 + internal/storage/search.go | 146 + internal/storage/services.go | 156 ++ internal/storage/share.go | 55 + internal/storage/stats.go | 211 ++ internal/storage/storage.go | 105 + internal/storage/weekly.go | 166 ++ migrations/import/README.md | 21 + migrations/import/build_price_map.py | 193 ++ migrations/import/dedup_and_label.py | 92 + migrations/import/export_content.py | 224 ++ migrations/import/export_dir_icons.py | 72 + migrations/import/export_doctor_bio.py | 87 + migrations/import/export_doctor_experience.py | 47 + migrations/import/export_media.py | 117 + migrations/import/export_seed.py | 229 ++ migrations/import/find_bio_glitches.py | 34 + migrations/import/fix_blocks_rn.py | 51 + migrations/import/fix_review_texts.py | 39 + migrations/import/make_webp.py | 67 + .../import/rebuild_price_from_1c_only.py | 73 + migrations/import/sqlread.py | 93 + seo-structure.md | 707 +++++ seo/DEPLOYMENT.md | 57 + seo/analyze.mjs | 118 + seo/cluster-analysis.json | 1746 ++++++++++++ seo/clusters_exact.csv | 161 ++ seo/content-audit.json | 365 +++ seo/content-gaps.md | 133 + seo/crawl-audit.json | 38 + seo/service-profiles.json | 8 + seo/site-profiles.json | 10 + web/index.html | 16 + web/package-lock.json | 2074 ++++++++++++++ web/package.json | 27 + web/public/apple-touch-icon.png | Bin 0 -> 4538 bytes .../doctors/abakarova-patimat-alievna.png | Bin 0 -> 184182 bytes .../abdullaev-magomed-abdullaevich.png | Bin 0 -> 201508 bytes .../abdurakhmanova-zaliyat-abdulaevna.png | Bin 0 -> 147776 bytes .../doctors/afanaseva-elena-fyedorovna.png | Bin 0 -> 212376 bytes .../doctors/akhmedova-maira-gazalievna.png | Bin 0 -> 189534 bytes .../akhmedova-milana-nimatullakhovna.png | Bin 0 -> 169662 bytes .../akhmetkhanova-saida-abdulaevna.png | Bin 0 -> 162150 bytes .../assets/doctors/alieva-elvira-alievna.png | Bin 0 -> 170616 bytes .../doctors/alilova-patimat-mirzaevna.png | Bin 0 -> 195194 bytes .../azmedkhanovu-patimat-bagomedovnu.png | Bin 0 -> 194920 bytes .../doctors/bakhishov-rinat-tagirovich.png | Bin 0 -> 167552 bytes .../biarslanova-liana-biarslanovna.png | Bin 0 -> 166703 bytes .../doctors/chergesova-zaira-muratovna.png | Bin 0 -> 154914 bytes .../doctors/eldarkhanova-pakhu-mutaevna.png | Bin 0 -> 190938 bytes .../doctors/esetov-murad-azedinovich.png | Bin 0 -> 213870 bytes .../gadzhimuradova-rasiyat-gadzhievna.png | Bin 0 -> 142324 bytes .../doctors/gasanova-galimat-shikhovna.png | Bin 0 -> 172148 bytes .../gasanova-shakhrizat-magomedovna.png | Bin 0 -> 170561 bytes .../doctors/gaydarova-arapat-eldarovna.png | Bin 0 -> 160134 bytes .../doctors/gimbatova-zalina-muradovna.png | Bin 0 -> 194289 bytes .../doctors/idrisova-aida-shamilevna-.png | Bin 0 -> 234197 bytes .../doctors/idrisova-zarema-zurkanayevna.png | Bin 0 -> 164969 bytes .../doctors/inkova-ayshat-inkavovna.png | Bin 0 -> 194305 bytes .../doctors/irzaeva-sevil-vagifovna.png | Bin 0 -> 161818 bytes .../doctors/isaeva-zarema-kaybullakhovna.png | Bin 0 -> 198310 bytes .../doctors/ismailova-naizat-tashbekovna.png | Bin 0 -> 185121 bytes .../doctors/khizrieva-zaira-sayputdinovna.png | Bin 0 -> 187374 bytes .../kurbanismailova-rakhimat-ramazanovna.png | Bin 0 -> 172832 bytes .../doctors/magdieva-zarema-khulatdaevna.png | Bin 0 -> 202370 bytes .../doctors/magomedova-sabina-yaverovna.png | Bin 0 -> 208410 bytes .../magomedova-svetlana-seferbekovna-.png | Bin 0 -> 179520 bytes .../doctors/maksudov-albert-akhmedovich.png | Bin 0 -> 197307 bytes .../doctors/medzhidova-asiyat-muminovna.png | Bin 0 -> 213359 bytes .../doctors/mikailov-magomed-shamilovich.png | Bin 0 -> 168581 bytes web/public/assets/doctors/mingbolatov.png | Bin 0 -> 177034 bytes .../muftalieva-dzhuleta-byunyaminovna.png | Bin 0 -> 187898 bytes .../doctors/mutaeva-irina-vitalevna.png | Bin 0 -> 180552 bytes .../nurmagomedova-rekuyat-mamedovna-.png | Bin 0 -> 235605 bytes .../doctors/omarova-aminat-magomedovna.png | Bin 0 -> 180234 bytes .../doctors/omarova-maysarat-askhabovna.png | Bin 0 -> 173411 bytes .../rasulova-dzhamilyat-akhmedovna.png | Bin 0 -> 222738 bytes .../doctors/saaduev-saadu-khabibovich.png | Bin 0 -> 172787 bytes .../assets/doctors/savina-iya-yurevna.png | Bin 0 -> 177747 bytes .../doctors/saygidnorova-marin-muradovna.png | Bin 0 -> 195804 bytes .../doctors/shugaev-eldar-edmanovich.png | Bin 0 -> 188790 bytes .../doctors/taibov-murad-tayibovich.png | Bin 0 -> 199940 bytes .../temirova-gyulbakhar-tadzhirovna.png | Bin 0 -> 180887 bytes .../doctors/tumalaeva-naida-ganipaevna.png | Bin 0 -> 245481 bytes .../yusufova-dzhamilya-sirazhitdinovna.png | Bin 0 -> 234014 bytes web/public/favicon.ico | Bin 0 -> 3653 bytes web/scripts/audit-content.mjs | 55 + web/scripts/audit-site.mjs | 63 + web/scripts/prerender.mjs | 62 + web/scripts/render-page.mjs | 21 + web/scripts/seo-document.mjs | 20 + web/scripts/verify-runtime-seo.mjs | 58 + web/scripts/verify-seo.mjs | 109 + web/src/App.jsx | 82 + web/src/api.js | 93 + web/src/assets/figma/callback-phone-3d.png | Bin 0 -> 317823 bytes web/src/assets/figma/callback-phone-3d.webp | Bin 0 -> 15850 bytes web/src/assets/figma/checkup-cta-3d.webp | Bin 0 -> 21098 bytes web/src/assets/figma/checkup-hero-girl.webp | Bin 0 -> 78836 bytes web/src/assets/figma/checkups-hero-3d.webp | Bin 0 -> 47868 bytes web/src/assets/figma/ck-beremennost.webp | Bin 0 -> 10490 bytes web/src/assets/figma/ck-davlenie.webp | Bin 0 -> 11428 bytes web/src/assets/figma/ck-mini.webp | Bin 0 -> 12936 bytes web/src/assets/figma/ck-mne-god.webp | Bin 0 -> 12386 bytes web/src/assets/figma/ck-pervoklashka.webp | Bin 0 -> 13348 bytes web/src/assets/figma/ck-podrostok.webp | Bin 0 -> 12934 bytes web/src/assets/figma/ck-rak-sheyki.webp | Bin 0 -> 11856 bytes web/src/assets/figma/ck-roditeli.webp | Bin 0 -> 14380 bytes web/src/assets/figma/ck-v-sad.webp | Bin 0 -> 13092 bytes .../assets/figma/ck-zdorovaya-devochka.webp | Bin 0 -> 16820 bytes web/src/assets/figma/ck-zhenskoe-zdorove.webp | Bin 0 -> 12038 bytes web/src/assets/figma/ck-zhkt.webp | Bin 0 -> 14442 bytes web/src/assets/figma/dir-detskaya-klinika.png | Bin 0 -> 2526027 bytes .../assets/figma/dir-detskaya-klinika.webp | Bin 0 -> 142346 bytes web/src/assets/figma/dir-estetic.png | Bin 0 -> 1325975 bytes web/src/assets/figma/dir-estetic.webp | Bin 0 -> 97010 bytes web/src/assets/figma/dir-ginekologiya.png | Bin 0 -> 1305311 bytes web/src/assets/figma/dir-ginekologiya.webp | Bin 0 -> 43938 bytes web/src/assets/figma/dir-osteo.png | Bin 0 -> 248826 bytes web/src/assets/figma/dir-osteo.webp | Bin 0 -> 19944 bytes web/src/assets/figma/hero-dana-letters.svg | 16 + web/src/assets/figma/ico-baby.png | Bin 0 -> 2336 bytes web/src/assets/figma/ico-cancer-ribbon.png | Bin 0 -> 2684 bytes web/src/assets/figma/ico-family.png | Bin 0 -> 1644 bytes web/src/assets/figma/ico-female.png | Bin 0 -> 1040 bytes web/src/assets/figma/ico-health-checkup.png | Bin 0 -> 2136 bytes web/src/assets/figma/ico-playground.png | Bin 0 -> 1751 bytes web/src/assets/figma/ico-pregnant.png | Bin 0 -> 1668 bytes web/src/assets/figma/ico-school.png | Bin 0 -> 938 bytes web/src/assets/figma/ico-stethoscope.png | Bin 0 -> 1892 bytes web/src/assets/figma/ico-stomach.png | Bin 0 -> 1991 bytes web/src/assets/figma/ico-teenager.png | Bin 0 -> 1641 bytes web/src/assets/figma/ico-tonometer.png | Bin 0 -> 1368 bytes .../assets/figma/icon-accordion-chevron.svg | 5 + web/src/assets/figma/icon-arrow-right.svg | 5 + web/src/assets/figma/icon-calendar.svg | 6 + web/src/assets/figma/icon-check.svg | 5 + web/src/assets/figma/icon-chevron-dark.svg | 5 + web/src/assets/figma/icon-chevron-down.svg | 5 + web/src/assets/figma/icon-chevron-white.svg | 5 + web/src/assets/figma/icon-close.svg | 5 + web/src/assets/figma/icon-dot-coral.svg | 3 + web/src/assets/figma/icon-dot-green.svg | 3 + web/src/assets/figma/icon-dot-urgent.svg | 3 + web/src/assets/figma/icon-max.png | Bin 0 -> 352458 bytes web/src/assets/figma/icon-max.svg | 1 + web/src/assets/figma/icon-max.webp | Bin 0 -> 2680 bytes web/src/assets/figma/icon-search-16.svg | 6 + web/src/assets/figma/icon-search-18.svg | 6 + web/src/assets/figma/icon-search-mobile.svg | 6 + web/src/assets/figma/icon-search-price.svg | 6 + web/src/assets/figma/icon-search.svg | 6 + web/src/assets/figma/icon-star-small.svg | 10 + web/src/assets/figma/icon-star.svg | 3 + web/src/assets/figma/icon-whatsapp-bg.svg | 11 + web/src/assets/figma/icon-whatsapp-circle.svg | 14 + web/src/assets/figma/icon-whatsapp-glyph.svg | 5 + web/src/assets/figma/icon-whatsapp-mark.svg | 10 + web/src/assets/figma/icon-whatsapp.svg | 1 + web/src/assets/figma/logo-dana-white.svg | 5 + web/src/assets/figma/logo-dana.svg | 5 + web/src/assets/figma/logo-mobile-white.svg | 10 + web/src/assets/figma/logo-mobile.svg | 10 + web/src/assets/figma/logo-subtitle-white.svg | 28 + web/src/assets/figma/logo-subtitle.svg | 28 + web/src/assets/figma/map-contacts.png | Bin 0 -> 2074956 bytes web/src/assets/figma/map-contacts.webp | Bin 0 -> 130210 bytes web/src/assets/figma/promo-podrostok.webp | Bin 0 -> 55610 bytes web/src/assets/figma/promo-rodilsya.webp | Bin 0 -> 69166 bytes web/src/assets/figma/promo-sad.webp | Bin 0 -> 161952 bytes web/src/assets/figma/promo-sportsmen.webp | Bin 0 -> 112752 bytes web/src/assets/figma/promo-zhkt.webp | Bin 0 -> 92094 bytes web/src/assets/figma/promos-hero-3d.webp | Bin 0 -> 60944 bytes web/src/assets/figma/service-urgent-3d.webp | Bin 0 -> 39266 bytes web/src/assets/figma/slider-arrow-doctors.svg | 6 + .../assets/figma/slider-arrow-hero-next.svg | 6 + web/src/assets/figma/slider-arrow-hero.svg | 6 + web/src/assets/figma/svc-bol.webp | Bin 0 -> 45656 bytes .../assets/figma/svc-detskaya-klinika.webp | Bin 0 -> 85438 bytes web/src/assets/figma/svc-endokrinologiya.webp | Bin 0 -> 61910 bytes web/src/assets/figma/svc-ginekologiya.webp | Bin 0 -> 35862 bytes web/src/assets/figma/svc-khirurgiya.webp | Bin 0 -> 123764 bytes web/src/assets/figma/svc-mammologiya.webp | Bin 0 -> 62236 bytes web/src/assets/figma/svc-oftalmologiya.webp | Bin 0 -> 40874 bytes web/src/assets/figma/svc-onkologiya.webp | Bin 0 -> 39060 bytes .../figma/svc-plasticheskaya-khirurgiya.webp | Bin 0 -> 56744 bytes web/src/assets/figma/svc-proktologiya.webp | Bin 0 -> 99268 bytes web/src/assets/figma/svc-urologiya.webp | Bin 0 -> 124976 bytes web/src/components/BookingSection.css | 170 ++ web/src/components/BookingSection.jsx | 143 + web/src/components/Breadcrumbs.jsx | 23 + web/src/components/CallbackForm.jsx | 81 + web/src/components/ClinicMap.css | 51 + web/src/components/ClinicMap.jsx | 37 + web/src/components/CompanySection.css | 228 ++ web/src/components/CompanySection.jsx | 94 + web/src/components/ContactsSection.css | 336 +++ web/src/components/ContactsSection.jsx | 57 + web/src/components/CookieBanner.css | 63 + web/src/components/CookieBanner.jsx | 35 + web/src/components/CtaBand.css | 149 + web/src/components/CtaBand.jsx | 19 + web/src/components/DoctorCard.css | 320 +++ web/src/components/DoctorCard.jsx | 108 + web/src/components/Footer.css | 251 ++ web/src/components/Footer.jsx | 115 + web/src/components/Header.css | 187 ++ web/src/components/Header.jsx | 148 + web/src/components/HeroSlider.css | 496 ++++ web/src/components/HeroSlider.jsx | 243 ++ web/src/components/Layout.jsx | 41 + web/src/components/LeadModal.css | 351 +++ web/src/components/LeadModal.jsx | 394 +++ web/src/components/MegaMenu.css | 108 + web/src/components/MegaMenu.jsx | 111 + web/src/components/MobileMenu.css | 48 + web/src/components/MobileMenu.jsx | 59 + web/src/components/NativeBooking.css | 258 ++ web/src/components/NativeBooking.jsx | 399 +++ web/src/components/SearchBox.css | 123 + web/src/components/SearchBox.jsx | 128 + web/src/components/SocialLinks.css | 26 + web/src/components/SocialLinks.jsx | 34 + web/src/context/ApiData.jsx | 8 + web/src/context/LeadModalContext.jsx | 27 + web/src/context/SiteData.jsx | 67 + web/src/entry-server.jsx | 101 + web/src/lib/attribution.js | 45 + web/src/lib/format.js | 35 + web/src/lib/links.js | 47 + web/src/lib/media.js | 133 + web/src/lib/metrika.js | 81 + web/src/lib/phone.js | 21 + web/src/lib/sanitize.js | 36 + web/src/lib/seo.js | 85 + web/src/lib/serviceContent.js | 67 + web/src/lib/serviceNavigation.js | 22 + web/src/lib/slots.js | 42 + web/src/lib/text.js | 41 + web/src/lib/useApi.js | 26 + web/src/lib/usePageTitle.js | 41 + web/src/lib/video.js | 7 + web/src/main.jsx | 31 + web/src/pages/AboutPage.jsx | 65 + web/src/pages/CheckupPage.css | 431 +++ web/src/pages/CheckupPage.jsx | 153 ++ web/src/pages/CheckupsPage.css | 405 +++ web/src/pages/CheckupsPage.jsx | 145 + web/src/pages/CompanyListPages.jsx | 330 +++ web/src/pages/CompanyPages.css | 758 +++++ web/src/pages/ContactsPage.css | 308 +++ web/src/pages/ContactsPage.jsx | 139 + web/src/pages/DoctorPage.css | 566 ++++ web/src/pages/DoctorPage.jsx | 243 ++ web/src/pages/DoctorsPage.css | 211 ++ web/src/pages/DoctorsPage.jsx | 129 + web/src/pages/HomePage.css | 246 ++ web/src/pages/HomePage.jsx | 122 + web/src/pages/LegalPage.css | 114 + web/src/pages/LegalPage.jsx | 70 + web/src/pages/PostPage.jsx | 74 + web/src/pages/PostsPage.jsx | 54 + web/src/pages/PricesPage.css | 493 ++++ web/src/pages/PricesPage.jsx | 174 ++ web/src/pages/PromosPage.css | 240 ++ web/src/pages/PromosPage.jsx | 94 + web/src/pages/ServicePage.css | 721 +++++ web/src/pages/ServicePage.jsx | 312 +++ web/src/pages/ServicesPage.css | 101 + web/src/pages/ServicesPage.jsx | 48 + web/src/pages/StubPage.css | 57 + web/src/pages/StubPage.jsx | 32 + web/src/styles/base.css | 194 ++ web/src/styles/tokens.css | 83 + web/vite.config.js | 17 + 499 files changed, 52904 insertions(+) create mode 100644 .deploy/dana-medical.caddy create mode 100644 .deploy/remote-build-start-dana.sh create mode 100644 .deploy/remote-init-dana.sh create mode 100644 .deploy/remote-install-dana-caddy.sh create mode 100644 .deploy/remote-restore-dana.sh create mode 100644 .deploy/remote-secure-dana.sh create mode 100644 .deploy/remote-smoke-dana.sh create mode 100644 .deploy/remote-start-dana-db.sh create mode 100755 .deploy/remote-update-dana-caddy.sh create mode 100644 .deploy/remote-verify-dana-preview.sh create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 cmd/server/main.go create mode 100644 deploy.env.example create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/api/admin.go create mode 100644 internal/api/admin_banners.go create mode 100644 internal/api/admin_banners_test.go create mode 100644 internal/api/admin_checkups.go create mode 100644 internal/api/admin_doctors.go create mode 100644 internal/api/admin_documents.go create mode 100644 internal/api/admin_documents_test.go create mode 100644 internal/api/admin_inbox.go create mode 100644 internal/api/admin_media.go create mode 100644 internal/api/admin_partners.go create mode 100644 internal/api/admin_partners_test.go create mode 100644 internal/api/admin_posts.go create mode 100644 internal/api/admin_posts_test.go create mode 100644 internal/api/admin_promos.go create mode 100644 internal/api/admin_promos_test.go create mode 100644 internal/api/admin_redirects.go create mode 100644 internal/api/admin_redirects_test.go create mode 100644 internal/api/admin_reports.go create mode 100644 internal/api/admin_services.go create mode 100644 internal/api/admin_site.go create mode 100644 internal/api/admin_site_test.go create mode 100644 internal/api/admin_specialties.go create mode 100644 internal/api/admin_specialties_test.go create mode 100644 internal/api/admin_stats.go create mode 100644 internal/api/admin_stats_test.go create mode 100644 internal/api/admin_vacancies.go create mode 100644 internal/api/admin_weekly.go create mode 100644 internal/api/adminui.go create mode 100644 internal/api/adminui/admin.css create mode 100644 internal/api/adminui/assets/README.txt create mode 100644 internal/api/adminui/assets/channels/2gis.png create mode 100644 internal/api/adminui/assets/channels/SOURCES.md create mode 100644 internal/api/adminui/assets/channels/doctu.png create mode 100644 internal/api/adminui/assets/channels/doctu.svg create mode 100644 internal/api/adminui/assets/channels/google.png create mode 100644 internal/api/adminui/assets/channels/instagram.png create mode 100644 internal/api/adminui/assets/channels/max.png create mode 100644 internal/api/adminui/assets/channels/napopravku.png create mode 100644 internal/api/adminui/assets/channels/ochevidno-site.png create mode 100644 internal/api/adminui/assets/channels/prodoctorov.png create mode 100644 internal/api/adminui/assets/channels/sber.png create mode 100644 internal/api/adminui/assets/channels/telegram.png create mode 100644 internal/api/adminui/assets/channels/vk.png create mode 100644 internal/api/adminui/assets/channels/website.svg create mode 100644 internal/api/adminui/assets/channels/yandex.png create mode 100644 internal/api/adminui/assets/channels/zoon.png create mode 100644 internal/api/adminui/assets/hall.webp create mode 100644 internal/api/adminui/assets/heart.webp create mode 100644 internal/api/adminui/assets/lobby.webp create mode 100644 internal/api/adminui/banners.js create mode 100644 internal/api/adminui/clinic.js create mode 100644 internal/api/adminui/dashboard.js create mode 100644 internal/api/adminui/doctors-dash.js create mode 100644 internal/api/adminui/doctors.js create mode 100644 internal/api/adminui/fonts/montserrat-alternates-cyrillic-300-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-cyrillic-400-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-cyrillic-500-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-cyrillic-600-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-latin-300-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-latin-400-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-latin-500-normal.woff2 create mode 100644 internal/api/adminui/fonts/montserrat-alternates-latin-600-normal.woff2 create mode 100644 internal/api/adminui/index.html create mode 100644 internal/api/adminui/leads.css create mode 100644 internal/api/adminui/leads.js create mode 100644 internal/api/adminui/onec.css create mode 100644 internal/api/adminui/onec.js create mode 100644 internal/api/adminui/pbx.js create mode 100644 internal/api/adminui/reports-history.css create mode 100644 internal/api/adminui/reports-history.js create mode 100644 internal/api/adminui/reports.js create mode 100644 internal/api/adminui/services.js create mode 100644 internal/api/adminui/share.html create mode 100644 internal/api/adminui/share.js create mode 100644 internal/api/adminui/shell.js create mode 100644 internal/api/adminui/stats.js create mode 100644 internal/api/adminui/weekly-experiment.css create mode 100644 internal/api/adminui/weekly-experiment.js create mode 100644 internal/api/adminui/weekly.js create mode 100644 internal/api/api.go create mode 100644 internal/api/booking_confirm.go create mode 100644 internal/api/booking_confirm_test.go create mode 100644 internal/api/clientip_test.go create mode 100644 internal/api/clinics.go create mode 100644 internal/api/clinics_test.go create mode 100644 internal/api/handlers.go create mode 100644 internal/api/handlers_content.go create mode 100644 internal/api/handlers_leads.go create mode 100644 internal/api/handlers_leads_test.go create mode 100644 internal/api/handlers_site.go create mode 100644 internal/api/legacy.go create mode 100644 internal/api/media_webp.go create mode 100644 internal/api/media_webp_test.go create mode 100644 internal/api/notify.go create mode 100644 internal/api/onec.go create mode 100644 internal/api/onec_webhook.go create mode 100644 internal/api/onec_webhook_test.go create mode 100644 internal/api/pbx.go create mode 100644 internal/api/pbx_test.go create mode 100644 internal/api/phones_parse.go create mode 100644 internal/api/ratelimit.go create mode 100644 internal/api/redact.go create mode 100644 internal/api/report_bitrix.go create mode 100644 internal/api/report_partner_uploads.go create mode 100644 internal/api/report_partner_uploads_test.go create mode 100644 internal/api/report_phones.go create mode 100644 internal/api/report_phones_test.go create mode 100644 internal/api/report_prodoctorov.go create mode 100644 internal/api/report_prodoctorov_sample_test.go create mode 100644 internal/api/report_prodoctorov_test.go create mode 100644 internal/api/report_sberhealth.go create mode 100644 internal/api/report_sberhealth_large_test.go create mode 100644 internal/api/report_sberhealth_test.go create mode 100644 internal/api/security_pack_test.go create mode 100644 internal/api/share.go create mode 100644 internal/api/ssr.go create mode 100644 internal/api/ssr_test.go create mode 100644 internal/api/tables.go create mode 100644 internal/api/testdata/render-fixture.mjs create mode 100644 internal/api/video.go create mode 100644 internal/api/video_test.go create mode 100644 internal/api/waitcall.go create mode 100644 internal/api/waitcall.go.root-backup create mode 100644 internal/api/waitcall_test.go create mode 100644 internal/api/website.go create mode 100644 internal/api/website_test.go create mode 100644 internal/bitrix/client.go create mode 100644 internal/bitrix/leads.go create mode 100644 internal/bitrix/leads_test.go create mode 100644 internal/bitrix/redact_test.go create mode 100644 internal/config/config.go create mode 100644 internal/domain/content.go create mode 100644 internal/domain/domain.go create mode 100644 internal/domain/notice.go create mode 100644 internal/domain/site.go create mode 100644 internal/megafon/client.go create mode 100644 internal/megafon/client_test.go create mode 100644 internal/notify/email.go create mode 100644 internal/notify/email_test.go create mode 100644 internal/onec/client.go create mode 100644 internal/onec/client_test.go create mode 100644 internal/prodoctorov/client.go create mode 100644 internal/prodoctorov/client_test.go create mode 100644 internal/sberhealth/biff.go create mode 100644 internal/sberhealth/biff_test.go create mode 100644 internal/sberhealth/client.go create mode 100644 internal/sberhealth/client_test.go create mode 100644 internal/sberhealth/xls.go create mode 100644 internal/storage/admin.go create mode 100644 internal/storage/admin_checkups.go create mode 100644 internal/storage/admin_content.go create mode 100644 internal/storage/admin_doctors.go create mode 100644 internal/storage/admin_documents.go create mode 100644 internal/storage/admin_faq.go create mode 100644 internal/storage/admin_inbox.go create mode 100644 internal/storage/admin_partners.go create mode 100644 internal/storage/admin_posts.go create mode 100644 internal/storage/admin_promos.go create mode 100644 internal/storage/admin_services.go create mode 100644 internal/storage/admin_site.go create mode 100644 internal/storage/admin_specialties.go create mode 100644 internal/storage/admin_vacancies.go create mode 100644 internal/storage/banners.go create mode 100644 internal/storage/booking_confirm.go create mode 100644 internal/storage/booking_confirm_test.go create mode 100644 internal/storage/conflict.go create mode 100644 internal/storage/content.go create mode 100644 internal/storage/dashboard.go create mode 100644 internal/storage/doctor_quality.go create mode 100644 internal/storage/doctors.go create mode 100644 internal/storage/lead_notice.go create mode 100644 internal/storage/leads.go create mode 100644 internal/storage/media.go create mode 100644 internal/storage/migrations/0001_core.sql create mode 100644 internal/storage/migrations/0002_content.sql create mode 100644 internal/storage/migrations/0003_leads_admin.sql create mode 100644 internal/storage/migrations/0004_seed_forms.sql create mode 100644 internal/storage/migrations/0005_review_video.sql create mode 100644 internal/storage/migrations/0006_mockup.sql create mode 100644 internal/storage/migrations/0007_legal.sql create mode 100644 internal/storage/migrations/0008_legal_new_site.sql create mode 100644 internal/storage/migrations/0009_promo_checkup_admin.sql create mode 100644 internal/storage/migrations/0010_banner_layouts.sql create mode 100644 internal/storage/migrations/0011_company.sql create mode 100644 internal/storage/migrations/0012_documents.sql create mode 100644 internal/storage/migrations/0013_review_dates.sql create mode 100644 internal/storage/migrations/0014_doctor_bio_text.sql create mode 100644 internal/storage/migrations/0015_patients_page.sql create mode 100644 internal/storage/migrations/0016_checkup_kra.sql create mode 100644 internal/storage/migrations/0017_reports.sql create mode 100644 internal/storage/migrations/0018_pbx.sql create mode 100644 internal/storage/migrations/0019_doctor_photos.sql create mode 100644 internal/storage/migrations/0020_onec.sql create mode 100644 internal/storage/migrations/0021_weekly_reports.sql create mode 100644 internal/storage/migrations/0022_doctor_rating_override.sql create mode 100644 internal/storage/migrations/0023_booking_confirm.sql create mode 100644 internal/storage/migrations/0024_clinics.sql create mode 100644 internal/storage/migrations/0025_share_links.sql create mode 100644 internal/storage/onec.go create mode 100644 internal/storage/pbx.go create mode 100644 internal/storage/price_sections.go create mode 100644 internal/storage/prices.go create mode 100644 internal/storage/public_paths.go create mode 100644 internal/storage/redirects.go create mode 100644 internal/storage/reports.go create mode 100644 internal/storage/search.go create mode 100644 internal/storage/services.go create mode 100644 internal/storage/share.go create mode 100644 internal/storage/stats.go create mode 100644 internal/storage/storage.go create mode 100644 internal/storage/weekly.go create mode 100644 migrations/import/README.md create mode 100644 migrations/import/build_price_map.py create mode 100644 migrations/import/dedup_and_label.py create mode 100644 migrations/import/export_content.py create mode 100644 migrations/import/export_dir_icons.py create mode 100644 migrations/import/export_doctor_bio.py create mode 100644 migrations/import/export_doctor_experience.py create mode 100644 migrations/import/export_media.py create mode 100644 migrations/import/export_seed.py create mode 100644 migrations/import/find_bio_glitches.py create mode 100644 migrations/import/fix_blocks_rn.py create mode 100644 migrations/import/fix_review_texts.py create mode 100644 migrations/import/make_webp.py create mode 100644 migrations/import/rebuild_price_from_1c_only.py create mode 100644 migrations/import/sqlread.py create mode 100644 seo-structure.md create mode 100644 seo/DEPLOYMENT.md create mode 100644 seo/analyze.mjs create mode 100644 seo/cluster-analysis.json create mode 100644 seo/clusters_exact.csv create mode 100644 seo/content-audit.json create mode 100644 seo/content-gaps.md create mode 100644 seo/crawl-audit.json create mode 100644 seo/service-profiles.json create mode 100644 seo/site-profiles.json create mode 100644 web/index.html create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/public/apple-touch-icon.png create mode 100644 web/public/assets/doctors/abakarova-patimat-alievna.png create mode 100644 web/public/assets/doctors/abdullaev-magomed-abdullaevich.png create mode 100644 web/public/assets/doctors/abdurakhmanova-zaliyat-abdulaevna.png create mode 100644 web/public/assets/doctors/afanaseva-elena-fyedorovna.png create mode 100644 web/public/assets/doctors/akhmedova-maira-gazalievna.png create mode 100644 web/public/assets/doctors/akhmedova-milana-nimatullakhovna.png create mode 100644 web/public/assets/doctors/akhmetkhanova-saida-abdulaevna.png create mode 100644 web/public/assets/doctors/alieva-elvira-alievna.png create mode 100644 web/public/assets/doctors/alilova-patimat-mirzaevna.png create mode 100644 web/public/assets/doctors/azmedkhanovu-patimat-bagomedovnu.png create mode 100644 web/public/assets/doctors/bakhishov-rinat-tagirovich.png create mode 100644 web/public/assets/doctors/biarslanova-liana-biarslanovna.png create mode 100644 web/public/assets/doctors/chergesova-zaira-muratovna.png create mode 100644 web/public/assets/doctors/eldarkhanova-pakhu-mutaevna.png create mode 100644 web/public/assets/doctors/esetov-murad-azedinovich.png create mode 100644 web/public/assets/doctors/gadzhimuradova-rasiyat-gadzhievna.png create mode 100644 web/public/assets/doctors/gasanova-galimat-shikhovna.png create mode 100644 web/public/assets/doctors/gasanova-shakhrizat-magomedovna.png create mode 100644 web/public/assets/doctors/gaydarova-arapat-eldarovna.png create mode 100644 web/public/assets/doctors/gimbatova-zalina-muradovna.png create mode 100644 web/public/assets/doctors/idrisova-aida-shamilevna-.png create mode 100644 web/public/assets/doctors/idrisova-zarema-zurkanayevna.png create mode 100644 web/public/assets/doctors/inkova-ayshat-inkavovna.png create mode 100644 web/public/assets/doctors/irzaeva-sevil-vagifovna.png create mode 100644 web/public/assets/doctors/isaeva-zarema-kaybullakhovna.png create mode 100644 web/public/assets/doctors/ismailova-naizat-tashbekovna.png create mode 100644 web/public/assets/doctors/khizrieva-zaira-sayputdinovna.png create mode 100644 web/public/assets/doctors/kurbanismailova-rakhimat-ramazanovna.png create mode 100644 web/public/assets/doctors/magdieva-zarema-khulatdaevna.png create mode 100644 web/public/assets/doctors/magomedova-sabina-yaverovna.png create mode 100644 web/public/assets/doctors/magomedova-svetlana-seferbekovna-.png create mode 100644 web/public/assets/doctors/maksudov-albert-akhmedovich.png create mode 100644 web/public/assets/doctors/medzhidova-asiyat-muminovna.png create mode 100644 web/public/assets/doctors/mikailov-magomed-shamilovich.png create mode 100644 web/public/assets/doctors/mingbolatov.png create mode 100644 web/public/assets/doctors/muftalieva-dzhuleta-byunyaminovna.png create mode 100644 web/public/assets/doctors/mutaeva-irina-vitalevna.png create mode 100644 web/public/assets/doctors/nurmagomedova-rekuyat-mamedovna-.png create mode 100644 web/public/assets/doctors/omarova-aminat-magomedovna.png create mode 100644 web/public/assets/doctors/omarova-maysarat-askhabovna.png create mode 100644 web/public/assets/doctors/rasulova-dzhamilyat-akhmedovna.png create mode 100644 web/public/assets/doctors/saaduev-saadu-khabibovich.png create mode 100644 web/public/assets/doctors/savina-iya-yurevna.png create mode 100644 web/public/assets/doctors/saygidnorova-marin-muradovna.png create mode 100644 web/public/assets/doctors/shugaev-eldar-edmanovich.png create mode 100644 web/public/assets/doctors/taibov-murad-tayibovich.png create mode 100644 web/public/assets/doctors/temirova-gyulbakhar-tadzhirovna.png create mode 100644 web/public/assets/doctors/tumalaeva-naida-ganipaevna.png create mode 100644 web/public/assets/doctors/yusufova-dzhamilya-sirazhitdinovna.png create mode 100644 web/public/favicon.ico create mode 100644 web/scripts/audit-content.mjs create mode 100644 web/scripts/audit-site.mjs create mode 100644 web/scripts/prerender.mjs create mode 100644 web/scripts/render-page.mjs create mode 100644 web/scripts/seo-document.mjs create mode 100644 web/scripts/verify-runtime-seo.mjs create mode 100644 web/scripts/verify-seo.mjs create mode 100644 web/src/App.jsx create mode 100644 web/src/api.js create mode 100644 web/src/assets/figma/callback-phone-3d.png create mode 100644 web/src/assets/figma/callback-phone-3d.webp create mode 100644 web/src/assets/figma/checkup-cta-3d.webp create mode 100644 web/src/assets/figma/checkup-hero-girl.webp create mode 100644 web/src/assets/figma/checkups-hero-3d.webp create mode 100644 web/src/assets/figma/ck-beremennost.webp create mode 100644 web/src/assets/figma/ck-davlenie.webp create mode 100644 web/src/assets/figma/ck-mini.webp create mode 100644 web/src/assets/figma/ck-mne-god.webp create mode 100644 web/src/assets/figma/ck-pervoklashka.webp create mode 100644 web/src/assets/figma/ck-podrostok.webp create mode 100644 web/src/assets/figma/ck-rak-sheyki.webp create mode 100644 web/src/assets/figma/ck-roditeli.webp create mode 100644 web/src/assets/figma/ck-v-sad.webp create mode 100644 web/src/assets/figma/ck-zdorovaya-devochka.webp create mode 100644 web/src/assets/figma/ck-zhenskoe-zdorove.webp create mode 100644 web/src/assets/figma/ck-zhkt.webp create mode 100644 web/src/assets/figma/dir-detskaya-klinika.png create mode 100644 web/src/assets/figma/dir-detskaya-klinika.webp create mode 100644 web/src/assets/figma/dir-estetic.png create mode 100644 web/src/assets/figma/dir-estetic.webp create mode 100644 web/src/assets/figma/dir-ginekologiya.png create mode 100644 web/src/assets/figma/dir-ginekologiya.webp create mode 100644 web/src/assets/figma/dir-osteo.png create mode 100644 web/src/assets/figma/dir-osteo.webp create mode 100644 web/src/assets/figma/hero-dana-letters.svg create mode 100644 web/src/assets/figma/ico-baby.png create mode 100644 web/src/assets/figma/ico-cancer-ribbon.png create mode 100644 web/src/assets/figma/ico-family.png create mode 100644 web/src/assets/figma/ico-female.png create mode 100644 web/src/assets/figma/ico-health-checkup.png create mode 100644 web/src/assets/figma/ico-playground.png create mode 100644 web/src/assets/figma/ico-pregnant.png create mode 100644 web/src/assets/figma/ico-school.png create mode 100644 web/src/assets/figma/ico-stethoscope.png create mode 100644 web/src/assets/figma/ico-stomach.png create mode 100644 web/src/assets/figma/ico-teenager.png create mode 100644 web/src/assets/figma/ico-tonometer.png create mode 100644 web/src/assets/figma/icon-accordion-chevron.svg create mode 100644 web/src/assets/figma/icon-arrow-right.svg create mode 100644 web/src/assets/figma/icon-calendar.svg create mode 100644 web/src/assets/figma/icon-check.svg create mode 100644 web/src/assets/figma/icon-chevron-dark.svg create mode 100644 web/src/assets/figma/icon-chevron-down.svg create mode 100644 web/src/assets/figma/icon-chevron-white.svg create mode 100644 web/src/assets/figma/icon-close.svg create mode 100644 web/src/assets/figma/icon-dot-coral.svg create mode 100644 web/src/assets/figma/icon-dot-green.svg create mode 100644 web/src/assets/figma/icon-dot-urgent.svg create mode 100644 web/src/assets/figma/icon-max.png create mode 100644 web/src/assets/figma/icon-max.svg create mode 100644 web/src/assets/figma/icon-max.webp create mode 100644 web/src/assets/figma/icon-search-16.svg create mode 100644 web/src/assets/figma/icon-search-18.svg create mode 100644 web/src/assets/figma/icon-search-mobile.svg create mode 100644 web/src/assets/figma/icon-search-price.svg create mode 100644 web/src/assets/figma/icon-search.svg create mode 100644 web/src/assets/figma/icon-star-small.svg create mode 100644 web/src/assets/figma/icon-star.svg create mode 100644 web/src/assets/figma/icon-whatsapp-bg.svg create mode 100644 web/src/assets/figma/icon-whatsapp-circle.svg create mode 100644 web/src/assets/figma/icon-whatsapp-glyph.svg create mode 100644 web/src/assets/figma/icon-whatsapp-mark.svg create mode 100644 web/src/assets/figma/icon-whatsapp.svg create mode 100644 web/src/assets/figma/logo-dana-white.svg create mode 100644 web/src/assets/figma/logo-dana.svg create mode 100644 web/src/assets/figma/logo-mobile-white.svg create mode 100644 web/src/assets/figma/logo-mobile.svg create mode 100644 web/src/assets/figma/logo-subtitle-white.svg create mode 100644 web/src/assets/figma/logo-subtitle.svg create mode 100644 web/src/assets/figma/map-contacts.png create mode 100644 web/src/assets/figma/map-contacts.webp create mode 100644 web/src/assets/figma/promo-podrostok.webp create mode 100644 web/src/assets/figma/promo-rodilsya.webp create mode 100644 web/src/assets/figma/promo-sad.webp create mode 100644 web/src/assets/figma/promo-sportsmen.webp create mode 100644 web/src/assets/figma/promo-zhkt.webp create mode 100644 web/src/assets/figma/promos-hero-3d.webp create mode 100644 web/src/assets/figma/service-urgent-3d.webp create mode 100644 web/src/assets/figma/slider-arrow-doctors.svg create mode 100644 web/src/assets/figma/slider-arrow-hero-next.svg create mode 100644 web/src/assets/figma/slider-arrow-hero.svg create mode 100644 web/src/assets/figma/svc-bol.webp create mode 100644 web/src/assets/figma/svc-detskaya-klinika.webp create mode 100644 web/src/assets/figma/svc-endokrinologiya.webp create mode 100644 web/src/assets/figma/svc-ginekologiya.webp create mode 100644 web/src/assets/figma/svc-khirurgiya.webp create mode 100644 web/src/assets/figma/svc-mammologiya.webp create mode 100644 web/src/assets/figma/svc-oftalmologiya.webp create mode 100644 web/src/assets/figma/svc-onkologiya.webp create mode 100644 web/src/assets/figma/svc-plasticheskaya-khirurgiya.webp create mode 100644 web/src/assets/figma/svc-proktologiya.webp create mode 100644 web/src/assets/figma/svc-urologiya.webp create mode 100644 web/src/components/BookingSection.css create mode 100644 web/src/components/BookingSection.jsx create mode 100644 web/src/components/Breadcrumbs.jsx create mode 100644 web/src/components/CallbackForm.jsx create mode 100644 web/src/components/ClinicMap.css create mode 100644 web/src/components/ClinicMap.jsx create mode 100644 web/src/components/CompanySection.css create mode 100644 web/src/components/CompanySection.jsx create mode 100644 web/src/components/ContactsSection.css create mode 100644 web/src/components/ContactsSection.jsx create mode 100644 web/src/components/CookieBanner.css create mode 100644 web/src/components/CookieBanner.jsx create mode 100644 web/src/components/CtaBand.css create mode 100644 web/src/components/CtaBand.jsx create mode 100644 web/src/components/DoctorCard.css create mode 100644 web/src/components/DoctorCard.jsx create mode 100644 web/src/components/Footer.css create mode 100644 web/src/components/Footer.jsx create mode 100644 web/src/components/Header.css create mode 100644 web/src/components/Header.jsx create mode 100644 web/src/components/HeroSlider.css create mode 100644 web/src/components/HeroSlider.jsx create mode 100644 web/src/components/Layout.jsx create mode 100644 web/src/components/LeadModal.css create mode 100644 web/src/components/LeadModal.jsx create mode 100644 web/src/components/MegaMenu.css create mode 100644 web/src/components/MegaMenu.jsx create mode 100644 web/src/components/MobileMenu.css create mode 100644 web/src/components/MobileMenu.jsx create mode 100644 web/src/components/NativeBooking.css create mode 100644 web/src/components/NativeBooking.jsx create mode 100644 web/src/components/SearchBox.css create mode 100644 web/src/components/SearchBox.jsx create mode 100644 web/src/components/SocialLinks.css create mode 100644 web/src/components/SocialLinks.jsx create mode 100644 web/src/context/ApiData.jsx create mode 100644 web/src/context/LeadModalContext.jsx create mode 100644 web/src/context/SiteData.jsx create mode 100644 web/src/entry-server.jsx create mode 100644 web/src/lib/attribution.js create mode 100644 web/src/lib/format.js create mode 100644 web/src/lib/links.js create mode 100644 web/src/lib/media.js create mode 100644 web/src/lib/metrika.js create mode 100644 web/src/lib/phone.js create mode 100644 web/src/lib/sanitize.js create mode 100644 web/src/lib/seo.js create mode 100644 web/src/lib/serviceContent.js create mode 100644 web/src/lib/serviceNavigation.js create mode 100644 web/src/lib/slots.js create mode 100644 web/src/lib/text.js create mode 100644 web/src/lib/useApi.js create mode 100644 web/src/lib/usePageTitle.js create mode 100644 web/src/lib/video.js create mode 100644 web/src/main.jsx create mode 100644 web/src/pages/AboutPage.jsx create mode 100644 web/src/pages/CheckupPage.css create mode 100644 web/src/pages/CheckupPage.jsx create mode 100644 web/src/pages/CheckupsPage.css create mode 100644 web/src/pages/CheckupsPage.jsx create mode 100644 web/src/pages/CompanyListPages.jsx create mode 100644 web/src/pages/CompanyPages.css create mode 100644 web/src/pages/ContactsPage.css create mode 100644 web/src/pages/ContactsPage.jsx create mode 100644 web/src/pages/DoctorPage.css create mode 100644 web/src/pages/DoctorPage.jsx create mode 100644 web/src/pages/DoctorsPage.css create mode 100644 web/src/pages/DoctorsPage.jsx create mode 100644 web/src/pages/HomePage.css create mode 100644 web/src/pages/HomePage.jsx create mode 100644 web/src/pages/LegalPage.css create mode 100644 web/src/pages/LegalPage.jsx create mode 100644 web/src/pages/PostPage.jsx create mode 100644 web/src/pages/PostsPage.jsx create mode 100644 web/src/pages/PricesPage.css create mode 100644 web/src/pages/PricesPage.jsx create mode 100644 web/src/pages/PromosPage.css create mode 100644 web/src/pages/PromosPage.jsx create mode 100644 web/src/pages/ServicePage.css create mode 100644 web/src/pages/ServicePage.jsx create mode 100644 web/src/pages/ServicesPage.css create mode 100644 web/src/pages/ServicesPage.jsx create mode 100644 web/src/pages/StubPage.css create mode 100644 web/src/pages/StubPage.jsx create mode 100644 web/src/styles/base.css create mode 100644 web/src/styles/tokens.css create mode 100644 web/vite.config.js diff --git a/.deploy/dana-medical.caddy b/.deploy/dana-medical.caddy new file mode 100644 index 0000000..ff454b2 --- /dev/null +++ b/.deploy/dana-medical.caddy @@ -0,0 +1,74 @@ +danamedical.ru { + encode zstd gzip + + header { + -Server + } + + reverse_proxy 172.17.0.1:8082 { + header_up X-Real-IP {remote_host} + } + + request_body { + max_size 20MB + } + + log { + output file /var/log/caddy/dana-medical.log { + roll_size 10MB + roll_keep 10 + } + } +} + +www.danamedical.ru { + redir https://danamedical.ru{uri} permanent +} + +new.danamedical.ru { + encode zstd gzip + + header { + X-Robots-Tag "noindex, nofollow" + -Server + } + + reverse_proxy 172.17.0.1:8082 { + header_up X-Real-IP {remote_host} + } + + request_body { + max_size 20MB + } + + log { + output file /var/log/caddy/dana-medical-preview.log { + roll_size 10MB + roll_keep 5 + } + } +} + +dana.195-95-133-47.sslip.io { + encode zstd gzip + + header { + X-Robots-Tag "noindex, nofollow" + -Server + } + + reverse_proxy 172.17.0.1:8082 { + header_up X-Real-IP {remote_host} + } + + request_body { + max_size 20MB + } + + log { + output file /var/log/caddy/dana-medical-preview.log { + roll_size 10MB + roll_keep 5 + } + } +} diff --git a/.deploy/remote-build-start-dana.sh b/.deploy/remote-build-start-dana.sh new file mode 100644 index 0000000..8ebe258 --- /dev/null +++ b/.deploy/remote-build-start-dana.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -eu + +cd /home/mikail/dana-medical +docker compose -p dana-medical -f docker-compose.prod.yml config --quiet +docker compose -p dana-medical -f docker-compose.prod.yml build api +docker compose -p dana-medical -f docker-compose.prod.yml create api + +media_volume="$(docker volume ls -q \ + --filter label=com.docker.compose.project=dana-medical \ + --filter label=com.docker.compose.volume=media)" +test -n "$media_volume" +docker run --rm \ + -v "$media_volume:/dest" \ + -v /home/mikail/dana-medical/media:/src:ro \ + postgres:16-alpine sh -c 'cp -a /src/. /dest/ && chown -R 10001:10001 /dest' + +docker compose -p dana-medical -f docker-compose.prod.yml up -d api +api_id="$(docker compose -p dana-medical -f docker-compose.prod.yml ps -q api)" +test -n "$api_id" +i=0 +status=starting +while [ "$i" -lt 45 ]; do + status="$(docker inspect --format '{{.State.Health.Status}}' "$api_id")" + [ "$status" = healthy ] && break + [ "$status" = unhealthy ] && docker logs --tail 80 "$api_id" && exit 1 + sleep 2 + i=$((i + 1)) +done +[ "$status" = healthy ] +printf 'api_health=%s\n' "$status" +printf 'media_volume=%s\n' "$media_volume" +printf 'media_files=%s\n' "$(docker run --rm -v "$media_volume:/data:ro" postgres:16-alpine sh -c 'find /data -type f | wc -l')" diff --git a/.deploy/remote-init-dana.sh b/.deploy/remote-init-dana.sh new file mode 100644 index 0000000..6fadc96 --- /dev/null +++ b/.deploy/remote-init-dana.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -eu + +root=/home/mikail/dana-medical +deploy="$root/.deploy" +test -f "$deploy/dana-source.tar.gz" +test -f "$deploy/dana-media.tar.gz" +test -f "$deploy/dana-predeploy.dump" +test -f "$deploy/.env" + +tar -xzf "$deploy/dana-source.tar.gz" -C "$root" +tar -xzf "$deploy/dana-media.tar.gz" -C "$root" + +umask 077 +db_password="$(openssl rand -hex 32)" +{ + printf 'POSTGRES_PASSWORD=%s\n' "$db_password" + printf 'SITE_URL=https://danamedical.ru\n' + printf 'DANA_PORT=8082\n' + printf 'ALLOWED_ORIGINS=https://danamedical.ru,https://www.danamedical.ru\n' + grep '^MEGAFON_API_URL=' "$deploy/.env" || true + grep '^MEGAFON_API_KEY=' "$deploy/.env" || true + printf 'SMTP_HOST=\n' + printf 'SMTP_PORT=465\n' + printf 'SMTP_USER=\n' + printf 'SMTP_PASSWORD=\n' + printf 'NOTIFY_FROM=\n' + printf 'NOTIFY_TO=\n' +} > "$root/.env" +chmod 600 "$root/.env" "$deploy/dana-predeploy.dump" +rm -f "$deploy/.env" + +test -f "$root/docker-compose.prod.yml" +test -f "$root/Dockerfile" +test -d "$root/media" +printf 'source_files=%s\n' "$(find "$root" -path "$deploy" -prune -o -type f -print | wc -l)" +printf 'media_files=%s\n' "$(find "$root/media" -type f | wc -l)" +printf 'env_mode=%s\n' "$(stat -c %a "$root/.env")" diff --git a/.deploy/remote-install-dana-caddy.sh b/.deploy/remote-install-dana-caddy.sh new file mode 100644 index 0000000..0969e35 --- /dev/null +++ b/.deploy/remote-install-dana-caddy.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -eu + +source_file=/home/mikail/dana-medical/.deploy/dana-medical.caddy +custom_dir=/home/kadir/timechecker/caddy-custom +target_file="$custom_dir/dana-medical.caddy" +container=timechecker-caddy-1 + +test -s "$source_file" +if docker run --rm -v "$custom_dir:/target:ro" alpine:3.22 test -e /target/dana-medical.caddy; then + echo 'target_already_exists' + exit 1 +fi + +docker run --rm \ + -v /home/mikail/dana-medical/.deploy:/source:ro \ + -v "$custom_dir:/target" \ + alpine:3.22 cp /source/dana-medical.caddy /target/dana-medical.caddy + +rollback() { + docker run --rm -v "$custom_dir:/target" alpine:3.22 rm -f /target/dana-medical.caddy >/dev/null 2>&1 || true +} +trap rollback INT TERM HUP + +if ! docker exec "$container" caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile; then + rollback + echo 'caddy_validation_failed' + exit 1 +fi + +if ! docker exec "$container" caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile; then + rollback + docker exec "$container" caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1 || true + echo 'caddy_reload_failed' + exit 1 +fi + +trap - INT TERM HUP +docker exec "$container" caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null | grep -q 'new.danamedical.ru' +echo 'caddy_route=installed' diff --git a/.deploy/remote-restore-dana.sh b/.deploy/remote-restore-dana.sh new file mode 100644 index 0000000..5d21a28 --- /dev/null +++ b/.deploy/remote-restore-dana.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -eu + +cd /home/mikail/dana-medical +dump=.deploy/dana-predeploy.dump +test -s "$dump" + +existing="$(docker compose -p dana-medical -f docker-compose.prod.yml exec -T db psql -U dana -d dana -Atqc "select count(*) from information_schema.tables where table_schema='public'")" +[ "$existing" = 0 ] + +docker compose -p dana-medical -f docker-compose.prod.yml exec -T db \ + pg_restore -U dana -d dana --exit-on-error --no-owner --no-privileges < "$dump" + +docker compose -p dana-medical -f docker-compose.prod.yml exec -T db psql -U dana -d dana -At \ + -c "select 'admins='||count(*) from admin_users; select 'services='||count(*) from services; select 'leads='||count(*) from leads; select 'pbx_calls='||count(*) from pbx_calls;" diff --git a/.deploy/remote-secure-dana.sh b/.deploy/remote-secure-dana.sh new file mode 100644 index 0000000..1bc0b2e --- /dev/null +++ b/.deploy/remote-secure-dana.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +root=/home/mikail/dana-medical +chmod 700 "$root/.deploy" +find "$root/.deploy" -maxdepth 1 -type f -exec chmod 600 {} \; +if [ -f "$root/remote-build-start-dana.sh" ]; then + mv "$root/remote-build-start-dana.sh" "$root/.deploy/" + chmod 600 "$root/.deploy/remote-build-start-dana.sh" +fi +printf 'deploy_mode=%s\n' "$(stat -c %a "$root/.deploy")" +printf 'env_mode=%s\n' "$(stat -c %a "$root/.env")" diff --git a/.deploy/remote-smoke-dana.sh b/.deploy/remote-smoke-dana.sh new file mode 100644 index 0000000..a8e50af --- /dev/null +++ b/.deploy/remote-smoke-dana.sh @@ -0,0 +1,49 @@ +#!/bin/sh +set -eu + +cd /home/mikail/dana-medical +origin=http://172.17.0.1:8082 +marker="DANA-DEPLOY-CHECK-$(date +%Y%m%d%H%M%S)" +body="/tmp/dana-lead-check-$$.json" +cleanup() { + docker compose -p dana-medical -f docker-compose.prod.yml exec -T db \ + psql -U dana -d dana -v ON_ERROR_STOP=1 -c "delete from leads where name='$marker'" >/dev/null 2>&1 || true + rm -f "$body" +} +trap cleanup EXIT INT TERM + +for path in / /services/ /company/staff/ /price/ /checkup/ /contacts/ /robots.txt /sitemap.xml /admin/ /api/v1/health /api/v1/forms; do + code="$(curl -sS -o /dev/null -w '%{http_code}' "$origin$path")" + [ "$code" = 200 ] + printf '%s=%s\n' "$path" "$code" +done + +curl -fsS "$origin/" | grep -q '/dev/null 2>&1 || true +} +trap rollback INT TERM HUP + +if ! docker exec "$container" caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile; then + rollback + echo 'caddy_validation_failed' + exit 1 +fi + +if ! docker exec "$container" caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile; then + rollback + docker exec "$container" caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1 || true + echo 'caddy_reload_failed' + exit 1 +fi + +trap - INT TERM HUP +docker run --rm -v "$custom_dir:/target" alpine:3.22 rm -f /target/dana-medical.caddy.previous +docker exec "$container" caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null | grep -q 'danamedical.ru' +echo 'caddy_dana_routes=installed' diff --git a/.deploy/remote-verify-dana-preview.sh b/.deploy/remote-verify-dana-preview.sh new file mode 100644 index 0000000..4d931c2 --- /dev/null +++ b/.deploy/remote-verify-dana-preview.sh @@ -0,0 +1,34 @@ +#!/bin/sh +set -eu + +origin=https://dana.195-95-133-47.sslip.io +headers="/tmp/dana-preview-headers-$$" +body="/tmp/dana-preview-body-$$" +cleanup() { rm -f "$headers" "$body"; } +trap cleanup EXIT INT TERM + +i=0 +status=000 +while [ "$i" -lt 30 ]; do + status="$(curl -sS --max-time 10 -D "$headers" -o "$body" -w '%{http_code}' "$origin/" 2>/dev/null || true)" + [ "$status" = 200 ] && break + sleep 2 + i=$((i + 1)) +done +[ "$status" = 200 ] +grep -qi '^x-robots-tag: noindex, nofollow' "$headers" +grep -q '/dev/null | openssl x509 -noout -subject -issuer -dates diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3a0da2c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.env +.env.* +!.env.example +.work +.gocache +.gotmp +media +web/node_modules +web/dist +web/.ssr +*.exe +*.log diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2a83b68 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Подключение к базе. Для локальной установки PostgreSQL порт 5432, +# для варианта с docker compose — 5433. +DATABASE_URL=postgres://dana:CHANGE_ME@localhost:5432/dana?sslmode=disable + +APP_ADDR=:8080 +MEDIA_DIR=./media +ALLOWED_ORIGINS=http://localhost:5173 +DEBUG=true + +# Готовый HTML сайта после npm run build в web. +WEB_DIR=./web/dist +# Один и тот же origin при сборке web и запуске Go. Для боевого сайта укажите HTTPS-домен. +SITE_URL=http://127.0.0.1:8080 + +# Актуальный HTML из БД на запросе; кеш обновляется при изменении данных. +# На сервере нужны Node.js, web/.ssr и установленные зависимости web. +SSR_RENDERER=./web/scripts/render-page.mjs +NODE_BINARY=node +# SSR_RENDERER=off — только снимки сборки, требующие ручной пересборки после правок. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c658f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +/server +/server.exe +/server.exe~ +/media/ +/tmp/ +.env +*.log +.gocache/ +.gotmp/ +.work/ +.idea/ +__pycache__/ +migrations/import/seed_*.sql +migrations/import/fix*.sql +migrations/import/*.csv +migrations/import/*_report.txt +migrations/import/*_plan.txt +web/node_modules/ +web/dist/ +web/.ssr/ +tmp_seed/ +backups/ +.deploy/*.tar.gz +.deploy/*.dump diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f1093e7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM node:24-alpine AS web-build +WORKDIR /src +COPY web/package.json web/package-lock.json ./web/ +RUN cd web && npm ci +COPY seo ./seo +COPY web ./web +RUN cd web && npm run build:client && npx vite build --ssr src/entry-server.jsx --outDir .ssr && npm prune --omit=dev + +FROM golang:1.26-alpine AS build +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server + +FROM node:24-alpine +RUN apk add --no-cache ca-certificates tzdata && adduser -D -u 10001 app +COPY --from=build /out/server /usr/local/bin/server +WORKDIR /app +COPY --from=web-build /src/web/dist ./web/dist +COPY --from=web-build /src/web/.ssr ./web/.ssr +COPY --from=web-build /src/web/node_modules ./web/node_modules +COPY web/package.json ./web/package.json +COPY web/scripts/render-page.mjs web/scripts/seo-document.mjs ./web/scripts/ +ENV WEB_DIR=/app/web/dist SSR_RENDERER=/app/web/scripts/render-page.mjs +USER app +EXPOSE 8080 +ENTRYPOINT ["server"] diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..16ca929 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,140 @@ +package main + +import ( + "context" + "errors" + "log/slog" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "dana/internal/api" + "dana/internal/bitrix" + "dana/internal/config" + "dana/internal/megafon" + "dana/internal/notify" + "dana/internal/onec" + "dana/internal/prodoctorov" + "dana/internal/sberhealth" + "dana/internal/storage" +) + +func main() { + if err := run(); err != nil { + slog.Error("fatal", "err", err) + os.Exit(1) + } +} + +func run() error { + cfg, err := config.Load() + if err != nil { + return err + } + + level := slog.LevelInfo + if cfg.Debug { + level = slog.LevelDebug + } + slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level}))) + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + pd, err := prodoctorov.New(cfg.ProdoctorovCookie, cfg.ProdoctorovLPU) + if err != nil { + return err + } + sh, err := sberhealth.New(cfg.SberHealthCookie, cfg.SberHealthClinic) + if err != nil { + return err + } + + store, err := storage.New(ctx, cfg.DatabaseURL) + if err != nil { + return err + } + defer store.Close() + + if err := store.Migrate(ctx); err != nil { + return err + } + + // Первый администратор заводится один раз, если таблица пуста. + if err := store.EnsureAdmin(ctx, cfg.AdminEmail, cfg.AdminPassword); err != nil { + return err + } + + if err := api.SetTrustedProxies(cfg.TrustedProxies); err != nil { + return err + } + handler := api.New(store, cfg.MediaDir) + if err := handler.FailInterruptedReports(ctx); err != nil { + slog.Warn("mark interrupted reports", "err", err) + } + handler.WithPBX(megafon.New(cfg.MegafonURL, cfg.MegafonKey)) + if cfg.OchevidnoMegafonURL != "" && cfg.OchevidnoMegafonKey != "" { + handler.WithClinicPBX("ochevidno", megafon.New(cfg.OchevidnoMegafonURL, cfg.OchevidnoMegafonKey)) + } + go handler.RunPBXSync(ctx) + handler.WithOneC(onec.New(cfg.OnecURL, cfg.OnecKey, cfg.OnecAuthHeader)) + handler.WithOneCWebhook(cfg.OnecWebhookKey) + handler.WithOneCTuning(time.Duration(cfg.OnecSyncSecs)*time.Second, cfg.OnecLongPoll) + go handler.RunOneCSync(ctx) + handler.WithBitrix(bitrix.New(cfg.BitrixWebhookURL)) + go handler.RunBookingConfirm(ctx) + handler.WithSMSC(cfg.SMSCLogin, cfg.SMSCPassword) + handler.WithSMSCCallbackToken(cfg.SMSCCallbackToken) + handler.WithProdoctorov(pd) + handler.WithSberHealth(sh) + if err := handler.WithWebsite(cfg.WebDir, cfg.SiteURL); err != nil { + return err + } + if err := handler.WithSSR(cfg.SSRRenderer, cfg.NodeBinary); err != nil { + return err + } + if cfg.SMTPHost != "" { + handler.WithNotifier(¬ify.Email{ + Host: cfg.SMTPHost, + Port: cfg.SMTPPort, + User: cfg.SMTPUser, + Password: cfg.SMTPPassword, + From: cfg.NotifyFrom, + To: cfg.NotifyTo, + SiteURL: cfg.SiteURL, + }) + slog.Info("lead notifications by email", "smtp", cfg.SMTPHost, "to", cfg.NotifyTo) + } else { + slog.Warn("SMTP_HOST is not set: lead notifications are disabled") + } + + srv := &http.Server{ + Addr: cfg.Addr, + Handler: handler.Routes(), + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 60 * time.Second, + } + + errCh := make(chan error, 1) + go func() { + slog.Info("listening", "addr", cfg.Addr) + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-ctx.Done(): + slog.Info("shutting down") + } + + shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) + defer cancel() + return srv.Shutdown(shutdownCtx) +} diff --git a/deploy.env.example b/deploy.env.example new file mode 100644 index 0000000..5574914 --- /dev/null +++ b/deploy.env.example @@ -0,0 +1,24 @@ +# Скопировать в .env только на VPS и заменить значения. +POSTGRES_PASSWORD=CHANGE_ME_LONG_RANDOM_VALUE +ADMIN_EMAIL=admin@example.ru +ADMIN_PASSWORD=CHANGE_ME_LONG_RANDOM_VALUE + +# Канонический адрес нового сайта. До переключения DNS приложение можно +# проверять через SSH-туннель, не публикуя его в Caddy. +SITE_URL=https://danamedical.ru +DANA_PORT=8082 +ALLOWED_ORIGINS=https://danamedical.ru,https://www.danamedical.ru + +# Уведомления о новых заявках. Если SMTP_HOST пуст, заявки сохраняются в +# админке, но письма не отправляются. +SMTP_HOST= +SMTP_PORT=465 +SMTP_USER= +SMTP_PASSWORD= +NOTIFY_FROM= +NOTIFY_TO= + +# Виртуальная АТС МегаФон. Без ключа раздел работает с уже загруженными +# данными, но автоматическая синхронизация отключена. +MEGAFON_API_URL=vats970345.megapbx.ru +MEGAFON_API_KEY= diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..7e46242 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,53 @@ +services: + db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: dana + POSTGRES_USER: dana + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} + volumes: + - db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U dana -d dana"] + interval: 5s + timeout: 3s + retries: 20 + + api: + build: + context: . + restart: unless-stopped + env_file: + - .env + environment: + DATABASE_URL: postgres://dana:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@db:5432/dana?sslmode=disable + APP_ADDR: ":8080" + MEDIA_DIR: /data/media + WEB_DIR: /app/web/dist + SSR_RENDERER: /app/web/scripts/render-page.mjs + NODE_BINARY: node + SITE_URL: ${SITE_URL:?SITE_URL is required} + DEBUG: "false" + # Caddy уже работает в контейнере Ability Center. Публикуем DANA только + # на docker0 хоста: из интернета порт недоступен, Caddy обращается к нему + # как к 172.17.0.1:8082. + ports: + - "172.17.0.1:${DANA_PORT:-8082}:8080" + volumes: + - media:/data/media + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8080/api/v1/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + security_opt: + - no-new-privileges:true + +volumes: + db-data: + media: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..31e93c7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,44 @@ +services: + db: + image: postgres:16-alpine + environment: + POSTGRES_DB: dana + POSTGRES_USER: dana + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dana} + ports: + - "5433:5432" + volumes: + - db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U dana -d dana"] + interval: 5s + timeout: 3s + retries: 10 + + api: + build: . + environment: + DATABASE_URL: postgres://dana:${POSTGRES_PASSWORD:-dana}@db:5432/dana?sslmode=disable + APP_ADDR: ":8080" + MEDIA_DIR: /data/media + DEBUG: "true" + SITE_URL: ${SITE_URL:-} + # Уведомления о заявках на почту; пустой SMTP_HOST — отключены. + # Для Яндекс Почты: smtp.yandex.ru, порт 465, пароль приложения, а не пароль ящика. + SMTP_HOST: ${SMTP_HOST:-} + SMTP_PORT: ${SMTP_PORT:-465} + SMTP_USER: ${SMTP_USER:-} + SMTP_PASSWORD: ${SMTP_PASSWORD:-} + NOTIFY_FROM: ${NOTIFY_FROM:-} + NOTIFY_TO: ${NOTIFY_TO:-} + ports: + - "8080:8080" + volumes: + - media:/data/media + depends_on: + db: + condition: service_healthy + +volumes: + db-data: + media: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..68511db --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module dana + +go 1.26.0 + +require ( + github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 + github.com/jackc/pgx/v5 v5.9.2 + golang.org/x/crypto v0.56.0 + golang.org/x/text v0.41.0 +) + +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..62d0ff1 --- /dev/null +++ b/go.sum @@ -0,0 +1,30 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 h1:n+nk0bNe2+gVbRI8WRbLFVwwcBQ0rr5p+gzkKb6ol8c= +github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7/go.mod h1:GPpMrAfHdb8IdQ1/R2uIRBsNfnPnwsYE9YYI5WyY1zw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/api/admin.go b/internal/api/admin.go new file mode 100644 index 0000000..269ff3d --- /dev/null +++ b/internal/api/admin.go @@ -0,0 +1,318 @@ +package api + +import ( + "encoding/json" + "errors" + "io" + "net" + "net/http" + "strconv" + "strings" + "time" + + "dana/internal/storage" +) + +const sessionCookie = "dana_admin" + +type ctxKey string + +const userKey ctxKey = "admin_user" + +// trustedProxies — откуда принимаем X-Forwarded-For / X-Real-IP. По умолчанию сам сервер и Docker-сети: +// порт сайта открыт только внутри сервера (172.17.0.1), снаружи к нему ходит лишь Caddy. +// Заголовки от любого другого адреса — подделка клиента для обхода лимитов по IP. +var trustedProxies = mustParseProxies("127.0.0.0/8,::1/128,172.16.0.0/12") + +// SetTrustedProxies задаёт доверенные прокси (TRUSTED_PROXIES): адреса или подсети через запятую, +// например адрес Caddy в Docker-сети. Пустая строка оставляет значение по умолчанию. +func SetTrustedProxies(spec string) error { + if strings.TrimSpace(spec) == "" { + return nil + } + nets, err := parseProxies(spec) + if err != nil { + return err + } + trustedProxies = nets + return nil +} + +func parseProxies(spec string) ([]*net.IPNet, error) { + var out []*net.IPNet + for _, part := range strings.Split(spec, ",") { + part = strings.TrimSpace(part) + if part == "" { + continue + } + if !strings.Contains(part, "/") { + if ip := net.ParseIP(part); ip != nil && ip.To4() != nil { + part += "/32" + } else { + part += "/128" + } + } + _, n, err := net.ParseCIDR(part) + if err != nil { + return nil, errors.New("TRUSTED_PROXIES: не адрес и не подсеть: " + part) + } + out = append(out, n) + } + return out, nil +} + +func mustParseProxies(spec string) []*net.IPNet { + n, err := parseProxies(spec) + if err != nil { + panic(err) + } + return n +} + +func isTrustedProxy(ip net.IP) bool { + for _, n := range trustedProxies { + if n.Contains(ip) { + return true + } + } + return false +} + +// clientIP определяет адрес посетителя. Заголовки прокси учитываем, только если соединение +// пришло от доверенного прокси; иначе берём адрес соединения. +// Первым смотрим X-Forwarded-For: его Caddy формирует сам (входящий от посетителя отбрасывает), +// а X-Real-IP Caddy без отдельной настройки пропускает от посетителя как есть. +func clientIP(r *http.Request) string { + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + host = r.RemoteAddr + } + remote := net.ParseIP(host) + if remote == nil || !isTrustedProxy(remote) { + return host + } + // справа налево: правые элементы дописали наши прокси, первый чужой — клиент + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + parts := strings.Split(xff, ",") + for i := len(parts) - 1; i >= 0; i-- { + ip := net.ParseIP(strings.TrimSpace(parts[i])) + if ip == nil { + break + } + if !isTrustedProxy(ip) { + return ip.String() + } + } + } + if real := strings.TrimSpace(r.Header.Get("X-Real-IP")); net.ParseIP(real) != nil { + return real + } + return host +} + +// drainBody дочитывает небольшое тело запроса перед отказом. Если ответить, не прочитав его, +// а клиент дошлёт тело уже после закрытия соединения, Windows рвёт соединение (RST), +// и клиент вместо ответа 401 получает ошибку сети. +func drainBody(r *http.Request) { + _, _ = io.Copy(io.Discard, io.LimitReader(r.Body, 1<<20)) +} + +// requireAuth пропускает только запросы с действующей сессией. +func (a *API) requireAuth(next func(http.ResponseWriter, *http.Request, *storage.AdminUser)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + c, err := r.Cookie(sessionCookie) + if err != nil || c.Value == "" { + drainBody(r) + fail(w, http.StatusUnauthorized, "unauthorized", "Требуется вход") + return + } + u, err := a.store.UserByToken(r.Context(), c.Value) + if err != nil { + drainBody(r) + fail(w, http.StatusUnauthorized, "unauthorized", "Сессия истекла") + return + } + next(w, r, u) + } +} + +func (a *API) adminLogin(w http.ResponseWriter, r *http.Request) { + // защита от перебора пароля: не больше 10 попыток входа с одного IP за 15 минут + if a.tooMany(w, "login:"+clientIP(r), 10, 15*time.Minute) { + return + } + var in struct { + Email string `json:"email"` + Password string `json:"password"` + } + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + token, u, err := a.store.Login(r.Context(), + strings.TrimSpace(strings.ToLower(in.Email)), in.Password, + clientIP(r), r.UserAgent()) + if errors.Is(err, storage.ErrBadCredentials) { + fail(w, http.StatusUnauthorized, "bad_credentials", "Неверный логин или пароль") + return + } + if err != nil { + handleErr(w, err) + return + } + http.SetCookie(w, &http.Cookie{ + Name: sessionCookie, + Value: token, + Path: "/", + HttpOnly: true, + Secure: true, + SameSite: http.SameSiteLaxMode, + MaxAge: 12 * 3600, + }) + ok(w, u, nil) +} + +func (a *API) adminLogout(w http.ResponseWriter, r *http.Request) { + if c, err := r.Cookie(sessionCookie); err == nil { + _ = a.store.Logout(r.Context(), c.Value) + } + http.SetCookie(w, &http.Cookie{Name: sessionCookie, Value: "", Path: "/", HttpOnly: true, Secure: true, SameSite: http.SameSiteLaxMode, MaxAge: -1}) + ok(w, map[string]string{"status": "ok"}, nil) +} + +func (a *API) adminMe(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + ok(w, u, nil) +} + +// ---------- Услуги ---------- + +func (a *API) adminServices(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminServices(r.Context(), r.URL.Query().Get("q")) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminUpdateService(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var in storage.ServiceUpdate + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if strings.TrimSpace(in.Name) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Название не может быть пустым") + return + } + if err := a.store.AdminUpdateService(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "service", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// ---------- Прайс ---------- + +func (a *API) adminPriceSections(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminPriceSections(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, nil) +} + +func (a *API) adminPriceItems(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + q := r.URL.Query() + sectionID, _ := strconv.ParseInt(q.Get("section_id"), 10, 64) + limit, _ := strconv.Atoi(q.Get("limit")) + if limit <= 0 || limit > 200 { + limit = 50 + } + offset, _ := strconv.Atoi(q.Get("offset")) + if offset < 0 { + offset = 0 + } + list, total, err := a.store.AdminPriceItems(r.Context(), sectionID, q.Get("q"), limit, offset) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": total, "limit": limit, "offset": offset}) +} + +func validPriceInput(in storage.PriceItemInput) string { + if strings.TrimSpace(in.Name) == "" { + return "Название не может быть пустым" + } + if in.Price < 0 { + return "Цена не может быть отрицательной" + } + if in.SectionID == 0 { + return "Не выбран раздел" + } + return "" +} + +func (a *API) adminUpdatePriceItem(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var in storage.PriceItemInput + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if msg := validPriceInput(in); msg != "" { + fail(w, http.StatusUnprocessableEntity, "validation", msg) + return + } + if err := a.store.AdminUpdatePriceItem(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "price_item", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminCreatePriceItem(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + var in storage.PriceItemInput + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if msg := validPriceInput(in); msg != "" { + fail(w, http.StatusUnprocessableEntity, "validation", msg) + return + } + id, err := a.store.AdminCreatePriceItem(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "price_item", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeletePriceItem(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeletePriceItem(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "price_item", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_banners.go b/internal/api/admin_banners.go new file mode 100644 index 0000000..8df9b86 --- /dev/null +++ b/internal/api/admin_banners.go @@ -0,0 +1,161 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + "unicode/utf8" + + "dana/internal/storage" +) + +// Совпадает со значением по умолчанию в миграции 0006. +const defaultBannerDisclaimer = "Имеются противопоказания, необходима консультация специалиста" + +const ( + maxBannerChips = 6 + maxBannerChipLen = 40 +) + +func (a *API) adminBanners(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminBanners(r.Context(), r.URL.Query().Get("position")) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func decodeBanner(w http.ResponseWriter, r *http.Request) (storage.BannerInput, bool) { + var in storage.BannerInput + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return in, false + } + in.Position = strings.TrimSpace(in.Position) + in.Layout = strings.TrimSpace(in.Layout) + in.Theme = strings.TrimSpace(in.Theme) + in.Title = strings.TrimSpace(in.Title) + in.Subtitle = strings.TrimSpace(in.Subtitle) + in.Badge = strings.TrimSpace(in.Badge) + in.LinkURL = strings.TrimSpace(in.LinkURL) + in.Disclaimer = strings.TrimSpace(in.Disclaimer) + if in.Position == "" { + in.Position = "home_hero" + } + if in.Layout == "" { + in.Layout = "image" + } + if in.Theme == "" { + in.Theme = "navy" + } + // Пустое поле из формы перетёрло бы значение по умолчанию из схемы, и реклама + // медуслуг ушла бы на сайт без обязательного предупреждения (ФЗ «О рекламе», ст. 24). + if in.Disclaimer == "" { + in.Disclaimer = defaultBannerDisclaimer + } + + switch in.Layout { + case "image": + // Текст на таких слайдах нарисован на картинке, поэтому достаточно + // либо картинки, либо заголовка — но хоть что-то показывать нужно. + if in.ImageID == nil && in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Нужна картинка или заголовок") + return in, false + } + case "text": + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "У текстового слайда нужен заголовок") + return in, false + } + case "doctor": + if in.Title == "" || in.ImageID == nil { + fail(w, http.StatusUnprocessableEntity, "validation", "У слайда с врачом нужны заголовок и фото") + return in, false + } + default: + fail(w, http.StatusUnprocessableEntity, "validation", "Неизвестный вид слайда") + return in, false + } + if in.Theme != "navy" && in.Theme != "lilac" && in.Theme != "orange" { + fail(w, http.StatusUnprocessableEntity, "validation", "Неизвестный цвет слайда") + return in, false + } + + chips := make([]string, 0, len(in.Chips)) + for _, c := range in.Chips { + if c = strings.TrimSpace(c); c == "" { + continue + } + if utf8.RuneCountInString(c) > maxBannerChipLen { + fail(w, http.StatusUnprocessableEntity, "validation", "Плашка длиннее 40 символов: «"+c+"»") + return in, false + } + chips = append(chips, c) + } + if len(chips) > maxBannerChips { + fail(w, http.StatusUnprocessableEntity, "validation", "Не больше 6 плашек на слайде") + return in, false + } + in.Chips = chips + + // Ссылка только на страницу сайта или на http(s): javascript: и прочее отсекаем. + if in.LinkURL != "" && !strings.HasPrefix(in.LinkURL, "/") && + !strings.HasPrefix(in.LinkURL, "https://") && !strings.HasPrefix(in.LinkURL, "http://") { + fail(w, http.StatusUnprocessableEntity, "validation", + "Ссылка должна вести на страницу сайта (/...) или начинаться с https://") + return in, false + } + if in.StartsAt != nil && in.EndsAt != nil && !in.EndsAt.After(*in.StartsAt) { + fail(w, http.StatusUnprocessableEntity, "validation", "Окончание показа должно быть позже начала") + return in, false + } + return in, true +} + +func (a *API) adminCreateBanner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeBanner(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateBanner(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "banner", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateBanner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeBanner(w, r) + if !valid { + return + } + if err := a.store.AdminUpdateBanner(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "banner", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteBanner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteBanner(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "banner", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_banners_test.go b/internal/api/admin_banners_test.go new file mode 100644 index 0000000..412c097 --- /dev/null +++ b/internal/api/admin_banners_test.go @@ -0,0 +1,41 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +func TestDecodeBannerRules(t *testing.T) { + cases := []struct { + name string + body string + wantOK bool + wantText string + }{ + {"text slide", `{"layout":"text","theme":"lilac","title":"Программы чек-апов"}`, true, ""}, + {"doctor slide", `{"layout":"doctor","title":"Уролог из Москвы","image_id":5,"chips":["26 лет стажа"," "]}`, true, ""}, + {"image slide by picture", `{"layout":"image","image_id":5}`, true, ""}, + {"text slide without title", `{"layout":"text","theme":"lilac"}`, false, "заголовок"}, + {"doctor slide without photo", `{"layout":"doctor","title":"Уролог"}`, false, "фото"}, + {"unknown layout", `{"layout":"video","title":"x"}`, false, "вид"}, + {"unknown theme", `{"layout":"text","theme":"red","title":"x"}`, false, "цвет"}, + {"too many chips", `{"layout":"text","title":"x","chips":["1","2","3","4","5","6","7"]}`, false, "6 плашек"}, + {"javascript link", `{"layout":"text","title":"x","link_url":"javascript:alert(1)"}`, false, "Ссылка"}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/api/v1/admin/banners", strings.NewReader(tc.body)) + in, valid := decodeBanner(w, r) + if valid != tc.wantOK || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: valid=%v body=%s, want valid=%v containing %q", tc.name, valid, w.Body.String(), tc.wantOK, tc.wantText) + continue + } + if valid && in.Disclaimer == "" { + t.Errorf("%s: disclaimer must default to the ФЗ «О рекламе» warning", tc.name) + } + if tc.name == "doctor slide" && len(in.Chips) != 1 { + t.Errorf("empty chips must be dropped, got %q", in.Chips) + } + } +} diff --git a/internal/api/admin_checkups.go b/internal/api/admin_checkups.go new file mode 100644 index 0000000..3984b7f --- /dev/null +++ b/internal/api/admin_checkups.go @@ -0,0 +1,147 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + "unicode/utf8" + + "dana/internal/storage" +) + +const ( + maxCheckupItems = 100 + maxCheckupItemLen = 300 +) + +type checkupRequest struct { + Slug string `json:"slug"` + Audience string `json:"audience"` + Name string `json:"name"` + Lead string `json:"lead"` + Price *float64 `json:"price"` + OldPrice *float64 `json:"old_price"` + ImageID *int64 `json:"image_id"` + Items []string `json:"items"` + Sort int `json:"sort"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +func decodeCheckup(w http.ResponseWriter, r *http.Request) (storage.CheckupInput, bool) { + var req checkupRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.CheckupInput{}, false + } + in := storage.CheckupInput{ + Audience: strings.TrimSpace(req.Audience), + Name: strings.TrimSpace(req.Name), + Lead: strings.TrimSpace(req.Lead), + Price: req.Price, + OldPrice: req.OldPrice, + ImageID: req.ImageID, + Sort: req.Sort, + Published: req.Published, + SEOTitle: strings.TrimSpace(req.SEOTitle), + SEODesc: strings.TrimSpace(req.SEODesc), + SEOH1: strings.TrimSpace(req.SEOH1), + } + if in.Name == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название программы") + return in, false + } + slug, valid := resolveSlug(w, req.Slug, in.Name) + if !valid { + return in, false + } + in.Slug = slug + + if in.Audience != "adults" && in.Audience != "kids" { + fail(w, http.StatusUnprocessableEntity, "validation", "Выберите, для кого программа: взрослые или дети") + return in, false + } + if in.Price != nil && *in.Price < 0 || in.OldPrice != nil && *in.OldPrice < 0 { + fail(w, http.StatusUnprocessableEntity, "validation", "Цена не может быть отрицательной") + return in, false + } + // Цена без скидки на сайте зачёркивается — она обязана быть больше действующей. + if in.OldPrice != nil && (in.Price == nil || *in.OldPrice <= *in.Price) { + fail(w, http.StatusUnprocessableEntity, "validation", + "Цена без скидки должна быть больше цены программы") + return in, false + } + if in.Sort == 0 { + in.Sort = 100 + } + + for _, item := range req.Items { + item = strings.TrimSpace(item) + if item == "" { + continue + } + if utf8.RuneCountInString(item) > maxCheckupItemLen { + fail(w, http.StatusUnprocessableEntity, "validation", + "Пункт состава длиннее 300 символов: "+string([]rune(item)[:40])+"…") + return in, false + } + in.Items = append(in.Items, item) + } + if len(in.Items) > maxCheckupItems { + fail(w, http.StatusUnprocessableEntity, "validation", "В составе программы не больше 100 пунктов") + return in, false + } + return in, true +} + +func (a *API) adminCheckups(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminCheckups(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateCheckup(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeCheckup(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateCheckup(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "checkup", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id, "slug": in.Slug}}) +} + +func (a *API) adminUpdateCheckup(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeCheckup(w, r) + if !valid { + return + } + oldSlug, err := a.store.PublishedCheckupSlug(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + if err := a.store.AdminUpdateCheckup(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + if oldSlug != "" && in.Published { + a.keepOldPath(r.Context(), checkupWebPath(oldSlug), checkupWebPath(in.Slug)) + } + a.store.Audit(r.Context(), u.ID, "update", "checkup", id, clientIP(r)) + ok(w, map[string]any{"id": id, "slug": in.Slug}, nil) +} diff --git a/internal/api/admin_doctors.go b/internal/api/admin_doctors.go new file mode 100644 index 0000000..dc98af1 --- /dev/null +++ b/internal/api/admin_doctors.go @@ -0,0 +1,206 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + "dana/internal/storage" +) + +var bookingProviders = map[string]bool{"none": true, "native": true, "medflex": true} +var bioKinds = map[string]bool{ + "education": true, "experience": true, "training": true, + "award": true, "conference": true, +} + +func (a *API) adminDoctors(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminDoctors(r.Context(), r.URL.Query().Get("q")) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminUpdateDoctor(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var in storage.DoctorUpdate + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if strings.TrimSpace(in.LastName) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Фамилия не может быть пустой") + return + } + if strings.TrimSpace(in.Slug) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Адрес (slug) не может быть пустым") + return + } + if in.Booking == "" { + in.Booking = "none" + } + if !bookingProviders[in.Booking] { + fail(w, http.StatusUnprocessableEntity, "validation", "Недопустимый способ записи") + return + } + if in.Rating != nil && (*in.Rating < 1 || *in.Rating > 5) { + fail(w, http.StatusUnprocessableEntity, "validation", "Рейтинг должен быть от 1,0 до 5,0") + return + } + // Medflex без ссылки работать не будет — не даём сохранить заведомо мёртвую кнопку. + if in.Booking == "medflex" && strings.TrimSpace(in.MedflexUser) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", + "Для записи через Medflex нужна ссылка") + return + } + oldSlug, err := a.store.PublishedDoctorSlug(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + oldPath := a.doctorPathBySlug(r.Context(), oldSlug) + if err := a.store.AdminUpdateDoctor(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + if oldPath != "" && in.Published { + a.keepOldPath(r.Context(), oldPath, a.doctorPathBySlug(r.Context(), in.Slug)) + } + a.store.Audit(r.Context(), u.ID, "update", "doctor", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +type doctorLinksRequest struct { + ServiceIDs []int64 `json:"service_ids"` + BranchIDs []int64 `json:"branch_ids"` +} + +// adminSetDoctorLinks сохраняет услуги, которые ведёт врач, и филиалы, где он принимает. +func (a *API) adminSetDoctorLinks(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var in doctorLinksRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10)).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if in.ServiceIDs == nil { + in.ServiceIDs = []int64{} + } + if in.BranchIDs == nil { + in.BranchIDs = []int64{} + } + if len(in.ServiceIDs) > 500 || len(in.BranchIDs) > 50 { + fail(w, http.StatusUnprocessableEntity, "validation", "Слишком много связей") + return + } + if err := a.store.SetDoctorLinks(r.Context(), id, in.ServiceIDs, in.BranchIDs); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update_links", "doctor", id, clientIP(r)) + ok(w, map[string]any{"id": id, "services": len(in.ServiceIDs), "branches": len(in.BranchIDs)}, nil) +} + +// ---------- Биография врача ---------- + +func (a *API) adminBioEntries(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + doctorID, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id врача") + return + } + list, err := a.store.AdminBioEntries(r.Context(), doctorID) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) decodeBioEntry(w http.ResponseWriter, r *http.Request) (storage.BioEntryInput, bool) { + var in storage.BioEntryInput + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return in, false + } + if !bioKinds[in.Kind] { + fail(w, http.StatusUnprocessableEntity, "validation", "Недопустимый тип записи") + return in, false + } + if strings.TrimSpace(in.Title) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Заполните текст записи") + return in, false + } + return in, true +} + +func (a *API) adminCreateBioEntry(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, okReq := a.decodeBioEntry(w, r) + if !okReq { + return + } + if in.DoctorID == 0 { + fail(w, http.StatusUnprocessableEntity, "validation", "Не указан врач") + return + } + id, err := a.store.AdminCreateBioEntry(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "doctor_bio", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateBioEntry(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, okReq := a.decodeBioEntry(w, r) + if !okReq { + return + } + if err := a.store.AdminUpdateBioEntry(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "doctor_bio", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteBioEntry(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteBioEntry(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "doctor_bio", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// adminDoctorQuality — данные для «Дашборда врачей»: биография по видам, отзывы и заявки по врачам. +func (a *API) adminDoctorQuality(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + rep, err := a.store.DoctorQuality(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, rep, nil) +} diff --git a/internal/api/admin_documents.go b/internal/api/admin_documents.go new file mode 100644 index 0000000..c15948d --- /dev/null +++ b/internal/api/admin_documents.go @@ -0,0 +1,230 @@ +package api + +import ( + "encoding/json" + "errors" + "net/http" + "strconv" + "strings" + + "dana/internal/storage" +) + +// documentKinds — допустимые виды документов (как в CHECK таблицы documents). +var documentKinds = map[string]bool{ + "license": true, "certificate": true, "award": true, "supervisor": true, "other": true, +} + +type documentRequest struct { + Kind string `json:"kind"` + Title string `json:"title"` + Body string `json:"body"` + FileID *int64 `json:"file_id"` + PreviewID *int64 `json:"preview_id"` + IssuedAt *string `json:"issued_at"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func decodeDocument(w http.ResponseWriter, r *http.Request) (storage.DocumentInput, bool) { + var req documentRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.DocumentInput{}, false + } + in := storage.DocumentInput{ + Kind: req.Kind, + Title: strings.TrimSpace(req.Title), + Body: strings.TrimSpace(strings.ReplaceAll(req.Body, "\r", "")), + FileID: req.FileID, + PreviewID: req.PreviewID, + Sort: req.Sort, + Published: req.Published, + } + if in.Sort == 0 { + in.Sort = 100 + } + if !documentKinds[in.Kind] { + fail(w, http.StatusUnprocessableEntity, "validation", "Выберите вид документа") + return in, false + } + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название") + return in, false + } + var err error + if in.IssuedAt, err = parseDate(req.IssuedAt); err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Дата выдачи в формате ГГГГ-ММ-ДД") + return in, false + } + // лицензию или награду без скана и файла на сайте показать нечем + if in.Published && in.Kind != "supervisor" && in.FileID == nil && in.PreviewID == nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Чтобы показать документ на сайте, загрузите скан или PDF") + return in, false + } + return in, true +} + +// checkDocumentMedia не даёт поставить PDF на место картинки-скана. +func (a *API) checkDocumentMedia(w http.ResponseWriter, r *http.Request, in storage.DocumentInput) bool { + if in.PreviewID == nil { + return true + } + mime, err := a.store.MediaMime(r.Context(), *in.PreviewID) + if errors.Is(err, storage.ErrNotFound) || (err == nil && !strings.HasPrefix(mime, "image/")) { + fail(w, http.StatusUnprocessableEntity, "validation", "Скан для просмотра должен быть картинкой, а PDF загрузите в поле «Файл документа»") + return false + } + if err != nil { + handleErr(w, err) + return false + } + return true +} + +func (a *API) adminDocuments(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminDocuments(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateDocument(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeDocument(w, r) + if !valid || !a.checkDocumentMedia(w, r, in) { + return + } + id, err := a.store.AdminCreateDocument(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "document", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateDocument(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeDocument(w, r) + if !valid || !a.checkDocumentMedia(w, r, in) { + return + } + if err := a.store.AdminUpdateDocument(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "document", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteDocument(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteDocument(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "document", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// ---------- вопросы и ответы ---------- + +type faqRequest struct { + Question string `json:"question"` + Answer string `json:"answer"` + ServiceID *int64 `json:"service_id"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func decodeFAQ(w http.ResponseWriter, r *http.Request) (storage.FAQInput, bool) { + var req faqRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.FAQInput{}, false + } + in := storage.FAQInput{ + Question: strings.TrimSpace(req.Question), + Answer: strings.TrimSpace(strings.ReplaceAll(req.Answer, "\r", "")), + ServiceID: req.ServiceID, + Sort: req.Sort, + Published: req.Published, + } + if in.Sort == 0 { + in.Sort = 100 + } + if in.Question == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите вопрос") + return in, false + } + if in.Published && in.Answer == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Чтобы показать вопрос на сайте, напишите ответ") + return in, false + } + return in, true +} + +func (a *API) adminFAQ(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminFAQ(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateFAQ(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeFAQ(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateFAQ(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "faq", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateFAQ(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeFAQ(w, r) + if !valid { + return + } + if err := a.store.AdminUpdateFAQ(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "faq", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteFAQ(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteFAQ(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "faq", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_documents_test.go b/internal/api/admin_documents_test.go new file mode 100644 index 0000000..d9708cc --- /dev/null +++ b/internal/api/admin_documents_test.go @@ -0,0 +1,65 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +func TestDecodeDocumentRules(t *testing.T) { + cases := []struct { + name string + body string + wantCode int + wantText string + }{ + {"license with scan", `{"kind":"license","title":"Лицензия","preview_id":5,"issued_at":"2025-12-17","published":true}`, 200, ""}, + {"supervisor without files", `{"kind":"supervisor","title":"Минздрав","body":"Телефон: +7","published":true}`, 200, ""}, + {"hidden draft without files", `{"kind":"award","title":"Премия","published":false}`, 200, ""}, + {"unknown kind", `{"kind":"passport","title":"x"}`, 422, "вид документа"}, + {"no title", `{"kind":"license","title":" "}`, 422, "название"}, + {"published license without files", `{"kind":"license","title":"Лицензия","published":true}`, 422, "загрузите скан"}, + {"bad date", `{"kind":"license","title":"Лицензия","issued_at":"17.12.2025"}`, 422, "ГГГГ-ММ-ДД"}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(tc.body)) + _, valid := decodeDocument(w, r) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + } +} + +func TestDecodeFAQRules(t *testing.T) { + cases := []struct { + name string + body string + wantCode int + wantText string + }{ + {"published answer", `{"question":"Где парковка?","answer":"Во дворе\r\nбесплатно","published":true}`, 200, ""}, + {"draft without answer", `{"question":"Черновик","published":false}`, 200, ""}, + {"no question", `{"question":"","answer":"x"}`, 422, "вопрос"}, + {"published without answer", `{"question":"Где парковка?","published":true}`, 422, "напишите ответ"}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(tc.body)) + in, valid := decodeFAQ(w, r) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + if valid && strings.Contains(in.Answer, "\r") { + t.Errorf("%s: answer keeps CR", tc.name) + } + } +} diff --git a/internal/api/admin_inbox.go b/internal/api/admin_inbox.go new file mode 100644 index 0000000..df27c8f --- /dev/null +++ b/internal/api/admin_inbox.go @@ -0,0 +1,198 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + "time" + + "dana/internal/storage" +) + +// допустимые значения статусов — совпадают с CHECK в схеме +var leadStatuses = map[string]bool{"new": true, "in_progress": true, "done": true, "spam": true} +var reviewStatuses = map[string]bool{"pending": true, "published": true, "rejected": true} + +func pageParams(r *http.Request) (limit, offset int) { + limit = queryInt(r, "limit", 50) + if limit <= 0 || limit > 200 { + limit = 50 + } + offset = queryInt(r, "offset", 0) + if offset < 0 { + offset = 0 + } + return limit, offset +} + +// ---------- Заявки ---------- + +func (a *API) adminLeads(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + limit, offset := pageParams(r) + q := r.URL.Query() + var from, to *time.Time + for _, filter := range []struct { + key string + out **time.Time + }{{"from", &from}, {"to", &to}} { + if value := q.Get(filter.key); value != "" { + day, err := time.ParseInLocation("2006-01-02", value, time.Local) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректная дата в фильтре") + return + } + if filter.key == "to" { + day = day.AddDate(0, 0, 1) + } + *filter.out = &day + } + } + if from != nil && to != nil && !from.Before(*to) { + fail(w, http.StatusBadRequest, "bad_request", "Дата начала позже даты окончания") + return + } + list, total, err := a.store.AdminLeads(r.Context(), q.Get("status"), q.Get("q"), from, to, limit, offset) + if err != nil { + handleErr(w, err) + return + } + counts, err := a.store.LeadCounts(r.Context()) + if err != nil { + handleErr(w, err) + return + } + today, err := a.store.LeadTodayCount(r.Context()) + if err != nil { + handleErr(w, err) + return + } + // Просмотр заявок — это доступ к персональным данным, фиксируем в журнале. + a.store.Audit(r.Context(), u.ID, "view_leads", "lead", 0, clientIP(r)) + ok(w, list, map[string]any{"total": total, "limit": limit, "offset": offset, "counts": counts, "today": today}) +} + +func (a *API) adminUpdateLead(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var in storage.LeadUpdate + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if !leadStatuses[in.Status] { + fail(w, http.StatusUnprocessableEntity, "validation", "Недопустимый статус заявки") + return + } + if err := a.store.AdminUpdateLead(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "lead", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// ---------- Отзывы ---------- + +func (a *API) adminReviews(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + limit, offset := pageParams(r) + q := r.URL.Query() + list, total, err := a.store.AdminReviews(r.Context(), q.Get("status"), q.Get("q"), limit, offset) + if err != nil { + handleErr(w, err) + return + } + counts, err := a.store.ReviewCounts(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": total, "limit": limit, "offset": offset, "counts": counts}) +} + +func decodeReview(w http.ResponseWriter, r *http.Request) (storage.ReviewUpdate, bool) { + var in storage.ReviewUpdate + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10)).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return in, false + } + in.AuthorName = strings.TrimSpace(in.AuthorName) + in.Text = strings.TrimSpace(strings.ReplaceAll(in.Text, "\r", "")) + in.Reply = strings.TrimSpace(strings.ReplaceAll(in.Reply, "\r", "")) + if !reviewStatuses[in.Status] { + fail(w, http.StatusUnprocessableEntity, "validation", "Недопустимый статус отзыва") + return in, false + } + video, err := embedVideoURL(in.VideoURL) + if err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", + "Видео — ссылка на YouTube или Rutube, например https://youtu.be/…") + return in, false + } + in.VideoURL = video + // У видео-отзывов текста нет — содержимое там в ролике. + if in.Text == "" && in.VideoURL == "" { + fail(w, http.StatusUnprocessableEntity, "validation", + "Отзыв должен содержать текст или ссылку на видео") + return in, false + } + if in.AuthorName == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите имя автора отзыва") + return in, false + } + if in.Rating != nil && (*in.Rating < 1 || *in.Rating > 5) { + fail(w, http.StatusUnprocessableEntity, "validation", "Оценка должна быть от 1 до 5") + return in, false + } + return in, true +} + +// adminCreateReview — отзыв, записанный со слов пациента или видеоотзыв с YouTube. +func (a *API) adminCreateReview(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeReview(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateReview(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "review", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateReview(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeReview(w, r) + if !valid { + return + } + if err := a.store.AdminUpdateReview(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "review", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteReview(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteReview(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "review", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_media.go b/internal/api/admin_media.go new file mode 100644 index 0000000..5b6b1dd --- /dev/null +++ b/internal/api/admin_media.go @@ -0,0 +1,120 @@ +package api + +import ( + "crypto/rand" + "encoding/hex" + "image" + _ "image/gif" + _ "image/jpeg" + _ "image/png" + "io" + "log/slog" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "time" + + "dana/internal/storage" +) + +const maxUploadSize = 10 << 20 // 10 МБ + +// Разрешены только растровые картинки. SVG не пускаем: внутри него может быть +// скрипт, и загруженный файл выполнился бы на домене сайта. +var uploadTypes = map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/webp": ".webp", + "image/gif": ".gif", +} + +// adminUploadMedia принимает картинку или PDF документа (multipart, поле file) и регистрирует её +// в медиатеке. Тип определяется по содержимому, а не по расширению из имени файла. +func (a *API) adminUploadMedia(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + if a.mediaDir == "" { + fail(w, http.StatusInternalServerError, "no_media_dir", "Не настроена папка для файлов") + return + } + r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize+(1<<20)) + if err := r.ParseMultipartForm(maxUploadSize); err != nil { + fail(w, http.StatusBadRequest, "bad_upload", "Файл не больше 10 МБ") + return + } + file, _, err := r.FormFile("file") + if err != nil { + fail(w, http.StatusBadRequest, "no_file", "Не передан файл") + return + } + defer file.Close() + + head := make([]byte, 512) + n, _ := io.ReadFull(file, head) + mime := http.DetectContentType(head[:n]) + ext, allowed := uploadTypes[mime] + // PDF принимаем только в поля документов (?accept=document), чтобы он не попал на место картинки. + documentUpload := r.URL.Query().Get("accept") == "document" + if mime == "application/pdf" && documentUpload { + ext, allowed = ".pdf", true + } + if !allowed { + msg := "Можно загрузить JPEG, PNG, WebP или GIF" + if documentUpload { + msg = "Можно загрузить PDF, JPEG, PNG, WebP или GIF" + } + fail(w, http.StatusUnprocessableEntity, "bad_type", msg) + return + } + + var width, height *int + if _, err := file.Seek(0, io.SeekStart); err != nil { + handleErr(w, err) + return + } + if cfg, _, err := image.DecodeConfig(file); err == nil { + width, height = &cfg.Width, &cfg.Height + } + if _, err := file.Seek(0, io.SeekStart); err != nil { + handleErr(w, err) + return + } + + rnd := make([]byte, 16) + if _, err := rand.Read(rnd); err != nil { + handleErr(w, err) + return + } + rel := path.Join("uploads", time.Now().Format("2006/01"), hex.EncodeToString(rnd)+ext) + dst := filepath.Join(a.mediaDir, filepath.FromSlash(rel)) + if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { + handleErr(w, err) + return + } + out, err := os.Create(dst) + if err != nil { + handleErr(w, err) + return + } + size, copyErr := io.Copy(out, file) + closeErr := out.Close() + if copyErr != nil || closeErr != nil { + _ = os.Remove(dst) + slog.Error("save upload", "copy", copyErr, "close", closeErr) + fail(w, http.StatusInternalServerError, "save_failed", "Не удалось сохранить файл") + return + } + + webPath := "/media/" + rel + alt := strings.TrimSpace(r.FormValue("alt")) + id, err := a.store.CreateMedia(r.Context(), webPath, mime, size, width, height, alt) + if err != nil { + _ = os.Remove(dst) // файл без записи в медиатеке никому не нужен + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "media", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{ + "id": id, "path": webPath, "width": width, "height": height, + }}) +} diff --git a/internal/api/admin_partners.go b/internal/api/admin_partners.go new file mode 100644 index 0000000..fbddffa --- /dev/null +++ b/internal/api/admin_partners.go @@ -0,0 +1,119 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/url" + "strconv" + "strings" + + "dana/internal/storage" +) + +type partnerRequest struct { + Name string `json:"name"` + URL string `json:"url"` + LogoID *int64 `json:"logo_id"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +// partnerURL проверяет ссылку на сайт партнёра: только http(s) с доменом. +// Адрес «glaz-almaz05.ru» без схемы дополняем до https://. +func partnerURL(raw string) (string, bool) { + raw = strings.TrimSpace(raw) + if raw == "" { + return "", true + } + if !strings.Contains(raw, "://") { + raw = "https://" + raw + } + u, err := url.Parse(raw) + if err != nil || (u.Scheme != "https" && u.Scheme != "http") || !strings.Contains(u.Hostname(), ".") || u.User != nil { + return "", false + } + return u.String(), true +} + +func decodePartner(w http.ResponseWriter, r *http.Request) (storage.PartnerInput, bool) { + var req partnerRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.PartnerInput{}, false + } + in := storage.PartnerInput{ + Name: strings.TrimSpace(req.Name), + LogoID: req.LogoID, + Sort: req.Sort, + Published: req.Published, + } + if in.Sort == 0 { + in.Sort = 100 + } + if in.Name == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название партнёра") + return in, false + } + link, valid := partnerURL(req.URL) + if !valid { + fail(w, http.StatusUnprocessableEntity, "validation", "Сайт — адрес вида https://example.ru") + return in, false + } + in.URL = link + return in, true +} + +func (a *API) adminPartners(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminPartners(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreatePartner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodePartner(w, r) + if !valid { + return + } + id, err := a.store.AdminCreatePartner(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "partner", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdatePartner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodePartner(w, r) + if !valid { + return + } + if err := a.store.AdminUpdatePartner(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "partner", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeletePartner(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeletePartner(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "partner", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_partners_test.go b/internal/api/admin_partners_test.go new file mode 100644 index 0000000..613fd34 --- /dev/null +++ b/internal/api/admin_partners_test.go @@ -0,0 +1,55 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +func TestPartnerURL(t *testing.T) { + for in, want := range map[string]string{ + "": "", + "glaz-almaz05.ru": "https://glaz-almaz05.ru", + " https://ochevidno.ru/ ": "https://ochevidno.ru/", + "http://abilitycenter.ru": "http://abilitycenter.ru", + } { + if got, ok := partnerURL(in); !ok || got != want { + t.Errorf("partnerURL(%q) = %q, %v; want %q", in, got, ok, want) + } + } + for _, in := range []string{"javascript:alert(1)", "https://localhost", "ftp://files.example.ru", "https://user:pass@example.ru"} { + if got, ok := partnerURL(in); ok { + t.Errorf("partnerURL(%q) accepted as %q", in, got) + } + } +} + +func TestDecodeReviewRules(t *testing.T) { + cases := []struct { + name, body, wantText string + wantCode int + }{ + {"text review", `{"author_name":"Мадина","text":"Спасибо","status":"published","rating":5}`, "", 200}, + {"video review link is normalised", `{"author_name":"Пациент","video_url":"https://youtu.be/V1w8DhkWARc","status":"published"}`, "", 200}, + {"foreign video host", `{"author_name":"Пациент","video_url":"javascript:alert(1)","status":"published"}`, "YouTube или Rutube", 422}, + {"empty review", `{"author_name":"Пациент","status":"pending"}`, "текст или ссылку", 422}, + {"no author", `{"author_name":" ","text":"Спасибо","status":"pending"}`, "имя автора", 422}, + {"bad status", `{"author_name":"А","text":"Б","status":"spam"}`, "статус", 422}, + {"bad rating", `{"author_name":"А","text":"Б","status":"pending","rating":7}`, "от 1 до 5", 422}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(tc.body)) + in, valid := decodeReview(w, r) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + if tc.name == "video review link is normalised" && in.VideoURL != "https://www.youtube.com/embed/V1w8DhkWARc" { + t.Errorf("video url = %q", in.VideoURL) + } + } +} diff --git a/internal/api/admin_posts.go b/internal/api/admin_posts.go new file mode 100644 index 0000000..d051a88 --- /dev/null +++ b/internal/api/admin_posts.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "net/http" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "dana/internal/storage" +) + +// Новости и статьи редактируются в админке одинаково; различаются только видом записи. +var postKindLabels = map[string]string{"news": "новость", "article": "статью"} + +// authHandler — обработчик админки, которому requireAuth передаёт вошедшего пользователя. +type authHandler = func(http.ResponseWriter, *http.Request, *storage.AdminUser) + +const maxPostBody = 2 << 20 // текст статьи с разметкой; картинки грузятся отдельно + +var tagRE = regexp.MustCompile(`<[^>]*>`) + +type postRequest struct { + Slug string `json:"slug"` + Title string `json:"title"` + Lead string `json:"lead"` + HTML string `json:"html"` + ImageID *int64 `json:"image_id"` + PublishedAt string `json:"published_at"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +// parsePublishedAt принимает дату «2025-05-28» (полдень по Москве) или полную метку времени, +// которую админка возвращает без изменений, если дату не трогали. +func parsePublishedAt(raw string) (time.Time, error) { + raw = strings.TrimSpace(raw) + if raw == "" { + return time.Now(), nil + } + if t, err := time.Parse(time.RFC3339Nano, raw); err == nil { + return t, nil + } + moscow := time.FixedZone("MSK", 3*60*60) + return time.ParseInLocation("2006-01-02 15:04", raw+" 12:00", moscow) +} + +func decodePost(w http.ResponseWriter, r *http.Request, kind string) (storage.PostInput, bool) { + var req postRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxPostBody)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос или слишком большой текст") + return storage.PostInput{}, false + } + in := storage.PostInput{ + Kind: kind, + Title: strings.TrimSpace(req.Title), + Lead: strings.TrimSpace(req.Lead), + ImageID: req.ImageID, + Published: req.Published, + SEOTitle: strings.TrimSpace(req.SEOTitle), + SEODesc: strings.TrimSpace(req.SEODesc), + SEOH1: strings.TrimSpace(req.SEOH1), + } + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите заголовок") + return in, false + } + if utf8.RuneCountInString(in.Title) > 300 { + fail(w, http.StatusUnprocessableEntity, "validation", "Заголовок длиннее 300 символов") + return in, false + } + slug, valid := resolveSlug(w, req.Slug, in.Title) + if !valid { + return in, false + } + in.Slug = slug + + html := strings.TrimSpace(req.HTML) + text := strings.TrimSpace(strings.ReplaceAll(tagRE.ReplaceAllString(html, ""), " ", "")) + if in.Published && text == "" && !strings.Contains(html, "Текст

","published_at":"2026-09-17","published":true}`, 200, ""}, + {"draft without text", `{"title":"Черновик","published":false}`, 200, ""}, + {"picture-only post", `{"title":"Афиша","html":"

","published":true}`, 200, ""}, + {"no title", `{"title":" ","published":false}`, 422, "заголовок"}, + {"published without text", `{"title":"Пусто","html":"

 

","published":true}`, 422, "добавьте текст"}, + {"bad date", `{"title":"Дата","published_at":"17.09.2026"}`, 422, "ГГГГ-ММ-ДД"}, + {"bad slug", `{"title":"Адрес","slug":"новость"}`, 422, "латинские"}, + // адреса со старого сайта: хвостовой дефис, подчёркивание — запись должна сохраняться + {"legacy slug", `{"title":"Старая","slug":"v-klinike-dana-","published":false}`, 200, ""}, + {"legacy underscore", `{"title":"Старая","slug":"lech_bol","published":false}`, 200, ""}, + {"leading dash", `{"title":"Адрес","slug":"-dana"}`, 422, "латинские"}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/api/v1/admin/news", strings.NewReader(tc.body)) + _, valid := decodePost(w, r, "news") + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + } +} + +func TestDecodePostStoresHTMLBlockAndDate(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(`{"title":"Статья","html":"

Раздел

","published_at":"2025-05-28"}`)) + in, valid := decodePost(w, r, "article") + if !valid { + t.Fatal(w.Body.String()) + } + var blocks []map[string]string + if err := json.Unmarshal(in.Blocks, &blocks); err != nil || len(blocks) != 1 || blocks[0]["html"] != "

Раздел

" { + t.Errorf("blocks = %s", in.Blocks) + } + if in.Slug != "statya" { + t.Errorf("slug = %s", in.Slug) + } + if got := in.PublishedAt.UTC().Format(time.RFC3339); got != "2025-05-28T09:00:00Z" { + t.Errorf("published_at = %s, want midday Moscow", got) + } + + // неизменённая дата возвращается полной меткой времени и сохраняется как есть + w = httptest.NewRecorder() + r = httptest.NewRequest("POST", "/", strings.NewReader(`{"title":"Статья","published_at":"2024-01-13T14:03:41+03:00"}`)) + in, _ = decodePost(w, r, "article") + if got := in.PublishedAt.UTC().Format(time.RFC3339); got != "2024-01-13T11:03:41Z" { + t.Errorf("kept published_at = %s", got) + } +} + +func TestDecodeVacancy(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(`{"title":"Администратор","requirements":"- грамотная речь\r\n\r\n• ответственность ","published":true}`)) + in, valid := decodeVacancy(w, r) + if !valid { + t.Fatal(w.Body.String()) + } + if in.Requirements != "грамотная речь\nответственность" || in.Slug != "administrator" || in.Sort != 100 { + t.Errorf("got %+v", in) + } + + w = httptest.NewRecorder() + r = httptest.NewRequest("POST", "/", strings.NewReader(`{"title":"Пустая","published":true}`)) + if _, valid := decodeVacancy(w, r); valid || !strings.Contains(w.Body.String(), "опишите") { + t.Errorf("empty published vacancy accepted: %d %s", w.Code, w.Body.String()) + } +} diff --git a/internal/api/admin_promos.go b/internal/api/admin_promos.go new file mode 100644 index 0000000..d1e2c25 --- /dev/null +++ b/internal/api/admin_promos.go @@ -0,0 +1,205 @@ +package api + +import ( + "encoding/json" + "net/http" + "regexp" + "strconv" + "strings" + "time" + + "dana/internal/storage" +) + +// Адрес: латиница, цифры, дефис и подчёркивание. Хвостовой дефис и «_» встречаются в адресах +// со старого сайта («…v-klinike-dana-», «lech_bol») — их нужно сохранять, иначе запись не сохранить. +var slugRE = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]*$`) + +var translit = map[rune]string{ + 'а': "a", 'б': "b", 'в': "v", 'г': "g", 'д': "d", 'е': "e", 'ё': "e", 'ж': "zh", 'з': "z", + 'и': "i", 'й': "y", 'к': "k", 'л': "l", 'м': "m", 'н': "n", 'о': "o", 'п': "p", 'р': "r", + 'с': "s", 'т': "t", 'у': "u", 'ф': "f", 'х': "kh", 'ц': "ts", 'ч': "ch", 'ш': "sh", 'щ': "shch", + 'ъ': "", 'ы': "y", 'ь': "", 'э': "e", 'ю': "yu", 'я': "ya", +} + +// slugFromTitle делает адрес из названия: «Я в сад» → «ya-v-sad». +func slugFromTitle(title string) string { + var b strings.Builder + dash := false + for _, r := range strings.ToLower(title) { + switch { + case r >= 'a' && r <= 'z' || r >= '0' && r <= '9': + b.WriteRune(r) + dash = false + case translit[r] != "" || r == 'ъ' || r == 'ь': + b.WriteString(translit[r]) + dash = false + default: + if !dash && b.Len() > 0 { + b.WriteByte('-') + dash = true + } + } + } + s := strings.Trim(b.String(), "-") + if len(s) > 80 { + s = strings.Trim(s[:80], "-") + } + return s +} + +// resolveSlug берёт адрес из формы или делает его из названия и проверяет формат. +func resolveSlug(w http.ResponseWriter, raw, title string) (string, bool) { + slug := strings.ToLower(strings.TrimSpace(raw)) + if slug == "" { + slug = slugFromTitle(title) + } + if !slugRE.MatchString(slug) || len(slug) > 100 { + fail(w, http.StatusUnprocessableEntity, "validation", + "Адрес — латинские буквы, цифры и дефисы, например ya-v-sad") + return "", false + } + return slug, true +} + +func parseDate(raw *string) (*time.Time, error) { + if raw == nil || strings.TrimSpace(*raw) == "" { + return nil, nil + } + t, err := time.Parse("2006-01-02", strings.TrimSpace(*raw)) + if err != nil { + return nil, err + } + return &t, nil +} + +type promoRequest struct { + Slug string `json:"slug"` + Title string `json:"title"` + Lead string `json:"lead"` + ImageID *int64 `json:"image_id"` + DiscountPercent *int `json:"discount_percent"` + Terms string `json:"terms"` + CheckupID *int64 `json:"checkup_id"` + StartsAt *string `json:"starts_at"` + EndsAt *string `json:"ends_at"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +func decodePromo(w http.ResponseWriter, r *http.Request) (storage.PromoInput, bool) { + var req promoRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.PromoInput{}, false + } + in := storage.PromoInput{ + Title: strings.TrimSpace(req.Title), + Lead: strings.TrimSpace(req.Lead), + ImageID: req.ImageID, + DiscountPercent: req.DiscountPercent, + Terms: strings.TrimSpace(req.Terms), + CheckupID: req.CheckupID, + Published: req.Published, + SEOTitle: strings.TrimSpace(req.SEOTitle), + SEODesc: strings.TrimSpace(req.SEODesc), + SEOH1: strings.TrimSpace(req.SEOH1), + } + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название акции") + return in, false + } + slug, valid := resolveSlug(w, req.Slug, in.Title) + if !valid { + return in, false + } + in.Slug = slug + + if in.DiscountPercent != nil && (*in.DiscountPercent < 1 || *in.DiscountPercent > 99) { + fail(w, http.StatusUnprocessableEntity, "validation", "Скидка — от 1 до 99 %") + return in, false + } + var err error + if in.StartsAt, err = parseDate(req.StartsAt); err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Дата начала в формате ГГГГ-ММ-ДД") + return in, false + } + if in.EndsAt, err = parseDate(req.EndsAt); err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Дата окончания в формате ГГГГ-ММ-ДД") + return in, false + } + if in.StartsAt != nil && in.EndsAt != nil && in.EndsAt.Before(*in.StartsAt) { + fail(w, http.StatusUnprocessableEntity, "validation", "Окончание акции не может быть раньше начала") + return in, false + } + + // Закон «О рекламе» (ст. 9): рекламируя скидку, нужно сообщить её условия и срок. + if in.Published && in.Terms == "" { + fail(w, http.StatusUnprocessableEntity, "validation", + "Чтобы показать акцию на сайте, заполните её условия") + return in, false + } + if in.Published && in.DiscountPercent != nil && in.EndsAt == nil { + fail(w, http.StatusUnprocessableEntity, "validation", + "У акции со скидкой укажите последний день действия") + return in, false + } + return in, true +} + +func (a *API) adminPromos(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminPromos(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreatePromo(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodePromo(w, r) + if !valid { + return + } + id, err := a.store.AdminCreatePromo(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "promo", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id, "slug": in.Slug}}) +} + +func (a *API) adminUpdatePromo(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodePromo(w, r) + if !valid { + return + } + if err := a.store.AdminUpdatePromo(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "promo", id, clientIP(r)) + ok(w, map[string]any{"id": id, "slug": in.Slug}, nil) +} + +func (a *API) adminDeletePromo(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeletePromo(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "promo", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_promos_test.go b/internal/api/admin_promos_test.go new file mode 100644 index 0000000..6089c51 --- /dev/null +++ b/internal/api/admin_promos_test.go @@ -0,0 +1,92 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +func TestSlugFromTitle(t *testing.T) { + cases := map[string]string{ + "Я в сад": "ya-v-sad", + "Здоровый ЖКТ": "zdorovyy-zhkt", + "Мне год (1–2 года)": "mne-god-1-2-goda", + " Чек-ап «Я подросток»! ": "chek-ap-ya-podrostok", + "Подъезд, щука": "podezd-shchuka", + "EMSELLA 2026": "emsella-2026", + "!!!": "", + } + for title, want := range cases { + if got := slugFromTitle(title); got != want { + t.Errorf("slugFromTitle(%q) = %q, want %q", title, got, want) + } + } +} + +func decodePromoBody(body string) (int, string) { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/api/v1/admin/promos", strings.NewReader(body)) + if _, valid := decodePromo(w, r); valid { + return 200, "" + } + return w.Code, w.Body.String() +} + +func TestDecodePromoRules(t *testing.T) { + cases := []struct { + name string + body string + wantCode int + wantText string + }{ + {"valid published promo", `{"title":"Я в сад","discount_percent":20,"terms":"Скидка на программу чек-апа","ends_at":"2026-12-31","published":true}`, 200, ""}, + {"draft without terms is allowed", `{"title":"Черновик","published":false}`, 200, ""}, + {"no title", `{"title":" ","published":false}`, 422, "название"}, + {"published without terms", `{"title":"Я в сад","published":true}`, 422, "условия"}, + {"discount without end date", `{"title":"Я в сад","discount_percent":20,"terms":"x","published":true}`, 422, "последний день"}, + {"discount out of range", `{"title":"Я в сад","discount_percent":150,"published":false}`, 422, "от 1 до 99"}, + {"end before start", `{"title":"Я в сад","starts_at":"2026-10-01","ends_at":"2026-09-01","published":false}`, 422, "раньше начала"}, + {"bad date", `{"title":"Я в сад","ends_at":"31.12.2026","published":false}`, 422, "ГГГГ-ММ-ДД"}, + {"bad slug", `{"title":"Я в сад","slug":"Я в сад","published":false}`, 422, "латинские"}, + } + for _, tc := range cases { + code, body := decodePromoBody(tc.body) + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(body, tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, body, tc.wantCode, tc.wantText) + } + } +} + +func decodeCheckupBody(body string) (int, string, int) { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/api/v1/admin/checkups", strings.NewReader(body)) + in, valid := decodeCheckup(w, r) + if valid { + return 200, "", len(in.Items) + } + return w.Code, w.Body.String(), 0 +} + +func TestDecodeCheckupRules(t *testing.T) { + code, body, items := decodeCheckupBody(`{"name":"Здоровая девочка","audience":"kids","price":5000,"old_price":6000,"items":["УЗИ"," ","Ферритин"]}`) + if code != 200 || items != 2 { + t.Fatalf("valid checkup rejected or empty items kept: %d %s items=%d", code, body, items) + } + + cases := []struct { + name string + body string + wantText string + }{ + {"unknown audience", `{"name":"X","audience":"pets"}`, "для кого"}, + {"old price not above price", `{"name":"X","audience":"adults","price":5000,"old_price":5000}`, "больше цены"}, + {"old price without price", `{"name":"X","audience":"adults","old_price":5000}`, "больше цены"}, + {"negative price", `{"name":"X","audience":"adults","price":-1}`, "отрицательной"}, + } + for _, tc := range cases { + code, body, _ := decodeCheckupBody(tc.body) + if code != 422 || !strings.Contains(body, tc.wantText) { + t.Errorf("%s: got %d %s, want 422 containing %q", tc.name, code, body, tc.wantText) + } + } +} diff --git a/internal/api/admin_redirects.go b/internal/api/admin_redirects.go new file mode 100644 index 0000000..268c1ae --- /dev/null +++ b/internal/api/admin_redirects.go @@ -0,0 +1,143 @@ +package api + +import ( + "context" + "encoding/json" + "log/slog" + "net/http" + "net/url" + "strconv" + "strings" + + "dana/internal/storage" +) + +// keepOldPath оставляет редирект со старого адреса, если в админке сменили адрес записи. +// Ссылки из поисковиков, соцсетей и закладок продолжают работать. +func (a *API) keepOldPath(ctx context.Context, oldPath, newPath string) { + if oldPath == "" || newPath == "" || oldPath == newPath { + return + } + if _, err := a.store.SaveRedirect(ctx, oldPath, newPath); err != nil { + slog.Error("save redirect after slug change", "from", oldPath, "to", newPath, "err", err) + } +} + +func checkupWebPath(slug string) string { return "/checkup/" + slug + "/" } + +// doctorPathBySlug — канонический адрес опубликованного врача или "". +func (a *API) doctorPathBySlug(ctx context.Context, slug string) string { + if slug == "" { + return "" + } + d, err := a.store.DoctorBySlug(ctx, slug) + if err != nil { + return "" + } + return doctorWebPath(*d) +} + +// ---------- ручные редиректы и журнал 404 ---------- + +type redirectRequest struct { + From string `json:"from_path"` + To string `json:"to_path"` +} + +// redirectPath приводит адрес к виду «/путь/»: принимает и полную ссылку со старого домена. +func redirectPath(raw string) (string, bool) { + raw = strings.TrimSpace(raw) + if raw == "" { + return "", false + } + if strings.Contains(raw, "://") { + u, err := url.Parse(raw) + if err != nil { + return "", false + } + raw = u.Path + } + raw = strings.SplitN(strings.SplitN(raw, "?", 2)[0], "#", 2)[0] + if !strings.HasPrefix(raw, "/") || strings.HasPrefix(raw, "//") || strings.ContainsAny(raw, " \t\r\n\\") { + return "", false + } + // файлы (/index.php, /404.php) оставляем как есть, разделы — со слэшем на конце + last := raw[strings.LastIndex(raw, "/")+1:] + if !strings.Contains(last, ".") { + raw = canonicalPagePath(raw) + } + return raw, len(raw) <= 300 +} + +func (a *API) adminRedirects(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminRedirects(r.Context()) + if err != nil { + handleErr(w, err) + return + } + missing, err := a.store.AdminNotFound(r.Context(), 100) + if err != nil { + handleErr(w, err) + return + } + ok(w, map[string]any{"redirects": emptyIfNil(list), "not_found": emptyIfNil(missing)}, nil) +} + +func (a *API) adminCreateRedirect(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + var req redirectRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 8<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + from, okFrom := redirectPath(req.From) + to, okTo := redirectPath(req.To) + if !okFrom || !okTo { + fail(w, http.StatusUnprocessableEntity, "validation", "Адреса — пути на сайте, например /services/uzi/ (можно вставить полную ссылку)") + return + } + if from == to || from == "/" { + fail(w, http.StatusUnprocessableEntity, "validation", "Старый и новый адрес должны различаться, главную перенаправить нельзя") + return + } + for _, reserved := range []string{"/api/", "/admin", "/media/", "/assets/"} { + if strings.HasPrefix(from, reserved) { + fail(w, http.StatusUnprocessableEntity, "validation", "Служебные адреса перенаправлять нельзя") + return + } + } + id, err := a.store.SaveRedirect(r.Context(), from, to) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "redirect", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id, "from_path": from, "to_path": to}}) +} + +func (a *API) adminDeleteRedirect(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.DeleteRedirect(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "redirect", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// adminDeleteNotFound убирает адрес из журнала 404 (например, мусорный запрос бота). +func (a *API) adminDeleteNotFound(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + path := r.URL.Query().Get("path") + if path == "" { + fail(w, http.StatusBadRequest, "bad_request", "Не указан адрес") + return + } + if err := a.store.DeleteNotFound(r.Context(), path); err != nil { + handleErr(w, err) + return + } + ok(w, map[string]any{"path": path}, nil) +} diff --git a/internal/api/admin_redirects_test.go b/internal/api/admin_redirects_test.go new file mode 100644 index 0000000..f2bb963 --- /dev/null +++ b/internal/api/admin_redirects_test.go @@ -0,0 +1,23 @@ +package api + +import "testing" + +func TestRedirectPath(t *testing.T) { + good := map[string]string{ + "/services/uzi": "/services/uzi/", + " /services/uzi/ ": "/services/uzi/", + "https://danamedical.ru/price/lech_bol/?a=1": "/price/lech_bol/", + "/404.php": "/404.php", + "/company/staff/#top": "/company/staff/", + } + for in, want := range good { + if got, ok := redirectPath(in); !ok || got != want { + t.Errorf("redirectPath(%q) = %q, %v; want %q", in, got, ok, want) + } + } + for _, in := range []string{"", "services/uzi", "//evil.example/x", "/a b/", `/a\b/`} { + if got, ok := redirectPath(in); ok { + t.Errorf("redirectPath(%q) accepted as %q", in, got) + } + } +} diff --git a/internal/api/admin_reports.go b/internal/api/admin_reports.go new file mode 100644 index 0000000..7d917a8 --- /dev/null +++ b/internal/api/admin_reports.go @@ -0,0 +1,436 @@ +package api + +import ( + "bytes" + "context" + "encoding/csv" + "errors" + "fmt" + "io" + "log/slog" + "math" + "net/http" + "net/url" + "path" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "dana/internal/storage" +) + +const maxReportFile = 10 << 20 // 10 МБ + +// Запрос на отчёт: книга 1С и необязательные файлы СберЗдоровья и ПроДокторов (до 10 МБ каждый). +const maxReportRequest = maxReportFile + (1+maxProdoctorovFiles)*maxPartnerFile + (1 << 20) + +// Виды отчётов. Файл нужен для отчёта по каналам — это книга Excel с выгрузкой 1С и листами каналов. +var reportKinds = map[string]struct { + title string + needFile bool +}{ + "phone_match": {"Отчёт по каналам", true}, +} + +// Этапы задания — как на странице «Отчёты». +const ( + stageUpload = iota + stageCheck + stageProcess + stageBuild + stageFinish + stagesTotal +) + +// reportJobs — отмена идущих заданий. Задания короткие (секунды), поэтому живут в памяти процесса; +// после перезапуска сервера незаконченные помечаются как прерванные. +type reportJobs struct { + mu sync.Mutex + cancels map[int64]context.CancelFunc +} + +var jobs = &reportJobs{cancels: map[int64]context.CancelFunc{}} + +func (j *reportJobs) add(id int64, c context.CancelFunc) { + j.mu.Lock() + j.cancels[id] = c + j.mu.Unlock() +} +func (j *reportJobs) done(id int64) { j.mu.Lock(); delete(j.cancels, id); j.mu.Unlock() } +func (j *reportJobs) cancel(id int64) { + j.mu.Lock() + c := j.cancels[id] + j.mu.Unlock() + if c != nil { + c() + } +} + +// FailInterruptedReports вызывается при старте сервера. +func (a *API) FailInterruptedReports(ctx context.Context) error { + return a.store.FailInterruptedReports(ctx) +} + +func (a *API) adminReports(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + clinic, valid := requestClinic(w, r) + if !valid { + return + } + list, err := a.store.Reports(r.Context(), clinic.Key, 50) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, nil) +} + +func (a *API) adminReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + rep, err := a.store.Report(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + ok(w, rep, nil) +} + +// adminCreateReport принимает вид отчёта и книгу Excel, заводит задание +// и считает его в фоне; страница опрашивает ход через GET /admin/reports/{id}. +func (a *API) adminCreateReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + started := time.Now() + r.Body = http.MaxBytesReader(w, r.Body, maxReportRequest) + if err := r.ParseMultipartForm(maxReportFile); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Файл больше 10 МБ или запрос повреждён") + return + } + clinic, knownClinic := clinicFrom(r.FormValue("clinic")) + if !knownClinic { + fail(w, http.StatusUnprocessableEntity, "validation", "Неизвестная клиника") + return + } + kind := r.FormValue("kind") + meta, known := reportKinds[kind] + if !known { + fail(w, http.StatusUnprocessableEntity, "validation", "Неизвестный вид отчёта") + return + } + var data []byte + var name string + if meta.needFile { + f, hdr, err := r.FormFile("file") + if err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Загрузите книгу Excel: лист выгрузки 1С и листы каналов с номерами") + return + } + defer f.Close() + name = path.Base(strings.ReplaceAll(hdr.Filename, "\\", "/")) + if ext := strings.ToLower(path.Ext(name)); ext != ".xlsx" && ext != ".csv" { + fail(w, http.StatusUnprocessableEntity, "validation", "Нужен файл .xlsx или .csv") + return + } + if data, err = io.ReadAll(f); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Файл не дочитался — загрузите ещё раз") + return + } + } + uploads, err := readPartnerUploads(r) + if err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", err.Error()) + return + } + // Даты из 1С — источник истины для всех каналов. Ручной диапазон остаётся + // запасным вариантом только для старых файлов без строки периода. + source := r.FormValue("source") + pbxWanted := source == "all" || source == "pbx" + bitrixWanted := source == "all" || source == "bitrix" || r.FormValue("bitrix") == "1" + // автовыгрузка из кабинетов площадок настроена только на кабинеты DANA + prodoctorovWanted := source == "all" && clinic.partnerCabinets && a.prodoctorov.Configured() + sberhealthWanted := source == "all" && clinic.partnerCabinets && a.sberhealth.Configured() + var period *pbxPeriod + if f, t := r.FormValue("pbx_from"), r.FormValue("pbx_to"); f != "" || t != "" { + pbxWanted = true + } + if pbxWanted || bitrixWanted || uploads.any() { + f, t := r.FormValue("pbx_from"), r.FormValue("pbx_to") + from, to, _, ok := detectPeriodFromFile(name, data) + if !ok && (f != "" || t != "") { + from, to, ok = statsRange(map[string][]string{"from": {f}, "to": {t}}, time.Now()) + if !ok || to.Sub(from) > 93*24*time.Hour { + fail(w, http.StatusUnprocessableEntity, "validation", "Период — даты ГГГГ-ММ-ДД, не длиннее трёх месяцев") + return + } + } + if !ok { + fail(w, http.StatusUnprocessableEntity, "validation", + "Не нашёл период в файле: в строке 2 должна быть запись «Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ»") + return + } + period = &pbxPeriod{from: from, to: to} + } + id, err := a.store.CreateReport(r.Context(), clinic.Key, kind, meta.title, name, int64(len(data)), u.ID) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "report", id, clientIP(r)) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + jobs.add(id, cancel) + uploadMS := time.Since(started).Milliseconds() + go func() { + defer jobs.done(id) + defer cancel() + a.runReport(ctx, id, clinic, kind, name, data, uploadMS, period, pbxWanted, bitrixWanted, prodoctorovWanted, sberhealthWanted, uploads, u.ID) + }() + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminCancelReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + changed, err := a.store.CancelReport(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + jobs.cancel(id) + if !changed { + fail(w, http.StatusConflict, "not_running", "Отчёт уже завершён") + return + } + a.store.Audit(r.Context(), u.ID, "cancel", "report", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.DeleteReport(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "report", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDownloadReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + name, data, err := a.store.ReportResult(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + // в файле отчёта — телефоны и имена пациентов: каждое скачивание фиксируем в аудите + a.store.Audit(r.Context(), u.ID, "download", "report", id, clientIP(r)) + w.Header().Set("Content-Type", "text/csv; charset=utf-8") + w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="report-%d.csv"; filename*=UTF-8''%s`, id, url.PathEscape(name))) + w.Header().Set("Cache-Control", "no-store") + _, _ = w.Write(data) +} + +// ---------- расчёт ---------- + +// progressTracker пишет этапы в базу: длительность каждого завершённого этапа и общий процент. +type progressTracker struct { + a *API + ctx context.Context + id int64 + stage int + start time.Time + stages []int64 + last int + // bounds — где начинается каждый этап на шкале 0–100: доли по реальному объёму работы. + // По умолчанию этапы равные. + bounds []int +} + +func (p *progressTracker) set(stage, inStage int) { + b := p.bounds + if len(b) != stagesTotal+1 { + b = []int{0, 20, 40, 60, 80, 100} + } + pct := b[stage] + (b[stage+1]-b[stage])*inStage/100 + if pct < p.last { + pct = p.last // процент не должен идти назад — выгрузка Битрикс24/АТС до этапа «Проверка + // данных» уже могла продвинуть его дальше, чем начало этого этапа + } + if stage == p.stage && pct-p.last < 1 && inStage != 0 { + return + } + p.last = pct + if p.a == nil || p.a.store == nil { // в тестах — без базы + return + } + if err := p.a.store.ReportProgress(context.Background(), p.id, stage, pct, p.stages); err != nil { + slog.Warn("report progress", "id", p.id, "err", err) + } +} + +// setExact — процент внутри этапа по обработанным строкам: done из total. +func (p *progressTracker) setExact(stage, done, total int) { + if total <= 0 { + return + } + in := done * 100 / total + if in > 99 { + in = 99 + } + p.set(stage, in) +} + +// next закрывает текущий этап и начинает следующий. +func (p *progressTracker) next() { + p.stages = append(p.stages, time.Since(p.start).Milliseconds()) + p.start = time.Now() + p.stage++ + p.set(p.stage, 0) +} + +func (a *API) runReport(ctx context.Context, id int64, clinic *clinicDef, kind, name string, data []byte, uploadMS int64, period *pbxPeriod, pbxWanted, bitrixWanted, prodoctorovWanted, sberhealthWanted bool, uploads partnerUploads, adminID int64) { + p := &progressTracker{a: a, ctx: ctx, id: id, stage: stageCheck, start: time.Now(), stages: []int64{uploadMS}} + p.set(stageCheck, 0) + var ( + result []byte + summary any + err error + ) + switch kind { + case "phone_match": + o := matchOpts{} + if pbxWanted && period != nil { + var po matchOpts + if po, err = a.pbxMatchOpts(ctx, clinic.Key, period); err == nil { + o.pbxLists = mergeChannelLists(o.pbxLists, po.pbxLists) + o.pbxInfo = po.pbxInfo + } + } + if err == nil && bitrixWanted && period != nil { + var bo matchOpts + onProgress := func(phase string, done, total int) { + if total <= 0 { + return + } + base := 0 + if phase == "contacts" { + base = 50 + } + p.set(stageCheck, base+done*50/total) + } + if bo, err = a.bitrixMatchOpts(ctx, clinic, period.from, period.to, onProgress); err == nil { + o.pbxLists = mergeChannelLists(o.pbxLists, bo.pbxLists) + o.bitrixInfo = bo.bitrixInfo + } + } + if err == nil && (prodoctorovWanted || len(uploads.prodoctorov) > 0) { + if period == nil { + err = errors.New("Для ПроДокторов нужен период выгрузки 1С") + } else { + // файлы, приложенные пользователем, главнее скачивания с сервера (его блокирует защита кабинета) + var po matchOpts + if len(uploads.prodoctorov) > 0 { + po, err = prodoctorovUploadOptsFor(clinic, uploads.prodoctorov, period.from, period.to) + } else { + po, err = a.prodoctorovMatchOpts(ctx, period.from, period.to) + } + if err == nil { + o.pbxLists = mergeChannelLists(o.pbxLists, po.pbxLists) + o.prodoctorovInfo = po.prodoctorovInfo + } + } + } + if err == nil && (sberhealthWanted || len(uploads.sber) > 0) { + if period == nil { + err = errors.New("Для СберЗдоровья нужен период выгрузки 1С") + } else { + var so matchOpts + if len(uploads.sber) > 0 { + so, err = sberhealthUploadOpts(uploads.sber, period.from, period.to) + } else { + so, err = a.sberhealthMatchOpts(ctx, period.from, period.to) + } + if err == nil { + o.pbxLists = mergeChannelLists(o.pbxLists, so.pbxLists) + o.sberhealthInfo = so.sberhealthInfo + } + } + } + if err == nil { + result, summary, err = a.phoneMatchOpts(ctx, p, name, data, o) + } + } + status, errText := "done", "" + switch { + case errors.Is(ctx.Err(), context.Canceled): + return // отменили — статус уже «отменён» + case errors.Is(ctx.Err(), context.DeadlineExceeded): + status, errText = "failed", "Расчёт шёл дольше 5 минут и остановлен" + case err != nil: + status, errText = "failed", err.Error() + } + if status == "done" { + p.stages = append(p.stages, time.Since(p.start).Milliseconds()) // «Завершение» + if kind == "phone_match" { + if sum, isSum := summary.(channelsSummary); isSum { + sum.Dashboard = a.saveWeeklyFromSummary(context.Background(), clinic.Key, name, data, sum, adminID) + summary = sum + } + } + } + stamp := time.Now().In(moscow).Format("2006-01-02") + resultName := "otchet-po-kanalam-" + stamp + ".csv" + if status != "done" { + result, resultName = nil, "" + } + if err := a.store.FinishReport(context.Background(), id, status, p.stages, summary, result, resultName, errText); err != nil { + slog.Error("finish report", "id", id, "err", err) + } +} + +// csvBytes — CSV для Excel: UTF-8 с BOM и точкой с запятой. +func csvBytes(rows [][]string) []byte { + var buf bytes.Buffer + buf.WriteString("\ufeff") + cw := csv.NewWriter(&buf) + cw.Comma = ';' + cw.UseCRLF = true + _ = cw.WriteAll(rows) + return buf.Bytes() +} + +var priceRE = regexp.MustCompile(`-?\d+(?:[.,]\d+)?`) + +// parsePrice понимает «1 500», «1500,00», «1 500 ₽», «от 2000 руб.». +func parsePrice(s string) (float64, bool) { + s = strings.NewReplacer(" ", "", " ", "", " ", "").Replace(s) + m := priceRE.FindString(s) + if m == "" { + return 0, false + } + v, err := strconv.ParseFloat(strings.ReplaceAll(m, ",", "."), 64) + if err != nil || v < 0 || v > 100_000_000 { + return 0, false + } + return v, true +} + +func fmtRub(v float64) string { + if v == math.Trunc(v) { + return strconv.FormatFloat(v, 'f', 0, 64) + } + return strings.ReplaceAll(strconv.FormatFloat(v, 'f', 2, 64), ".", ",") +} diff --git a/internal/api/admin_services.go b/internal/api/admin_services.go new file mode 100644 index 0000000..af05763 --- /dev/null +++ b/internal/api/admin_services.go @@ -0,0 +1,49 @@ +package api + +import ( + "encoding/json" + "net/http" + "strings" + + "dana/internal/storage" +) + +type newServiceRequest struct { + ParentID int64 `json:"parent_id"` + Slug string `json:"slug"` + Name string `json:"name"` + Lead string `json:"lead"` +} + +// adminCreateService заводит услугу скрытой: на сайт она попадёт, когда её заполнят и включат. +func (a *API) adminCreateService(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + var req newServiceRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + in := storage.NewService{ + ParentID: req.ParentID, + Slug: strings.ToLower(strings.TrimSpace(req.Slug)), + Name: strings.TrimSpace(req.Name), + Lead: strings.TrimSpace(req.Lead), + } + switch { + case in.Name == "" || len([]rune(in.Name)) > 200: + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название услуги (до 200 символов)") + return + case in.ParentID <= 0: + fail(w, http.StatusUnprocessableEntity, "validation", "Выберите направление") + return + case !slugRE.MatchString(in.Slug) || len(in.Slug) > 100: + fail(w, http.StatusUnprocessableEntity, "validation", "Адрес — латиница, цифры и дефис, например «priem-kardiologa»") + return + } + id, err := a.store.AdminCreateService(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "service", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} diff --git a/internal/api/admin_site.go b/internal/api/admin_site.go new file mode 100644 index 0000000..455ee64 --- /dev/null +++ b/internal/api/admin_site.go @@ -0,0 +1,317 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + + "dana/internal/storage" +) + +// ---------- Страницы ---------- + +// Правовые страницы должны быть доступны всегда: на них ссылаются формы заявок +// (согласие на обработку персональных данных) и баннер cookie. +var requiredPages = map[string]bool{"legaldocs": true, "privacy": true, "consent": true, "cookie": true} + +type pageRequest struct { + Title string `json:"title"` + HTML string `json:"html"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +func (a *API) adminPages(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminPages(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminUpdatePage(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var req pageRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxPostBody)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос или слишком большой текст") + return + } + current, err := a.store.AdminPage(r.Context(), id) + if err != nil { + handleErr(w, err) + return + } + in := storage.PageInput{ + Title: strings.TrimSpace(req.Title), + Published: req.Published, + SEOTitle: strings.TrimSpace(req.SEOTitle), + SEODesc: strings.TrimSpace(req.SEODesc), + SEOH1: strings.TrimSpace(req.SEOH1), + } + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите заголовок страницы") + return + } + if requiredPages[current.Slug] && !in.Published { + fail(w, http.StatusUnprocessableEntity, "validation", + "Правовые документы нельзя скрыть: на них ссылаются формы заявок и баннер cookie") + return + } + html := strings.TrimSpace(req.HTML) + if requiredPages[current.Slug] && strings.TrimSpace(tagRE.ReplaceAllString(html, "")) == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Текст правового документа не может быть пустым") + return + } + // Блоки, которые админка не редактирует (например, фото на странице «О клинике»), + // сохраняются как были; текстовые заменяются одним блоком из редактора. + var blocks []json.RawMessage + var old []json.RawMessage + _ = json.Unmarshal(current.Blocks, &old) + for _, b := range old { + var head struct { + Type string `json:"type"` + HTML *string `json:"html"` + } + if json.Unmarshal(b, &head) == nil && head.HTML == nil && head.Type != "html" { + blocks = append(blocks, b) + } + } + if html != "" { + b, _ := json.Marshal(map[string]string{"type": "html", "html": html}) + blocks = append(blocks, b) + } + if blocks == nil { + blocks = []json.RawMessage{} + } + in.Blocks, _ = json.Marshal(blocks) + if err := a.store.AdminUpdatePage(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "page", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// ---------- Настройки ---------- + +// Все настройки публичны (/api/v1/settings), поэтому админка пишет только известные +// ключи: пароли и ключи сервисов сюда попасть не должны, они живут в переменных окружения. +type legalSettings struct { + OrgName string `json:"org_name"` + OrgShort string `json:"org_short"` + INN string `json:"inn"` + OGRN string `json:"ogrn"` + Address string `json:"address"` + License string `json:"license"` + Email string `json:"email"` +} + +type settingsRequest struct { + Email string `json:"email"` + MaxURL string `json:"max_url"` + Legal legalSettings `json:"legal"` +} + +var ( + emailRE = regexp.MustCompile(`^[^@\s]+@[^@\s]+\.[^@\s]+$`) + onlyDigitsRE = regexp.MustCompile(`^\d+$`) +) + +func decodeSettings(w http.ResponseWriter, r *http.Request) (map[string]any, bool) { + var req settingsRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 32<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return nil, false + } + bad := func(msg string) (map[string]any, bool) { + fail(w, http.StatusUnprocessableEntity, "validation", msg) + return nil, false + } + trim := strings.TrimSpace + req.Email, req.MaxURL = trim(req.Email), trim(req.MaxURL) + l := &req.Legal + l.OrgName, l.OrgShort, l.INN, l.OGRN = trim(l.OrgName), trim(l.OrgShort), trim(l.INN), trim(l.OGRN) + l.Address, l.License, l.Email = trim(l.Address), trim(l.License), trim(l.Email) + + if req.Email != "" && !emailRE.MatchString(req.Email) { + return bad("Проверьте адрес почты для сайта") + } + if req.MaxURL != "" { + u, err := url.Parse(req.MaxURL) + if err != nil || u.Scheme != "https" || !strings.Contains(u.Hostname(), ".") { + return bad("Ссылка на MAX — адрес вида https://max.ru/…") + } + } + // Реквизиты по закону должны быть на сайте медицинской организации — пустыми их не оставляем. + if l.OrgName == "" || l.OrgShort == "" || l.Address == "" || l.License == "" { + return bad("Заполните название организации, адрес и лицензию") + } + if !onlyDigitsRE.MatchString(l.INN) || (len(l.INN) != 10 && len(l.INN) != 12) { + return bad("ИНН — 10 или 12 цифр") + } + if !onlyDigitsRE.MatchString(l.OGRN) || (len(l.OGRN) != 13 && len(l.OGRN) != 15) { + return bad("ОГРН — 13 или 15 цифр") + } + if l.Email != "" && !emailRE.MatchString(l.Email) { + return bad("Проверьте почту в реквизитах") + } + return map[string]any{"email": req.Email, "max_url": req.MaxURL, "legal": req.Legal}, true +} + +func (a *API) adminSettings(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + m, err := a.store.Settings(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, m, nil) +} + +func (a *API) adminUpdateSettings(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + values, valid := decodeSettings(w, r) + if !valid { + return + } + if err := a.store.AdminSetSettings(r.Context(), values); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "settings", 0, clientIP(r)) + ok(w, values, nil) +} + +// ---------- Филиалы ---------- + +type branchRequest struct { + Slug string `json:"slug"` + Name string `json:"name"` + Address string `json:"address"` + Phones []string `json:"phones"` + WorkHours []string `json:"work_hours"` + Lat *float64 `json:"lat"` + Lon *float64 `json:"lon"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func cleanList(items []string) []string { + out := []string{} + for _, s := range items { + if s = strings.TrimSpace(s); s != "" { + out = append(out, s) + } + } + return out +} + +func decodeBranch(w http.ResponseWriter, r *http.Request) (storage.BranchInput, bool) { + var req branchRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.BranchInput{}, false + } + in := storage.BranchInput{ + Name: strings.TrimSpace(req.Name), + Address: strings.TrimSpace(req.Address), + Phones: cleanList(req.Phones), + WorkHours: cleanList(req.WorkHours), + Lat: req.Lat, + Lon: req.Lon, + Sort: req.Sort, + Published: req.Published, + } + if in.Sort == 0 { + in.Sort = 100 + } + bad := func(msg string) (storage.BranchInput, bool) { + fail(w, http.StatusUnprocessableEntity, "validation", msg) + return in, false + } + if in.Name == "" || in.Address == "" { + return bad("Укажите название и адрес филиала") + } + slug, valid := resolveSlug(w, req.Slug, in.Name) + if !valid { + return in, false + } + in.Slug = slug + for _, p := range in.Phones { + // digitsRE (handlers_leads.go) совпадает с не-цифрами: после замены остаются только цифры + if n := len(digitsRE.ReplaceAllString(p, "")); n < 10 || n > 12 { + return bad("Телефон «" + p + "» — нужен номер целиком, например +7 928 962-00-94") + } + } + // Первый телефон первого филиала стоит в шапке сайта — без него показать нечего. + if in.Published && len(in.Phones) == 0 { + return bad("Укажите хотя бы один телефон филиала") + } + if (in.Lat == nil) != (in.Lon == nil) { + return bad("Укажите обе координаты или оставьте обе пустыми") + } + if in.Lat != nil && (*in.Lat < -90 || *in.Lat > 90 || *in.Lon < -180 || *in.Lon > 180) { + return bad("Координаты вне допустимого диапазона: широта −90…90, долгота −180…180") + } + return in, true +} + +func (a *API) adminBranches(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminBranches(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateBranch(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeBranch(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateBranch(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "branch", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateBranch(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeBranch(w, r) + if !valid { + return + } + if err := a.store.AdminUpdateBranch(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "branch", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// ---------- Главный экран админки ---------- + +func (a *API) adminDashboard(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + d, err := a.store.Dashboard(r.Context(), 14) + if err != nil { + handleErr(w, err) + return + } + ok(w, d, nil) +} diff --git a/internal/api/admin_site_test.go b/internal/api/admin_site_test.go new file mode 100644 index 0000000..918a534 --- /dev/null +++ b/internal/api/admin_site_test.go @@ -0,0 +1,70 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +const validLegal = `"legal":{"org_name":"ООО «Дана Медикал»","org_short":"ООО «Дана Медикал»","inn":"0500020358","ogrn":"1240500017702","address":"Махачкала","license":"Л041-01172-05/03997730"}` + +func TestDecodeSettings(t *testing.T) { + cases := []struct { + name, body, wantText string + wantCode int + }{ + {"valid", `{"email":"danaclinic@yandex.ru","max_url":"https://max.ru/dana",` + validLegal + `}`, "", 200}, + {"no max link", `{"email":"",` + validLegal + `}`, "", 200}, + {"bad email", `{"email":"danaclinic",` + validLegal + `}`, "почты", 422}, + {"max over http", `{"max_url":"javascript:alert(1)",` + validLegal + `}`, "MAX", 422}, + {"short inn", `{` + strings.Replace(validLegal, "0500020358", "05000", 1) + `}`, "ИНН", 422}, + {"bad ogrn", `{` + strings.Replace(validLegal, "1240500017702", "12405000177", 1) + `}`, "ОГРН", 422}, + {"no license", `{` + strings.Replace(validLegal, "Л041-01172-05/03997730", " ", 1) + `}`, "лицензию", 422}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("PUT", "/", strings.NewReader(tc.body)) + values, valid := decodeSettings(w, r) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + // в настройки пишутся только известные ключи + if valid && len(values) != 3 { + t.Errorf("%s: unexpected keys %v", tc.name, values) + } + } +} + +func TestDecodeBranch(t *testing.T) { + cases := []struct { + name, body, wantText string + wantCode int + }{ + {"valid", `{"name":"Ляхова, 22","address":"Махачкала","phones":[" +7 928 962-00-94 ",""],"lat":42.97,"lon":47.49,"published":true}`, "", 200}, + {"hidden without phone", `{"name":"Новый","address":"Махачкала","published":false}`, "", 200}, + {"published without phone", `{"name":"Новый","address":"Махачкала","published":true}`, "телефон", 422}, + {"short phone", `{"name":"Новый","address":"Махачкала","phones":["962-00"]}`, "целиком", 422}, + {"one coordinate", `{"name":"Новый","address":"Махачкала","lat":42.9}`, "обе координаты", 422}, + {"coordinates out of range", `{"name":"Новый","address":"Махачкала","lat":142.9,"lon":47.5}`, "диапазона", 422}, + {"no address", `{"name":"Новый","address":" "}`, "адрес", 422}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(tc.body)) + in, valid := decodeBranch(w, r) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + if tc.name == "valid" && (len(in.Phones) != 1 || in.Phones[0] != "+7 928 962-00-94" || in.Slug != "lyakhova-22") { + t.Errorf("valid: got %+v", in) + } + } +} diff --git a/internal/api/admin_specialties.go b/internal/api/admin_specialties.go new file mode 100644 index 0000000..b5ef468 --- /dev/null +++ b/internal/api/admin_specialties.go @@ -0,0 +1,153 @@ +package api + +import ( + "encoding/json" + "errors" + "net/http" + "strconv" + "strings" + + "dana/internal/storage" +) + +type specialtyRequest struct { + Slug string `json:"slug"` + Name string `json:"name"` + NamePlural string `json:"name_plural"` + Sort int `json:"sort"` +} + +func decodeSpecialty(w http.ResponseWriter, r *http.Request, withSlug bool) (storage.SpecialtyInput, bool) { + var req specialtyRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.SpecialtyInput{}, false + } + in := storage.SpecialtyInput{ + Slug: strings.ToLower(strings.TrimSpace(req.Slug)), + Name: strings.TrimSpace(req.Name), + NamePlural: strings.TrimSpace(req.NamePlural), + Sort: req.Sort, + } + if in.Sort == 0 { + in.Sort = 500 + } + if in.NamePlural == "" { + in.NamePlural = in.Name + } + if in.Name == "" { + in.Name = in.NamePlural + } + if in.Name == "" || len([]rune(in.Name)) > 100 || len([]rune(in.NamePlural)) > 100 { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название специальности (до 100 символов)") + return in, false + } + if withSlug && (!slugRE.MatchString(in.Slug) || len(in.Slug) > 100) { + fail(w, http.StatusUnprocessableEntity, "validation", "Адрес — латиница, цифры и дефис, например «kardiologi»") + return in, false + } + return in, true +} + +func (a *API) adminSpecialties(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminSpecialties(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateSpecialty(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeSpecialty(w, r, true) + if !valid { + return + } + id, err := a.store.AdminCreateSpecialty(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "specialty", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} + +func (a *API) adminUpdateSpecialty(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeSpecialty(w, r, false) + if !valid { + return + } + if err := a.store.AdminUpdateSpecialty(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "specialty", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminDeleteSpecialty(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteSpecialty(r.Context(), id); err != nil { + if errors.Is(err, storage.ErrInUse) { + fail(w, http.StatusConflict, "in_use", "В этой специальности есть врачи — сначала перенесите или удалите их") + return + } + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "specialty", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +type newDoctorRequest struct { + Slug string `json:"slug"` + LastName string `json:"last_name"` + FirstName string `json:"first_name"` + MiddleName string `json:"middle_name"` + Position string `json:"position"` + SpecialtyID int64 `json:"specialty_id"` +} + +// adminCreateDoctor заводит врача скрытым; фото, биографию и прочее заполняют в карточке. +func (a *API) adminCreateDoctor(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + var req newDoctorRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + in := storage.NewDoctor{ + Slug: strings.ToLower(strings.TrimSpace(req.Slug)), + LastName: strings.TrimSpace(req.LastName), + FirstName: strings.TrimSpace(req.FirstName), + MiddleName: strings.TrimSpace(req.MiddleName), + Position: strings.TrimSpace(req.Position), + SpecialtyID: req.SpecialtyID, + } + switch { + case in.LastName == "" || in.FirstName == "": + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите фамилию и имя") + return + case in.SpecialtyID <= 0: + fail(w, http.StatusUnprocessableEntity, "validation", "Выберите специальность — от неё зависит адрес страницы врача") + return + case !slugRE.MatchString(in.Slug) || len(in.Slug) > 100: + fail(w, http.StatusUnprocessableEntity, "validation", "Адрес — латиница, цифры и дефис, например «ivanov-ivan»") + return + } + id, err := a.store.AdminCreateDoctor(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "doctor", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id}}) +} diff --git a/internal/api/admin_specialties_test.go b/internal/api/admin_specialties_test.go new file mode 100644 index 0000000..c70fa3a --- /dev/null +++ b/internal/api/admin_specialties_test.go @@ -0,0 +1,52 @@ +package api + +import ( + "net/http/httptest" + "strings" + "testing" +) + +func TestDecodeSpecialtyRules(t *testing.T) { + cases := []struct { + name, body string + withSlug bool + wantCode int + wantText string + }{ + {"plural only fills name", `{"name_plural":"Кардиологи","slug":"kardiologi"}`, true, 200, ""}, + {"no name", `{"name_plural":" ","slug":"x"}`, true, 422, "название"}, + {"cyrillic slug", `{"name_plural":"Кардиологи","slug":"кардиологи"}`, true, 422, "латиница"}, + {"slug ignored on update", `{"name_plural":"Кардиологи"}`, false, 200, ""}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", "/", strings.NewReader(tc.body)) + in, valid := decodeSpecialty(w, r, tc.withSlug) + code := 200 + if !valid { + code = w.Code + } + if code != tc.wantCode || (tc.wantText != "" && !strings.Contains(w.Body.String(), tc.wantText)) { + t.Errorf("%s: got %d %s, want %d containing %q", tc.name, code, w.Body.String(), tc.wantCode, tc.wantText) + } + if valid && (in.Name == "" || in.Sort != 500) { + t.Errorf("%s: defaults not applied: %+v", tc.name, in) + } + } +} + +func TestCreateDoctorValidation(t *testing.T) { + a := &API{} + cases := []struct{ body, wantText string }{ + {`{"last_name":"Иванов","slug":"ivanov","specialty_id":2}`, "фамилию и имя"}, + {`{"last_name":"Иванов","first_name":"Иван","slug":"ivanov"}`, "специальность"}, + {`{"last_name":"Иванов","first_name":"Иван","slug":"Иванов","specialty_id":2}`, "латиница"}, + } + for _, tc := range cases { + w := httptest.NewRecorder() + a.adminCreateDoctor(w, httptest.NewRequest("POST", "/", strings.NewReader(tc.body)), nil) + if w.Code != 422 || !strings.Contains(w.Body.String(), tc.wantText) { + t.Errorf("%s: got %d %s", tc.body, w.Code, w.Body.String()) + } + } +} diff --git a/internal/api/admin_stats.go b/internal/api/admin_stats.go new file mode 100644 index 0000000..a4bb282 --- /dev/null +++ b/internal/api/admin_stats.go @@ -0,0 +1,67 @@ +package api + +import ( + "net/http" + "strconv" + "time" + + "dana/internal/storage" +) + +var moscow = func() *time.Location { + if loc, err := time.LoadLocation("Europe/Moscow"); err == nil { + return loc + } + return time.FixedZone("MSK", 3*60*60) +}() + +// statsRange разбирает ?from=YYYY-MM-DD&to=YYYY-MM-DD (обе даты включительно, по Москве). +// По умолчанию — последние 30 дней. Период — не длиннее двух лет. +func statsRange(q map[string][]string, now time.Time) (from, to time.Time, ok bool) { + get := func(k string) string { + if v := q[k]; len(v) > 0 { + return v[0] + } + return "" + } + today := time.Date(now.In(moscow).Year(), now.In(moscow).Month(), now.In(moscow).Day(), 0, 0, 0, 0, moscow) + to = today.AddDate(0, 0, 1) + from = today.AddDate(0, 0, -29) + if v := get("from"); v != "" { + t, err := time.ParseInLocation("2006-01-02", v, moscow) + if err != nil { + return from, to, false + } + from = t + } + if v := get("to"); v != "" { + t, err := time.ParseInLocation("2006-01-02", v, moscow) + if err != nil { + return from, to, false + } + to = t.AddDate(0, 0, 1) + } + if !to.After(from) || to.Sub(from) > 2*366*24*time.Hour { + return from, to, false + } + return from, to, true +} + +func (a *API) adminStats(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + from, to, valid := statsRange(r.URL.Query(), time.Now()) + if !valid { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный период: даты в формате ГГГГ-ММ-ДД, не длиннее двух лет") + return + } + branchID, _ := strconv.ParseInt(r.URL.Query().Get("branch_id"), 10, 64) + st, err := a.store.Stats(r.Context(), storage.StatsFilter{From: from, To: to, BranchID: branchID}) + if err != nil { + handleErr(w, err) + return + } + ok(w, st, map[string]any{ + "from": from.Format("2006-01-02"), + "to": to.AddDate(0, 0, -1).Format("2006-01-02"), + "days": int(to.Sub(from).Hours()/24 + 0.5), + }) +} diff --git a/internal/api/admin_stats_test.go b/internal/api/admin_stats_test.go new file mode 100644 index 0000000..462ee90 --- /dev/null +++ b/internal/api/admin_stats_test.go @@ -0,0 +1,27 @@ +package api + +import ( + "testing" + "time" +) + +func TestStatsRange(t *testing.T) { + now := time.Date(2026, 9, 21, 22, 30, 0, 0, time.UTC) // 22 сентября 01:30 по Москве + from, to, ok := statsRange(map[string][]string{}, now) + if !ok || from.Format("2006-01-02") != "2026-08-24" || to.Format("2006-01-02") != "2026-09-23" { + t.Fatalf("default range = %v..%v ok=%v", from, to, ok) + } + from, to, ok = statsRange(map[string][]string{"from": {"2026-09-01"}, "to": {"2026-09-01"}}, now) + if !ok || to.Sub(from) != 24*time.Hour { + t.Fatalf("one day range = %v..%v", from, to) + } + for _, q := range []map[string][]string{ + {"from": {"01.09.2026"}}, + {"from": {"2026-09-10"}, "to": {"2026-09-01"}}, + {"from": {"2020-01-01"}, "to": {"2026-01-01"}}, + } { + if _, _, ok := statsRange(q, now); ok { + t.Errorf("range %v accepted", q) + } + } +} diff --git a/internal/api/admin_vacancies.go b/internal/api/admin_vacancies.go new file mode 100644 index 0000000..84f05c4 --- /dev/null +++ b/internal/api/admin_vacancies.go @@ -0,0 +1,124 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + "dana/internal/storage" +) + +type vacancyRequest struct { + Slug string `json:"slug"` + Title string `json:"title"` + Description string `json:"description"` + Duties string `json:"duties"` + Requirements string `json:"requirements"` + Conditions string `json:"conditions"` + Salary string `json:"salary"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +// cleanLines убирает пустые строки и лишние пробелы: на сайте каждая строка — пункт списка. +func cleanLines(s string) string { + var out []string + for _, line := range strings.Split(strings.ReplaceAll(s, "\r", ""), "\n") { + line = strings.TrimSpace(strings.TrimLeft(strings.TrimSpace(line), "-•–—*")) + if line != "" { + out = append(out, line) + } + } + return strings.Join(out, "\n") +} + +func decodeVacancy(w http.ResponseWriter, r *http.Request) (storage.VacancyInput, bool) { + var req vacancyRequest + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 256<<10)).Decode(&req); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return storage.VacancyInput{}, false + } + in := storage.VacancyInput{ + Title: strings.TrimSpace(req.Title), + Description: strings.TrimSpace(req.Description), + Duties: cleanLines(req.Duties), + Requirements: cleanLines(req.Requirements), + Conditions: cleanLines(req.Conditions), + Salary: strings.TrimSpace(req.Salary), + Sort: req.Sort, + Published: req.Published, + } + if in.Sort == 0 { + in.Sort = 100 + } + if in.Title == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Укажите название вакансии") + return in, false + } + slug, valid := resolveSlug(w, req.Slug, in.Title) + if !valid { + return in, false + } + in.Slug = slug + if in.Published && in.Description == "" && in.Duties == "" && in.Requirements == "" && in.Conditions == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Чтобы показать вакансию на сайте, опишите её") + return in, false + } + return in, true +} + +func (a *API) adminVacancies(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + list, err := a.store.AdminVacancies(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, map[string]any{"total": len(list)}) +} + +func (a *API) adminCreateVacancy(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + in, valid := decodeVacancy(w, r) + if !valid { + return + } + id, err := a.store.AdminCreateVacancy(r.Context(), in) + if err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "create", "vacancy", id, clientIP(r)) + writeJSON(w, http.StatusCreated, envelope{Data: map[string]any{"id": id, "slug": in.Slug}}) +} + +func (a *API) adminUpdateVacancy(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + in, valid := decodeVacancy(w, r) + if !valid { + return + } + if err := a.store.AdminUpdateVacancy(r.Context(), id, in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "vacancy", id, clientIP(r)) + ok(w, map[string]any{"id": id, "slug": in.Slug}, nil) +} + +func (a *API) adminDeleteVacancy(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.AdminDeleteVacancy(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "vacancy", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} diff --git a/internal/api/admin_weekly.go b/internal/api/admin_weekly.go new file mode 100644 index 0000000..0164f74 --- /dev/null +++ b/internal/api/admin_weekly.go @@ -0,0 +1,281 @@ +package api + +import ( + "context" + "encoding/json" + "io" + "log/slog" + "net/http" + "path" + "regexp" + "strconv" + "strings" + "time" + + "dana/internal/storage" +) + +// Еженедельный/ежемесячный дашборд по каналам: отдельной загрузки файлов у него нет — +// источник один, это обычный «Отчёт по каналам» (вкладка «Новый отчёт»). Если в загруженной +// туда выгрузке 1С есть текст «Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ», посчитанный результат +// автоматически добавляется ещё и сюда, одной строкой истории. Эта страница — только +// просмотр накопленных периодов и правка бюджета по каналам. + +// Dates in 1C are inclusive. Internally all integrations use [from, to). +var periodRE = regexp.MustCompile(`(?i)период\s*:?\s*(?:с\s+)?(\d{1,2}\.\d{1,2}\.\d{4})\s*(?:-|–|—|по)\s*(\d{1,2}\.\d{1,2}\.\d{4})`) + +type reportFilePeriod struct { + From string `json:"from"` + To string `json:"to"` // inclusive, for the user + Label string `json:"label"` + Mode string `json:"mode"` +} + +type reportDashboard struct { + reportFilePeriod + ID int64 `json:"id,omitempty"` + Warning string `json:"warning,omitempty"` +} + +func describeReportPeriod(from, to time.Time) reportFilePeriod { + mode := "weeks" + if from.Day() == 1 && to.Equal(time.Date(from.Year(), from.Month()+1, 1, 0, 0, 0, 0, moscow)) { + mode = "months" + } + last := to.AddDate(0, 0, -1) + return reportFilePeriod{From: from.Format("2006-01-02"), To: last.Format("2006-01-02"), + Label: from.Format("02.01.2006") + "–" + last.Format("02.01.2006"), Mode: mode} +} + +func parseReportPeriod(text string) (from, to time.Time, label string, ok bool) { + m := periodRE.FindStringSubmatch(strings.Join(strings.Fields(text), " ")) + if m == nil { + return + } + f, err1 := time.ParseInLocation("2.1.2006", m[1], moscow) + t, err2 := time.ParseInLocation("2.1.2006", m[2], moscow) + if err1 != nil || err2 != nil || t.Before(f) { + return + } + end := t.AddDate(0, 0, 1) + if end.Sub(f) > 93*24*time.Hour { + return + } + return f, end, f.Format("02.01") + "–" + t.Format("02.01"), true +} + +// Row 2 of the actual 1C sheet is authoritative, not the filename, channel sheets +// or the report generation timestamp. Older exports may put it above the headers elsewhere. +func detectPeriodFromFile(name string, data []byte) (from, to time.Time, label string, ok bool) { + sheets, err := readWorkbook(name, data) + if err != nil { + return + } + for _, s := range sheets { + if serviceSheets[strings.ToLower(strings.TrimSpace(s.Name))] { + continue + } + cols, is1C := find1CColumns(s.Rows) + if !is1C { + continue + } + if cols.header > 1 { + text := strings.Join(s.Rows[1], " ") + if strings.Contains(strings.ToLower(text), "период") { + return parseReportPeriod(text) + } + } + for i := 0; i < cols.header; i++ { + text := strings.Join(s.Rows[i], " ") + if strings.Contains(strings.ToLower(text), "период") { + return parseReportPeriod(text) + } + } + return + } + return +} + +// saveWeeklyFromSummary — лучшая попытка: если в файле нашёлся период, кладёт уже посчитанную +// сводку «Отчёта по каналам» ещё и в историю дашборда. Ошибки сюда не прерывают основной отчёт, +// только пишутся в лог — для admin ничего не меняется, кроме того, что период не появится в дашборде. +func (a *API) saveWeeklyFromSummary(ctx context.Context, clinic, name string, data []byte, sum channelsSummary, adminID int64) *reportDashboard { + from, to, label, found := detectPeriodFromFile(name, data) + if !found { + return nil + } + result := &reportDashboard{reportFilePeriod: describeReportPeriod(from, to)} + cc, err := a.weeklyCallCenter(ctx, clinic, from, to, sum) + if err != nil { + slog.Warn("weekly dashboard: колл-ц", "err", err) + } + if id, err := a.store.UpsertWeeklyReport(ctx, clinic, from, to, label, name, cc, weeklyChannelsFromSummary(sum), adminID); err != nil { + slog.Warn("weekly dashboard: сохранить период", "err", err) + result.Warning = "Расчёт готов, но период не сохранился в дашборде. Повторите загрузку файла." + } else { + result.ID = id + } + return result +} + +// Read-only upload preview: no CRM/PBX requests and no database writes. +func (a *API) adminReportPeriod(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + r.Body = http.MaxBytesReader(w, r.Body, maxReportFile+(1<<20)) + if err := r.ParseMultipartForm(maxReportFile); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Файл больше 10 МБ или запрос повреждён") + return + } + if r.MultipartForm != nil { + defer r.MultipartForm.RemoveAll() + } + f, header, err := r.FormFile("file") + if err != nil { + fail(w, http.StatusUnprocessableEntity, "validation", "Выберите выгрузку 1С") + return + } + defer f.Close() + data, err := io.ReadAll(f) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Файл не дочитался") + return + } + name := path.Base(strings.ReplaceAll(header.Filename, "\\", "/")) + from, to, _, found := detectPeriodFromFile(name, data) + if !found { + fail(w, http.StatusUnprocessableEntity, "validation", "Не найден корректный период в строке 2 листа 1С: Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ") + return + } + ok(w, describeReportPeriod(from, to), nil) +} + +func weeklyChannelsFromSummary(sum channelsSummary) []storage.WeeklyChannel { + channels := make([]storage.WeeklyChannel, 0, len(sum.Channels)+1) + for _, c := range sum.Channels { + channels = append(channels, storage.WeeklyChannel{ + Key: weeklyChannelKey(c.Name), Title: c.Name, + Leads: c.Leads, Patients: c.Patients, Services: c.Services, PerPat: c.PerPat, + Revenue: c.Revenue, AvgCheck: c.AvgCheck, + }) + } + channels = append(channels, storage.WeeklyChannel{ + Key: "итого-интернет", Title: sum.Internet.Name, + Leads: sum.Internet.Leads, Patients: sum.Internet.Patients, Services: sum.Internet.Services, + PerPat: sum.Internet.PerPat, Revenue: sum.Internet.Revenue, AvgCheck: sum.Internet.AvgCheck, + }) + return channels +} + +// weeklyChannelKey — заголовок канала (как в книге) обратно в ключ knownChannels, для стабильной +// привязки бюджета к каналу между неделями. Неизвестные каналы используют сам заголовок в нижнем регистре. +func weeklyChannelKey(title string) string { + for k, d := range knownChannels { + if d.title == title { + return k + } + } + return strings.ToLower(strings.TrimSpace(title)) +} + +func (a *API) adminWeeklyReports(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + clinic, valid := requestClinic(w, r) + if !valid { + return + } + list, err := a.store.WeeklyReports(r.Context(), clinic.Key) + if err != nil { + handleErr(w, err) + return + } + ok(w, list, nil) +} + +func (a *API) adminDeleteWeeklyReport(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + if err := a.store.DeleteWeeklyReport(r.Context(), id); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "delete", "weekly_report", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +func (a *API) adminSetWeeklyBudget(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный id") + return + } + var body struct { + Channel string `json:"channel"` + Amount float64 `json:"amount"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Channel == "" || body.Amount < 0 { + fail(w, http.StatusUnprocessableEntity, "validation", "Нужны channel и неотрицательный amount") + return + } + if err := a.store.SetWeeklyBudget(r.Context(), id, body.Channel, body.Amount); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "weekly_report_budget", id, clientIP(r)) + ok(w, map[string]any{"id": id}, nil) +} + +// weeklyCallCenterSales — пациенты/услуги/выручка блока «Колл-ц»: совпадения номеров колл-центра +// («Колл-центр (все номера)») с 1С, как в таблице «Показатели», а не вся выгрузка 1С за период. +func weeklyCallCenterSales(sum channelsSummary) storage.WeeklyCallCenter { + title := knownChannels["все номера"].title + for _, c := range sum.Channels { + if c.Name == title { + return storage.WeeklyCallCenter{Patients: c.Patients, Services: c.Services, Revenue: c.Revenue, AvgCheck: c.AvgCheck} + } + } + return storage.WeeklyCallCenter{} +} + +// weeklyCallCenter — блок «Колл-ц»: весь колл-центр за период, без привязки к рекламному каналу. +// Пациенты/услуги/выручка — совпадения колл-центра из уже посчитанной сводки отчёта, звонки и время — из АТС. +func (a *API) weeklyCallCenter(ctx context.Context, clinic string, from, to time.Time, sum channelsSummary) (storage.WeeklyCallCenter, error) { + cc := weeklyCallCenterSales(sum) + if !a.pbxFor(clinic).Configured() { + return cc, nil + } + calls, err := a.store.PBXCalls(ctx, clinic, from, to) + if err != nil { + return cc, err + } + uniq := map[string]bool{} + var talkSum, waitSum, answered int + for _, c := range calls { + cc.Total++ + switch { + case isOutgoing(c): + cc.Outgoing++ + default: + cc.Incoming++ + if c.Client != "" { + uniq[c.Client] = true + } + if isMissed(c) { + cc.Missed++ + } else { + answered++ + talkSum += c.Duration + waitSum += c.Wait + } + } + } + cc.Unique = len(uniq) + if cc.Incoming > 0 { + cc.MissedPct = float64(cc.Missed) / float64(cc.Incoming) * 100 + } + if answered > 0 { + cc.TalkSec = talkSum / answered + cc.WaitSec = waitSum / answered + } + return cc, nil +} diff --git a/internal/api/adminui.go b/internal/api/adminui.go new file mode 100644 index 0000000..1c9d857 --- /dev/null +++ b/internal/api/adminui.go @@ -0,0 +1,18 @@ +package api + +import ( + "embed" + "io/fs" +) + +//go:embed adminui/* +var adminUIFS embed.FS + +// adminFS отдаёт файлы интерфейса админки, вшитые в бинарник. +func adminFS() fs.FS { + sub, err := fs.Sub(adminUIFS, "adminui") + if err != nil { + panic(err) + } + return sub +} diff --git a/internal/api/adminui/admin.css b/internal/api/adminui/admin.css new file mode 100644 index 0000000..de2780e --- /dev/null +++ b/internal/api/adminui/admin.css @@ -0,0 +1,1275 @@ +/* Админка DANA в визуальном языке референса BioTrack: воздушная оболочка на молочно-розовом + градиенте, белые карточки с большими скруглениями, органическая лента навигации. */ + +@font-face { font-family: 'Montserrat Alternates'; font-weight: 300; font-display: swap; + src: url(fonts/montserrat-alternates-cyrillic-300-normal.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 300; font-display: swap; + src: url(fonts/montserrat-alternates-latin-300-normal.woff2) format('woff2'); } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 400; font-display: swap; + src: url(fonts/montserrat-alternates-cyrillic-400-normal.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 400; font-display: swap; + src: url(fonts/montserrat-alternates-latin-400-normal.woff2) format('woff2'); } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 500; font-display: swap; + src: url(fonts/montserrat-alternates-cyrillic-500-normal.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 500; font-display: swap; + src: url(fonts/montserrat-alternates-latin-500-normal.woff2) format('woff2'); } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 600; font-display: swap; + src: url(fonts/montserrat-alternates-cyrillic-600-normal.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } +@font-face { font-family: 'Montserrat Alternates'; font-weight: 600; font-display: swap; + src: url(fonts/montserrat-alternates-latin-600-normal.woff2) format('woff2'); } + +:root { + --bg-milk: #f6f6f5; + --bg-cool: #eeeeef; + --bg-powder: #efcfd2; + --card: #ffffff; + --text: #33393b; + --muted: #929793; + --muted-2: #b5b8b6; + --line: #efefec; + --field: #f6f6f4; + --blue: #8dcbe9; + --blue-deep: #6cacED; + --logo-blue: #a7c9ed; + --mint: #bfe8d6; + --mint-text: #3f8f6d; + --rose: #e78c9f; + --rose-soft: #fbe3e8; + --rose-text: #b8566c; + --sky-soft: #dff0f8; + --sky-text: #3f86a8; + --butter: #f4e9b2; + --sage: #eaeef0; + --danger: #c2495d; + --warn: #b7813a; + --shadow: 0 10px 30px rgba(51, 57, 59, 0.05), 0 1px 2px rgba(51, 57, 59, 0.03); + --shadow-lift: 0 16px 40px rgba(51, 57, 59, 0.08); + --r-card: 26px; + --r-field: 14px; + /* Montserrat Alternates (шрифт сайта DANA) — для крупного: заголовки, цифры, логотип. + У него стилизованные кириллические «т» и «д», в мелком тексте они читаются плохо. */ + --font-display: 'Montserrat Alternates', 'Segoe UI', system-ui, sans-serif; + --font: 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif; + --ease: cubic-bezier(.22, .8, .24, 1); + /* совместимость с классами, которые использует код разделов */ + --accent: var(--text); + --accent-dark: #1f2426; +} + +* { box-sizing: border-box; } +/* hidden должен побеждать display: grid/flex у бейджей, подсказок и выпадающих окон */ +[hidden] { display: none !important; } +html { -webkit-text-size-adjust: 100%; } +body { + margin: 0; + min-height: 100vh; + font: 400 14px/1.5 var(--font); + color: var(--text); + background: + radial-gradient(1200px 900px at 100% 0%, rgba(239, 207, 210, .95), transparent 62%), + radial-gradient(900px 700px at 105% 100%, rgba(240, 214, 216, .7), transparent 60%), + linear-gradient(115deg, #f9f9f8 0%, #f1f1f1 38%, #f1e6e6 70%, #efd3d5 100%); + background-attachment: fixed; +} +button, input, select, textarea { font: inherit; color: inherit; } +a { color: inherit; } +:focus-visible { outline: 2px solid var(--blue-deep); outline-offset: 2px; } +.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } + +/* ---------- вход ---------- */ +#login { + max-width: 380px; margin: 12vh auto 0; padding: 34px 32px 28px; + background: var(--card); border-radius: 30px; box-shadow: var(--shadow); +} +#login h2 { margin: 18px 0 20px; font-family: var(--font-display); font-weight: 400; font-size: 26px; letter-spacing: -0.03em; } +#login label { display: block; margin-bottom: 14px; } +#login span { display: block; margin-bottom: 6px; color: var(--muted); font-size: 12px; } +.err { color: var(--danger); margin-top: 10px; min-height: 20px; font-size: 13px; } + +/* ---------- каркас ---------- */ +#app { display: flex; min-height: 100vh; padding: 18px 22px 18px 16px; gap: 22px; } +.shell { flex: 1; min-width: 0; display: flex; flex-direction: column; } + +/* органическая лента навигации: белая форма с плавными «перетяжками» по контуру */ +.side { + position: sticky; top: 18px; align-self: flex-start; + /* Keep tooltip descendants above the adjacent page's stacking contexts. */ + z-index: 50; + width: 76px; height: calc(100vh - 36px); min-height: 560px; flex-shrink: 0; + display: flex; flex-direction: column; align-items: center; + padding: 22px 0 18px; + filter: drop-shadow(0 12px 28px rgba(51, 57, 59, 0.06)); +} +.side__shape { position: absolute; inset: 0; width: 100%; height: 100%; z-index: -1; } +.side__logo { + font-family: var(--font-display); + display: flex; flex-direction: column; align-items: center; line-height: .92; + font-weight: 600; font-size: 17px; letter-spacing: -0.04em; text-decoration: none; +} +.side__logo b { color: var(--logo-blue); font-weight: 600; } +.side__logo span { color: var(--text); font-size: 13px; font-weight: 500; letter-spacing: -0.02em; } +.side__nav { display: flex; flex-direction: column; gap: 14px; margin-top: 78px; } +.side__foot { margin-top: auto; padding-top: 60px; display: flex; flex-direction: column; align-items: center; gap: 16px; } +.side__btn { + position: relative; width: 46px; height: 46px; border: 0; border-radius: 16px; + display: grid; place-items: center; background: transparent; color: #7e8481; cursor: pointer; + transition: background-color .25s var(--ease), color .25s var(--ease); +} +.side__btn svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } +.side__btn:hover { color: var(--text); background: #f3f5f5; } +.side__btn[aria-current="page"] { background: var(--sage); color: var(--text); } +.side__badge { + position: absolute; top: 6px; right: 6px; min-width: 16px; height: 16px; padding: 0 4px; + border-radius: 8px; background: var(--rose); color: #fff; font-size: 9px; font-weight: 600; + display: grid; place-items: center; line-height: 1; +} +.side__btn .tip { + position: absolute; left: calc(100% + 12px); top: 50%; transform: translate(-4px, -50%); + padding: 7px 12px; border-radius: 12px; background: var(--card); color: var(--text); + font-size: 12px; white-space: nowrap; box-shadow: var(--shadow-lift); + opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s var(--ease); z-index: 30; +} +.side__btn:hover .tip, .side__btn:focus-visible .tip { opacity: 1; transform: translate(0, -50%); } +.switch { + position: relative; width: 46px; height: 26px; border: 0; border-radius: 13px; padding: 0; + background: #e3e6e5; cursor: pointer; transition: background-color .25s; +} +.switch::after { + content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; + background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.12); transition: transform .25s var(--ease); +} +.switch[aria-checked="true"] { background: var(--blue-deep); } +.switch[aria-checked="true"]::after { transform: translateX(20px); } +.switch .tip { left: calc(100% + 12px); } +.switch:hover .tip, .switch:focus-visible .tip { opacity: 1; transform: translate(0, -50%); } +.switch .tip { + position: absolute; top: 50%; transform: translate(-4px, -50%); padding: 7px 12px; border-radius: 12px; + background: var(--card); color: var(--text); font-size: 12px; white-space: nowrap; box-shadow: var(--shadow-lift); + opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s var(--ease); z-index: 30; +} + +/* ---------- верхняя панель ---------- */ +.top { display: flex; align-items: center; gap: 12px; min-height: 58px; padding-left: 4px; position: relative; z-index: 20; } +.search { position: relative; width: min(330px, 40vw); } +.search__shape { display: flex; align-items: center; } +.search input { + flex: 1; height: 42px; border: 0; border-radius: 21px 0 0 21px; padding: 0 18px; + background: var(--card); font-size: 12px; outline: none; min-width: 0; +} +.search input::placeholder { color: var(--muted); } +/* «перетяжка» между полем и кругом с лупой, как у капсулы поиска в референсе */ +.search__neck { width: 22px; height: 42px; flex-shrink: 0; display: block; } +.search__btn { + width: 46px; height: 46px; margin-left: -4px; border-radius: 50%; border: 0; cursor: pointer; + background: var(--card); display: grid; place-items: center; flex-shrink: 0; +} +.search__btn span { width: 34px; height: 34px; border-radius: 50%; background: var(--sage); display: grid; place-items: center; } +.search__btn svg, .round svg { width: 17px; height: 17px; stroke: var(--text); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.search__results { + position: absolute; left: 0; top: calc(100% + 8px); width: min(460px, 86vw); max-height: 60vh; overflow: auto; + background: var(--card); border-radius: 20px; box-shadow: var(--shadow-lift); padding: 8px; +} +.search__results button { + display: flex; width: 100%; gap: 10px; align-items: baseline; text-align: left; + border: 0; background: none; padding: 9px 12px; border-radius: 12px; cursor: pointer; +} +.search__results button:hover, .search__results button:focus-visible { background: var(--field); } +.search__results small { color: var(--muted); font-size: 11px; margin-left: auto; white-space: nowrap; } +.search__empty { padding: 12px; color: var(--muted); font-size: 12px; } +.top__spacer { flex: 1; } +.round { + position: relative; width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer; + background: var(--card); display: grid; place-items: center; transition: box-shadow .25s, transform .25s var(--ease); +} +.round:hover { box-shadow: var(--shadow-lift); transform: translateY(-1px); } +.round__dot { position: absolute; top: 10px; right: 11px; width: 7px; height: 7px; border-radius: 50%; background: var(--rose); box-shadow: 0 0 0 2px #fff; } +.who { display: flex; align-items: center; gap: 12px; margin-left: 18px; } +.who__avatar { + width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; + background: linear-gradient(135deg, #dcebf5, #f4d9de); color: var(--text); font-weight: 500; font-size: 14px; + box-shadow: 0 0 0 3px #fff; +} +.who__name { font-weight: 500; font-size: 14px; line-height: 1.2; } +.who__meta { color: var(--muted); font-size: 11px; margin-top: 3px; display: flex; gap: 10px; } +.popover { + position: absolute; top: calc(100% + 10px); right: 0; width: 320px; max-height: 70vh; overflow: auto; + background: var(--card); border-radius: 22px; box-shadow: var(--shadow-lift); padding: 16px; z-index: 40; +} +.popover h4 { margin: 2px 4px 10px; font-weight: 500; font-size: 13px; } +.popover__item { + display: flex; gap: 12px; align-items: center; width: 100%; padding: 10px; border: 0; background: none; + border-radius: 14px; text-align: left; cursor: pointer; +} +.popover__item:hover { background: var(--field); } +.popover__item b { font-family: var(--font-display); font-weight: 500; font-size: 18px; min-width: 32px; letter-spacing: -0.02em; } +.popover__item span { font-size: 12px; color: var(--text); } +.popover__item small { display: block; color: var(--muted); font-size: 11px; } +.popover__empty { color: var(--muted); font-size: 12px; padding: 8px 4px; } + +/* ---------- переключатель подразделов ---------- */ +.subtabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 18px; padding: 5px; width: fit-content; max-width: 100%; + background: rgba(255, 255, 255, .6); border-radius: 18px; backdrop-filter: blur(6px); } +.subtabs:empty { display: none; } +.subtabs button { border: 0; background: none; padding: 8px 16px; border-radius: 14px; cursor: pointer; color: var(--muted); font-size: 13px; } +.subtabs button:hover { color: var(--text); } +.subtabs button[aria-current="page"] { background: linear-gradient(100deg, #f08a3c, #f6a45e); color: #fff; box-shadow: 0 6px 16px rgba(240, 138, 60, .28); } +.page-title { margin: 10px 0 4px; font-family: var(--font-display); font-weight: 500; font-size: 40px; letter-spacing: -0.04em; line-height: 1.05; } +main { flex: 1; min-width: 0; } + +/* ---------- карточки, таблицы, формы (все разделы) ---------- */ +.card { + background: var(--card); border-radius: var(--r-card); padding: 22px 24px; margin-bottom: 18px; + box-shadow: var(--shadow); border: 0; +} +table { width: 100%; border-collapse: collapse; } +th, td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; } +th { white-space: nowrap; font-size: 11px; font-weight: 500; color: var(--muted); letter-spacing: 0; text-transform: none; } +tr:last-child td { border-bottom: 0; } +tbody tr { transition: background-color .2s; } +tbody tr:hover { background: #fafaf8; } +input, select, textarea { + padding: 9px 13px; border: 1px solid #ebebe7; border-radius: var(--r-field); background: #fff; width: 100%; + transition: border-color .2s, box-shadow .2s; +} +input:focus, select:focus, textarea:focus { outline: none; border-color: #d3d7d5; box-shadow: 0 0 0 4px rgba(141, 203, 233, .22); } +/* Native controls retain keyboard/form behaviour; only their presentation changes. */ +input[type=checkbox], .rp-source-opt input[type=radio] { + appearance: none; width: 20px; min-width: 20px; height: 20px; flex: 0 0 20px; + padding: 0; margin: 0; border: 1.5px solid #c7d3df; border-radius: 6px; + background: #fff center / 14px 14px no-repeat; vertical-align: middle; + cursor: pointer; transition: background-color .18s, border-color .18s, box-shadow .18s; +} +.rp-source-opt input[type=radio] { border-radius: 50%; } +input[type=checkbox]:checked, .rp-source-opt input[type=radio]:checked { + border-color: #4389e9; background-color: #4389e9; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m3.5 8 3 3 6-6' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} +input[type=checkbox]:indeterminate { + border-color: #4389e9; background-color: #4389e9; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8h8' fill='none' stroke='white' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E"); +} +input[type=checkbox]:not(:disabled):hover, .rp-source-opt input[type=radio]:not(:disabled):hover { border-color: #4389e9; } +input[type=checkbox]:focus-visible, .rp-source-opt input[type=radio]:focus-visible { + outline: 2px solid #4389e9; outline-offset: 3px; box-shadow: 0 0 0 4px #e9f2ff; +} +input[type=checkbox]:disabled, .rp-source-opt input[type=radio]:disabled { opacity: .45; cursor: not-allowed; } +@media (forced-colors: active) { + input[type=checkbox], .rp-source-opt input[type=radio] { appearance: auto; background: none; accent-color: auto; } +} + +button.btn { + background: var(--text); color: #fff; border: 0; padding: 9px 18px; border-radius: 999px; cursor: pointer; + font-size: 13px; transition: background-color .2s, transform .2s var(--ease), box-shadow .2s; +} +button.btn:hover { background: var(--accent-dark); box-shadow: 0 6px 16px rgba(51, 57, 59, .16); } +button.btn:active { transform: translateY(1px); } +button.btn.ghost { background: #fff; color: var(--text); box-shadow: inset 0 0 0 1px #e7e8e4; } +button.btn.ghost:hover { background: var(--field); box-shadow: inset 0 0 0 1px #dcddd9; } +button.btn.danger { background: #fff; color: var(--danger); box-shadow: inset 0 0 0 1px #f0dfe2; } +button.btn.danger:hover { background: var(--rose-soft); } +button.btn:disabled { opacity: .45; cursor: default; box-shadow: none; } +.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; } +.toolbar input[type=search] { max-width: 320px; } +.toolbar select { max-width: 280px; } +.spacer { flex: 1; } +.muted { color: var(--muted); } +.right { text-align: right; white-space: nowrap; } +.pill { + display: inline-block; white-space: nowrap; font-size: 11px; padding: 3px 10px; border-radius: 999px; + background: #e6f6ee; color: var(--mint-text); +} +.pill.off { background: var(--rose-soft); color: var(--rose-text); } +.pill.wait { background: #fbf3d9; color: var(--warn); } +.pill.sky { background: var(--sky-soft); color: var(--sky-text); } +.pill.grey { background: #eeefed; color: var(--muted); } +.dir-row td { background: #fafaf7; font-weight: 500; } +.note { margin: 0 0 14px; color: var(--muted); font-size: 12px; line-height: 1.55; } +.warn { color: var(--warn); font-size: 12px; } +h3.sub { font-weight: 500; font-size: 15px; margin: 2px 0 12px; } +.pager { display: flex; gap: 10px; align-items: center; justify-content: flex-end; margin-top: 14px; } +.num { width: 110px; } .sortnum { width: 70px; } +.toast { + position: fixed; bottom: 22px; right: 22px; background: var(--text); color: #fff; padding: 12px 18px; + border-radius: 16px; opacity: 0; transition: .25s; pointer-events: none; z-index: 100; font-size: 13px; +} +.toast.show { opacity: 1; } +.toast.bad { background: var(--danger); } + +dialog { border: 0; border-radius: 28px; padding: 0; max-width: 580px; width: 92%; box-shadow: 0 30px 80px rgba(51, 57, 59, .18); color: var(--text); } +dialog.wide { max-width: 940px; } +dialog::backdrop { background: rgba(240, 230, 231, .55); backdrop-filter: blur(6px); } +.dlg-head { padding: 22px 26px 8px; font-family: var(--font-display); font-weight: 500; font-size: 17px; letter-spacing: -0.02em; } +.dlg-body { padding: 14px 26px; max-height: 70vh; overflow: auto; } +.dlg-foot { padding: 16px 26px 22px; display: flex; gap: 10px; justify-content: flex-end; } +.field { margin-bottom: 14px; } +.field span { display: block; margin-bottom: 6px; color: var(--muted); font-size: 12px; } +.row2 { display: flex; gap: 12px; } .row2 > * { flex: 1; } +.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; max-width: 900px; } +.form-grid .wide { grid-column: 1 / -1; } +.img-field { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } +.img-preview { width: 96px; height: 72px; object-fit: cover; border-radius: 14px; background: var(--field); } +.img-field input[type=file] { width: auto; flex: 1; } +.file-link { font-size: 13px; word-break: break-all; } +.rv-text { white-space: pre-line; max-height: 4.6em; overflow: hidden; } +.logo-cell img { max-width: 90px; max-height: 40px; object-fit: contain; } +.doc-photo { width: 44px; height: 52px; object-fit: cover; border-radius: 14px; background: var(--field); display: block; } +.checklist { border: 1px solid #ebebe7; border-radius: var(--r-field); overflow: hidden; } +.checklist input[type=search] { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; } +.checklist-box { max-height: 340px; overflow: auto; padding: 6px 12px; } +.checklist-group { font-weight: 500; margin: 12px 0 4px; display: flex; align-items: center; gap: 8px; } +.checklist label { display: flex; gap: 8px; align-items: flex-start; padding: 3px 0 3px 16px; cursor: pointer; } +.checklist label input { width: auto; margin-top: 3px; } +.checklist .count { font-weight: 400; color: var(--muted); font-size: 11px; } +.rte { border: 1px solid #ebebe7; border-radius: var(--r-field); background: #fff; overflow: hidden; } +.rte-bar { display: flex; flex-wrap: wrap; gap: 4px; padding: 8px; border-bottom: 1px solid var(--line); background: #fbfbfa; position: sticky; top: -14px; z-index: 1; } +.rte-bar button { font-size: 12px; padding: 5px 10px; border: 0; border-radius: 10px; background: #fff; box-shadow: inset 0 0 0 1px #ebebe7; cursor: pointer; } +.rte-bar button:hover { box-shadow: inset 0 0 0 1px var(--text); } +.rte-bar .sep { width: 1px; background: var(--line); margin: 2px 4px; } +.rte-area { min-height: 320px; padding: 14px 16px; outline: none; line-height: 1.65; font-family: 'Segoe UI', system-ui, sans-serif; } +.rte-area:focus { box-shadow: inset 0 0 0 2px rgba(141, 203, 233, .5); } +.rte-area h2 { font-size: 19px; margin: 18px 0 8px; } .rte-area h3 { font-size: 16px; margin: 14px 0 6px; } +.rte-area p { margin: 0 0 10px; } .rte-area img { max-width: 100%; height: auto; border-radius: 12px; } +.rte-area a { color: var(--sky-text); } +.rte-src { display: block; width: 100%; min-height: 320px; border: 0; border-radius: 0; font: 12px/1.5 ui-monospace, Consolas, monospace; } + +/* ---------- главный экран ---------- */ +.dash { display: grid; grid-template-columns: minmax(0, 40fr) minmax(0, 60fr); gap: 10px 26px; align-items: start; } +.dash__heart { position: relative; min-height: 640px; } +.dash__right { display: flex; flex-direction: column; gap: 18px; min-width: 0; } + +.summary { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; padding: 38px 6px 12px; flex-wrap: wrap; } +.summary__title { + margin: 0; font-family: var(--font-display); font-weight: 500; font-size: clamp(46px, 5.4vw, 78px); line-height: .94; letter-spacing: -0.065em; +} +.summary__stats { display: flex; gap: clamp(18px, 3vw, 40px); padding-bottom: 14px; } +.stat { background: none; border: 0; padding: 0; text-align: center; cursor: pointer; } +.stat b { display: block; font-family: var(--font-display); font-weight: 500; font-size: clamp(22px, 2.1vw, 30px); letter-spacing: -0.04em; line-height: 1.1; } +.stat span { display: block; color: var(--muted); font-size: 11px; margin-top: 4px; } +.stat:hover b { color: #000; } + +.heart { position: absolute; inset: 0 0 0 0; } +.heart__img { position: absolute; left: 4%; top: 7%; width: 88%; max-width: 560px; height: auto; object-fit: contain; + filter: drop-shadow(0 30px 40px rgba(150, 40, 50, .18)); } +.heart__missing { + position: absolute; left: 6%; top: 12%; width: 80%; aspect-ratio: 5 / 6; border-radius: 48% 52% 46% 54% / 40% 42% 58% 60%; + border: 1.5px dashed #dcc4c8; background: radial-gradient(circle at 45% 40%, rgba(255,255,255,.7), rgba(255,255,255,.15) 70%); + display: grid; place-items: center; text-align: center; padding: 16%; color: var(--muted); font-size: 12px; line-height: 1.5; +} +.heart__missing > div { max-width: 52%; margin: 0 22% 8% 0; } +.heart__missing b { display: block; color: var(--text); font-weight: 500; font-size: 14px; margin-bottom: 6px; } +.heart__missing code { font-size: 11px; background: #fff; padding: 1px 6px; border-radius: 6px; } +.marker { + position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px; border-radius: 50%; + background: #fff; box-shadow: 0 0 0 7px rgba(255, 255, 255, .38), 0 2px 8px rgba(0,0,0,.12); z-index: 2; +} +.marker::after { content: ''; position: absolute; inset: -7px; border-radius: 50%; border: 1px solid rgba(255,255,255,.7); animation: pulse 3.2s var(--ease) infinite; } +.marker__line { position: absolute; height: 1px; background: rgba(255,255,255,.75); transform-origin: 0 50%; z-index: 1; } +@keyframes pulse { 0% { transform: scale(.8); opacity: .9; } 70% { transform: scale(1.9); opacity: 0; } 100% { opacity: 0; } } +.vital { + position: absolute; z-index: 3; min-width: 124px; padding: 14px 18px 13px; border: 0; cursor: pointer; + background: rgba(255, 255, 255, .94); border-radius: 22px; box-shadow: var(--shadow); text-align: center; + transition: transform .3s var(--ease), box-shadow .3s; +} +.vital:hover { transform: translateY(-3px); box-shadow: var(--shadow-lift); } +.vital__label { display: block; color: var(--muted); font-size: 11px; } +.vital__value { display: block; font-family: var(--font-display); font-weight: 400; font-size: 24px; letter-spacing: -0.04em; line-height: 1.25; margin-top: 2px; } +.vital__unit { display: block; font-size: 11px; color: var(--text); } + +.trends { padding: 22px 26px 14px; } +.trends__head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; } +.trends__head h2 { margin: 0; font-weight: 500; font-size: 13px; } +.trends__demo { font-size: 11px; color: var(--warn); background: #fbf3d9; border-radius: 999px; padding: 2px 10px; } +.trends__legend { margin-left: auto; display: flex; gap: 14px; font-size: 11px; color: var(--muted); } +.trends__legend i { display: inline-block; width: 18px; height: 8px; border-radius: 4px; margin-right: 6px; vertical-align: middle; } +.chart { position: relative; margin-top: 10px; } +.chart svg { display: block; width: 100%; height: auto; overflow: visible; } +.chart__line { fill: none; stroke: #8b8f8d; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.chart__axis { font-size: 15px; fill: #a9ada9; font-family: var(--font); } +.chart__frame { fill: none; stroke: #e2e3e0; stroke-width: 1; stroke-dasharray: 3 4; } +.chart__label { font-size: 10px; fill: var(--muted); font-family: var(--font); } +.chart__hit { fill: transparent; cursor: crosshair; } +.chart__dot { fill: #fff; stroke: var(--text); stroke-width: 1.5; } +.chart__guide { stroke: #d9dad7; stroke-width: 1; stroke-dasharray: 2 3; } +.tooltip { + position: absolute; pointer-events: none; transform: translate(-50%, calc(-100% - 12px)); white-space: nowrap; + background: var(--card); border-radius: 14px; padding: 8px 12px; font-size: 11px; box-shadow: var(--shadow-lift); z-index: 5; +} +.tooltip b { display: block; font-weight: 500; font-size: 13px; } + +.leads-card { padding: 20px 22px 10px; overflow-x: auto; } +.leads-table th { padding-top: 4px; } +.leads-table td { font-size: 12.5px; padding: 12px; } +.leads-table tbody tr { cursor: pointer; } +.leads-table td small { display: block; font-size: 10px; color: var(--muted); } +.leads-table .strong { font-weight: 500; } +.leads-table .soft { color: var(--muted); } +.card-link { border: 0; background: none; padding: 0; color: var(--muted); font-size: 12px; cursor: pointer; margin-left: auto; } +.card-link:hover { color: var(--text); } +.lead-detail dt { color: var(--muted); font-size: 11px; margin-top: 10px; } +.lead-detail dd { margin: 2px 0 0; } +.lead-detail { margin: 0 0 14px; } + +/* ---------- шапка: дата у календаря, подзаголовок раздела ---------- */ +.round--date { width: auto; border-radius: 22px; padding: 0 16px 0 14px; gap: 10px; grid-auto-flow: column; } +.round--date svg { width: 19px; height: 19px; } +.cal-date { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.2; text-align: left; } +.cal-date b { font-weight: 500; font-size: 13px; white-space: nowrap; } +.cal-date small { color: var(--muted); font-size: 11px; } +.page-sub { margin: 2px 0 18px; color: var(--muted); font-size: 15px; } + +/* ---------- раздел «Врачи» ---------- */ +.dr-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 20px; align-items: start; } +.dr-card { padding: 18px 18px 8px; } +.dr-toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; } +.dr-search { position: relative; flex: 1 1 280px; max-width: 400px; } +.dr-search > span { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); display: grid; pointer-events: none; } +.dr-search svg { width: 17px; height: 17px; stroke: var(--muted); fill: none; stroke-width: 1.6; stroke-linecap: round; } +.dr-search input { height: 44px; padding-left: 40px; border-radius: 14px; } +#dr-filter { height: 44px; width: auto; min-width: 220px; border-radius: 14px; } +.dr-count { color: var(--muted); font-size: 14px; } +.dr-note { + display: flex; gap: 12px; align-items: center; margin-bottom: 10px; padding: 10px 12px 10px 14px; + background: #eef5fc; border-radius: 14px; color: #5b6b78; font-size: 12px; line-height: 1.5; +} +.dr-note p { margin: 0; flex: 1; } +.dr-note__icon { display: grid; flex-shrink: 0; } +.dr-note__icon svg { width: 20px; height: 20px; fill: #5b9cf0; stroke: #fff; stroke-width: 1.8; stroke-linecap: round; } +.dr-note__close { border: 0; background: none; width: 30px; height: 30px; border-radius: 10px; display: grid; place-items: center; cursor: pointer; flex-shrink: 0; } +.dr-note__close:hover { background: rgba(255, 255, 255, .8); } +.dr-note__close svg { width: 16px; height: 16px; stroke: var(--text); fill: none; stroke-width: 1.8; stroke-linecap: round; } +.dr-scroll { overflow-x: auto; } +.dr-table { min-width: 860px; } +.dr-table thead th { background: #f8f9fa; padding-top: 12px; padding-bottom: 12px; font-size: 12px; } +.dr-table thead th:first-child { border-radius: 12px 0 0 12px; } +.dr-table thead th:last-child { border-radius: 0 12px 12px 0; } +.dr-table th button { border: 0; background: none; padding: 0; font: inherit; color: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; } +.dr-table th button svg { width: 14px; height: 14px; stroke: var(--muted-2); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.dr-table th[aria-sort="ascending"] button, .dr-table th[aria-sort="descending"] button { color: var(--text); } +.dr-table th[aria-sort="ascending"] svg, .dr-table th[aria-sort="descending"] svg { stroke: var(--text); } +.dr-table td { padding: 10px; } +.dr-who { display: flex; gap: 14px; align-items: center; min-width: 240px; } +.dr-photo { width: 52px; height: 52px; border-radius: 14px; object-fit: cover; object-position: 50% 15%; background: var(--field); flex-shrink: 0; } +.dr-photo--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; font-weight: 500; } +.dr-name { border: 0; background: none; padding: 0; text-align: left; font-weight: 500; font-size: 14px; cursor: pointer; } +.dr-name:hover { color: #3a7bd5; } +.dr-pos { color: var(--muted); font-size: 12.5px; margin-top: 2px; } +.chip { display: inline-block; white-space: nowrap; padding: 6px 12px; border-radius: 10px; background: #f3f4f5; font-size: 12px; } +.dr-num { font-weight: 500; } +.dr-branches { font-size: 12px; color: var(--muted); display: inline-block; max-width: 200px; } +.pill.warn-pill { background: #fdf0e3; color: #d0772b; padding: 5px 12px; } +.pill.dot { padding: 5px 12px 5px 10px; } +.pill.dot::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; margin-right: 7px; vertical-align: 1px; } +.dr-actions { white-space: nowrap; text-align: right; } +.dr-actions > * { vertical-align: middle; } +.soft-btn { + display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 14px; border: 0; border-radius: 12px; + background: #edf4fd; color: #3a6fb7; font-size: 12.5px; cursor: pointer; transition: background-color .2s; +} +.soft-btn:hover { background: #e0ecfb; } +.soft-btn svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } +.icon-btn { + width: 38px; height: 38px; margin-left: 6px; border-radius: 50%; border: 1px solid #ebecea; background: #fff; cursor: pointer; + display: inline-grid; place-items: center; transition: background-color .2s, border-color .2s; +} +.icon-btn:hover { background: var(--field); border-color: #dcddd9; } +.icon-btn svg { width: 16px; height: 16px; stroke: var(--text); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.icon-btn svg circle { fill: var(--text); stroke: none; } +.dr-empty { padding: 28px 12px; text-align: center; } +.menu { position: absolute; z-index: 60; min-width: 230px; padding: 6px; background: var(--card); border-radius: 16px; box-shadow: var(--shadow-lift); } +.menu button { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 9px 12px; border-radius: 10px; cursor: pointer; font-size: 13px; } +.menu button:hover, .menu button:focus-visible { background: var(--field); outline: none; } +.menu button.danger { color: var(--danger); } + +.dr-side { display: flex; flex-direction: column; gap: 18px; position: sticky; top: 18px; } +.dr-side .card { margin-bottom: 0; } +.dr-quick { padding: 20px 18px; } +.dr-quick h2 { margin: 0 0 14px 4px; font-weight: 600; font-size: 16px; } +.quick { + display: flex; align-items: center; gap: 12px; width: 100%; height: 48px; padding: 0 16px; margin-top: 10px; + border: 1px solid #f0f1ef; border-radius: 14px; background: #fff; cursor: pointer; font-size: 14px; text-align: left; + transition: background-color .2s, box-shadow .2s, transform .2s var(--ease); +} +.quick:first-of-type { margin-top: 0; } +.quick:hover { background: var(--field); } +.quick svg { width: 20px; height: 20px; stroke: #3a6fb7; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; } +.quick.primary { border: 0; color: #fff; background: linear-gradient(100deg, #3f86f2, #6aa8f5); box-shadow: 0 10px 22px rgba(63, 134, 242, .28); } +.quick.primary:hover { box-shadow: 0 14px 26px rgba(63, 134, 242, .36); transform: translateY(-1px); } +.quick.primary svg { stroke: #fff; } +.dr-summary { padding: 20px 18px 18px; } +.dr-summary__head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; } +.dr-summary__head span { font-size: 14px; } +.dr-summary__head b { display: block; font-family: var(--font-display); font-weight: 500; font-size: 32px; letter-spacing: -0.04em; line-height: 1.1; } +.dr-summary__icon { width: 56px; height: 56px; border-radius: 50%; background: #eaf2fd; display: grid; place-items: center; } +.dr-summary__icon svg { width: 28px; height: 28px; fill: #5b9cf0; stroke: #5b9cf0; stroke-width: 1; } +.dr-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; } +.mini-stat { border: 0; border-radius: 14px; padding: 10px 10px 8px; text-align: left; cursor: pointer; transition: box-shadow .2s, transform .2s var(--ease); } +.mini-stat span { display: flex; align-items: center; gap: 5px; font-size: 11px; white-space: nowrap; } +.mini-stat i { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; } +.mini-stat b { display: block; font-family: var(--font-display); font-weight: 500; font-size: 22px; letter-spacing: -0.03em; margin-top: 4px; color: var(--text); } +.mini-stat:hover { transform: translateY(-1px); } +.mini-stat[aria-pressed="true"] { box-shadow: inset 0 0 0 1.5px currentColor; } +.mini-stat.ok { background: #eaf7f0; color: #2f7d5a; } .mini-stat.ok i { background: #3fb27f; } +.mini-stat.warn { background: #fdf2e6; color: #b76a24; } .mini-stat.warn i { background: #f0913a; } +.mini-stat.rose { background: #fdeef1; color: #b8566c; } .mini-stat.rose i { background: #e8708a; } + +.sp-dlg { max-width: 620px; } +.sp-row { display: flex; align-items: center; gap: 8px; padding: 10px 4px; border-bottom: 1px solid var(--line); } +.sp-row:last-child { border-bottom: 0; } +.sp-row > div { flex: 1; min-width: 0; } +.sp-row b { font-weight: 500; display: block; } +.sp-row small { color: var(--muted); font-size: 11.5px; } +.sp-row .icon-btn { margin-left: 0; } + +@media (max-width: 1280px) { + .dr-layout { grid-template-columns: minmax(0, 1fr); } + .dr-side { position: static; display: grid; grid-template-columns: 1fr 1fr; order: -1; } +} +@media (max-width: 760px) { + .dr-side { grid-template-columns: 1fr; } + .round--date .cal-date { display: none; } + .round--date { width: 44px; padding: 0; } +} + +/* ---------- «Слайды на главной» ---------- */ +.bn-hero { + position: relative; overflow: hidden; min-height: 260px; margin: 4px 0 -34px; padding: 34px 28px 64px; + border-radius: 30px 30px 0 0; + background: linear-gradient(100deg, rgba(246, 248, 252, 1) 0%, rgba(233, 241, 250, .9) 45%, rgba(214, 229, 246, .7) 100%); +} +.bn-hero.has-photo { background: url(assets/hall.webp) right 42% / 68% auto no-repeat, #f5f7fa; } +.bn-hero--services.has-photo { background: url(assets/lobby.webp) right 48% / 68% auto no-repeat, #f5f7fa; } +/* левая часть остаётся светлой: текст читается поверх фото */ +.bn-hero::before { + content: ""; position: absolute; inset: 0; pointer-events: none; + background: linear-gradient(90deg, rgba(245, 247, 250, 1) 0%, rgba(245, 247, 250, 1) 32%, rgba(245, 247, 250, .6) 40%, rgba(245, 247, 250, 0) 50%); +} +.bn-hero::after { + content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 70px; pointer-events: none; + background: linear-gradient(to bottom, rgba(247, 248, 250, 0), rgba(247, 248, 250, .9)); +} +.bn-hero > * { position: relative; z-index: 1; } +.bn-hero__text { max-width: 520px; } +.bn-hero h1 { margin: 0; font-family: var(--font-display); font-weight: 500; font-size: clamp(34px, 3.6vw, 52px); letter-spacing: -0.05em; line-height: 1.02; } +.bn-hero__sub { margin: 6px 0 14px; font-size: clamp(16px, 1.4vw, 20px); color: #4d5558; } +.bn-hero__note { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.55; } +.bn-add { + position: absolute; top: 0; right: 0; display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 26px 0 22px; + border: 0; border-radius: 0 0 0 22px; color: #fff; font-size: 14px; cursor: pointer; + background: linear-gradient(100deg, #3f86f2, #6aa8f5); box-shadow: 0 10px 22px rgba(63, 134, 242, .28); + transition: box-shadow .2s, filter .2s; +} +.bn-add:hover { box-shadow: 0 14px 26px rgba(63, 134, 242, .36); filter: brightness(1.04); } +.bn-add svg { width: 18px; height: 18px; stroke: #fff; fill: none; stroke-width: 1.8; stroke-linecap: round; } +.bn-card { position: relative; z-index: 2; padding: 18px 18px 10px; } +.bn-toolbar { display: flex; align-items: center; gap: 12px; margin: 0 4px 12px; flex-wrap: wrap; } +.bn-toolbar b { font-weight: 500; font-size: 15px; } +.bn-toolbar span { font-size: 12px; } +#bn-filter { width: auto; min-width: 220px; height: 42px; border-radius: 14px; } +.bn-table { min-width: 1000px; } +.bn-table thead th { font-size: 12px; } +.bn-table td { padding: 10px 8px; } +.bn-table tbody tr.dragging { opacity: .45; } +.bn-table tbody tr.drop td { box-shadow: inset 0 2px 0 var(--blue-deep); } +.grip { border: 0; background: none; width: 28px; height: 36px; border-radius: 8px; cursor: grab; display: grid; place-items: center; color: var(--muted); } +.grip:hover { background: var(--field); color: var(--text); } +.grip svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; } +.bn-thumb { width: 220px; aspect-ratio: 20 / 7; border-radius: 12px; overflow: hidden; background: var(--field); position: relative; } +.bn-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; } +.bn-thumb--made { color: #fff; padding: 10px 12px; display: flex; flex-direction: column; justify-content: center; } +.bn-thumb--made b { font-weight: 600; font-size: 13px; line-height: 1.15; max-width: 62%; } +.bn-thumb--made small { font-size: 10px; opacity: .85; margin-top: 4px; max-width: 62%; } +.bn-thumb__doc { position: absolute; right: 0; bottom: 0; width: 40% !important; height: 100% !important; object-fit: cover; object-position: 50% 20%; } +.bn-title b { font-weight: 500; font-size: 13.5px; display: block; max-width: 220px; } +.bn-small { font-size: 12.5px; } +.bn-link { max-width: 170px; word-break: break-all; } +.bn-dates { white-space: pre-line; } +.bn-sort { display: inline-grid; place-items: center; min-width: 40px; height: 34px; padding: 0 8px; border-radius: 10px; background: #f3f4f5; font-size: 13px; } +.pill.bn-pill { padding: 7px 14px; font-size: 12px; } +.bn-edit { + display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 16px; border-radius: 12px; border: 1px solid #e7e8e4; + background: #fff; cursor: pointer; font-size: 13px; transition: background-color .2s; +} +.bn-edit:hover { background: var(--field); } +.bn-edit svg { width: 16px; height: 16px; stroke: var(--text); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } + +@media (max-width: 900px) { + .bn-hero { padding: 26px 20px 60px; } + .bn-hero::before { background: linear-gradient(90deg, rgba(247, 248, 250, 1) 0%, rgba(247, 248, 250, .9) 55%, rgba(247, 248, 250, .4) 100%); } + .bn-thumb { width: 170px; } +} +@media (max-width: 760px) { + .bn-add { position: relative; border-radius: 999px; margin-top: 18px; } + .bn-hero { border-radius: 24px 24px 0 0; } +} + +/* ---------- «Услуги»: направления-аккордеон ---------- */ +.hero-subtabs .subtabs { margin: 18px 0 0; background: rgba(255, 255, 255, .75); } +.bn-hero--services { padding-bottom: 58px; } +/* общие шапки с фото у разделов с вкладками («Контент сайта», «Отзывы», «Настройки») */ +.bn-hero--group { padding-bottom: 58px; } +.bn-hero--lobby.has-photo { background: url(assets/lobby.webp) right 48% / 68% auto no-repeat, #f5f7fa; } +.hero-body > .card:first-child { position: relative; z-index: 2; } +.sv-card { padding: 18px 18px 14px; } +.sv-toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; } +.sv-toolbar select { height: 44px; width: auto; min-width: 190px; border-radius: 14px; } +.sv-count { color: var(--muted); font-size: 13px; } +.sv-count u { text-decoration-color: #c9ccc8; text-underline-offset: 3px; color: var(--text); } +.sv-toolbar .bn-edit svg { width: 17px; height: 17px; } +.sv-group { border: 1px solid #f0f1ef; border-radius: 20px; margin-bottom: 10px; background: #fcfcfc; transition: box-shadow .2s, background-color .2s; } +.sv-group.open { background: #fff; box-shadow: 0 8px 22px rgba(51, 57, 59, .05); padding-bottom: 8px; } +.sv-top { display: flex; align-items: center; gap: 6px; padding-right: 12px; } +.sv-head { flex: 1; display: flex; align-items: center; gap: 16px; padding: 10px 12px 10px 10px; border: 0; background: none; cursor: pointer; text-align: left; border-radius: 20px; min-width: 0; } +.sv-icon { width: 58px; height: 58px; border-radius: 16px; display: grid; place-items: center; flex-shrink: 0; } +.sv-icon svg { width: 28px; height: 28px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } +.sv-head__text { flex: 1; min-width: 0; } +.sv-head__text b { display: block; font-weight: 600; font-size: 16px; } +.sv-head__text small { color: var(--muted); font-size: 12.5px; } +.sv-chev { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; } +.sv-chev svg { width: 18px; height: 18px; stroke: var(--text); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: transform .25s var(--ease); } +.sv-group.open .sv-chev svg { transform: rotate(180deg); } +.sv-head:hover .sv-chev { background: var(--field); } +.sv-dir-edit { margin-left: 0; opacity: 0; transition: opacity .2s; } +.sv-group:hover .sv-dir-edit, .sv-dir-edit:focus-visible { opacity: 1; } +.sv-table { min-width: 900px; } +.sv-table thead th { background: #f8f9fa; padding-top: 10px; padding-bottom: 10px; font-size: 12px; } +.sv-table td { padding: 8px 8px; font-size: 13px; } +.sv-table .sv-name { font-weight: 500; } +.sv-path { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.sv-table .bn-edit { height: 36px; } +.sv-table .icon-btn { width: 36px; height: 36px; } +.sv-table tbody tr.dragging { opacity: .45; } +.sv-table tbody tr.drop td { box-shadow: inset 0 2px 0 var(--blue-deep); } +.sv-table, .sv-group .dr-scroll { margin: 0 8px; width: calc(100% - 16px); } + +@media (max-width: 760px) { + .sv-toolbar .dr-search { flex-basis: 100%; max-width: none; } + .sv-toolbar select { flex: 1 1 40%; min-width: 0; } + .sv-icon { width: 46px; height: 46px; } + .sv-dir-edit { opacity: 1; } +} + +.legal-row { display: flex; align-items: center; gap: 14px; padding: 12px 4px; border-bottom: 1px solid var(--line); } +.legal-row:last-child { border-bottom: 0; } +.legal-row > div { flex: 1; min-width: 0; } +.legal-row b { display: block; font-weight: 500; } +.legal-row small { color: var(--muted); font-size: 12px; } +.legal-row small a { color: var(--sky-text); } + +/* ---------- «Статистика» ---------- */ +.bn-hero--stats.has-photo { background: url(assets/hall.webp) right 40% / 62% auto no-repeat, #f5f7fa; } +.bn-hero--stats { padding-bottom: 28px; margin-bottom: 18px; border-radius: 30px; } +.bn-hero--stats::after { display: none; } +.st-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 22px; } +.st-presets { display: flex; gap: 4px; padding: 5px; background: rgba(255, 255, 255, .8); border-radius: 18px; backdrop-filter: blur(6px); } +.st-presets button { border: 0; background: none; padding: 9px 16px; border-radius: 14px; cursor: pointer; color: var(--muted); font-size: 13px; } +.st-presets button:hover { color: var(--text); } +.st-presets button[aria-pressed="true"] { background: linear-gradient(100deg, #3f86f2, #6aa8f5); color: #fff; box-shadow: 0 6px 16px rgba(63, 134, 242, .28); } +.st-dates { display: flex; align-items: center; gap: 8px; padding: 0 14px; height: 48px; background: rgba(255, 255, 255, .9); border-radius: 16px; } +.st-dates svg { width: 18px; height: 18px; stroke: var(--text); fill: none; stroke-width: 1.6; stroke-linecap: round; display: block; } +.st-dates input { border: 0; padding: 4px; width: 132px; background: transparent; } +#st-branch { width: auto; min-width: 200px; height: 48px; border-radius: 16px; margin-left: auto; background: rgba(255, 255, 255, .92); } +.st-kpis { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; margin-bottom: 14px; } +.kpi { display: flex; gap: 14px; align-items: flex-start; padding: 18px; margin: 0; } +.kpi__icon { width: 48px; height: 48px; border-radius: 14px; display: grid; place-items: center; flex-shrink: 0; } +.kpi__icon svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.kpi__label { display: block; font-size: 13px; } +.kpi__value { font-family: var(--font-display); font-weight: 500; font-size: 30px; letter-spacing: -0.04em; line-height: 1.2; margin-right: 8px; } +.kpi__delta { font-size: 12px; color: var(--muted); white-space: nowrap; } +.kpi__delta.up { color: #2f9a6a; } .kpi__delta.down { color: var(--danger); } +.kpi small { display: block; color: var(--muted); font-size: 11.5px; margin-top: 2px; } +.st-grid2 { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 14px; } +.st-grid3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; } +.st-card { margin-bottom: 14px; padding: 20px 22px; min-width: 0; } +.st-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; } +.st-head h2 { margin: 0; font-weight: 600; font-size: 16px; } +.st-head select { width: auto; height: 38px; border-radius: 12px; } +.st-chart svg { display: block; width: 100%; height: auto; } +.st-grid { stroke: #eef0f1; stroke-width: 1; } +.st-axis { font-size: 11px; fill: #a3a8ac; font-family: var(--font); } +.st-line { fill: none; stroke: #4f8ff0; stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; } +.st-guide { stroke: #c9d3dd; stroke-dasharray: 3 3; } +.st-dot { fill: #fff; stroke: #4f8ff0; stroke-width: 2.4; } +.st-donut { display: flex; align-items: center; gap: 22px; } +.st-donut svg { width: 170px; height: 170px; flex-shrink: 0; } +.st-donut__n { font-family: var(--font-display); font-weight: 500; font-size: 26px; fill: var(--text); } +.st-donut__u { font-size: 11px; fill: var(--muted); } +.st-legend { list-style: none; margin: 0; padding: 0; flex: 1; min-width: 0; } +.st-legend li { display: grid; grid-template-columns: 12px minmax(0, 1fr) 44px 36px; gap: 10px; align-items: center; padding: 6px 0; font-size: 13px; } +.st-legend i { width: 11px; height: 11px; border-radius: 50%; } +.st-legend span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.st-legend em, .st-bar em { font-style: normal; color: var(--muted); text-align: right; font-size: 12.5px; } +.st-legend b, .st-bar b { font-weight: 500; text-align: right; } +.st-bar { display: grid; grid-template-columns: minmax(90px, 1.1fr) minmax(60px, 1.3fr) 40px 32px; gap: 10px; align-items: center; padding: 8px 0; font-size: 13px; } +.st-bar span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.st-bar__track { height: 12px; border-radius: 6px; background: #eef1f4; overflow: hidden; } +.st-bar__track i { display: block; height: 100%; border-radius: 6px; background: linear-gradient(90deg, #4f8ff0, #8bbaf5); } +.st-top th { background: #f8f9fa; font-size: 12px; } +.st-top td { font-size: 13px; padding: 9px 10px; } +.st-empty { color: var(--muted); font-size: 13px; padding: 24px 0; text-align: center; margin: 0; } +.st-note { color: var(--muted); font-size: 11.5px; margin: 10px 0 0; line-height: 1.5; } +.st-footnote { margin: 4px 6px 20px; } +.st-tip { pointer-events: none; } + +@media (max-width: 1280px) { + .st-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .st-grid3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} +@media (max-width: 1024px) { + .st-grid2 { grid-template-columns: minmax(0, 1fr); } + #st-branch { margin-left: 0; } +} +@media (max-width: 760px) { + .st-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } + .kpi { flex-direction: column; gap: 8px; padding: 14px; } + .st-grid3 { grid-template-columns: minmax(0, 1fr); } + .st-donut { flex-direction: column; align-items: stretch; } + .st-donut svg { margin: 0 auto; } + .st-presets { overflow-x: auto; max-width: 100%; } + .st-dates input { width: 118px; } +} + +/* ---------- «Чек-апы» ---------- */ +.ck-card { padding: 18px 18px 10px; } +.ck-table { min-width: 860px; } +.ck-table thead th { background: #f8f9fa; padding-top: 12px; padding-bottom: 12px; font-size: 12px; } +.ck-table thead th:first-child { border-radius: 12px 0 0 12px; } .ck-table thead th:last-child { border-radius: 0 12px 12px 0; } +.ck-table td { padding: 8px 10px; font-size: 13.5px; } +.ck-prog { display: flex; align-items: center; gap: 16px; min-width: 300px; } +.ck-thumb { width: 112px; height: 62px; border-radius: 12px; object-fit: cover; object-position: 80% 70%; background: var(--field); flex-shrink: 0; display: block; } +.ck-thumb--empty { display: grid; place-items: center; color: var(--muted); font-size: 11px; } +.ck-price { font-weight: 500; display: block; } +.ck-old { font-size: 12px; } +#ck-sort, #ck-aud { height: 44px; width: auto; min-width: 180px; border-radius: 14px; } + +/* ---------- «Дашборд врачей»: пастель — голубой, мятный, персиковый, пудровый ---------- */ +#tab-doctors-dash { --dd-ink: #243a5e; --dd-soft: #7d8aa3; --dd-line: #e6ecf5; } +.dd-top { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin: 6px 0 16px; } +.dd-title { margin: 0; font-family: var(--font-display); font-weight: 500; font-size: 30px; letter-spacing: -0.04em; color: var(--dd-ink); } +.dd-top [data-subtabs-slot] .subtabs { margin: 0; } +#dd-spec { width: auto; min-width: 230px; height: 44px; border-radius: 16px; border-color: var(--dd-line); background: rgba(255, 255, 255, .9); } +.dd-board { + display: grid; grid-template-columns: minmax(0, 40fr) minmax(0, 60fr); gap: 0; margin-bottom: 18px; + border-radius: 34px; overflow: hidden; box-shadow: 0 18px 50px rgba(64, 98, 150, .08); + background: linear-gradient(135deg, #f6f9fe 0%, #eef4fc 55%, #fbf2f2 100%); + border: 1px solid rgba(255, 255, 255, .9); +} +.dd-intro { padding: 34px 34px 30px; border-right: 1px solid var(--dd-line); } +.dd-intro h2 { margin: 0; font-family: var(--font-display); font-weight: 500; font-size: clamp(34px, 3.4vw, 50px); letter-spacing: -0.05em; line-height: 1.02; color: var(--dd-ink); } +.dd-desc { margin: 16px 0 28px; max-width: 420px; color: var(--dd-soft); font-size: 13.5px; line-height: 1.6; } +.dd-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; } +.dd-stat { border: 0; background: none; padding: 0; text-align: left; cursor: pointer; color: var(--dd-ink); } +.dd-stat__icon { width: 50px; height: 50px; border-radius: 50%; display: grid; place-items: center; margin-bottom: 14px; box-shadow: 0 8px 18px rgba(64, 98, 150, .08); transition: transform .25s var(--ease); } +.dd-stat:hover .dd-stat__icon { transform: translateY(-2px); } +.dd-stat__icon svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } +.dd-stat b { font-family: var(--font-display); font-weight: 500; font-size: 26px; letter-spacing: -0.04em; } +.dd-stat__of { font-size: 11px; color: var(--dd-soft); margin-left: 4px; } +.dd-stat__label { display: block; font-size: 12px; color: var(--dd-soft); margin-top: 4px; line-height: 1.35; } +.dd-overview { padding: 30px 30px 22px; min-width: 0; background: rgba(255, 255, 255, .55); } +.dd-overview__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; } +.dd-overview h2 { margin: 0 0 10px; font-family: var(--font-display); font-weight: 500; font-size: 24px; letter-spacing: -0.03em; color: var(--dd-ink); } +.dd-overview #dd-spec { height: 34px; min-width: 0; padding: 4px 30px 4px 12px; font-size: 12px; border-radius: 12px; } +.dd-total { padding: 10px 16px; border-radius: 18px; background: rgba(255, 255, 255, .85); box-shadow: 0 6px 18px rgba(64, 98, 150, .06); } +.dd-total b { display: block; font-family: var(--font-display); font-weight: 500; font-size: 24px; letter-spacing: -0.03em; color: var(--dd-ink); line-height: 1.15; } +.dd-total span { font-size: 11.5px; color: var(--dd-soft); } +.dd-chart { position: relative; margin-top: 8px; } +.dd-chart svg { display: block; width: 100%; height: auto; } +.dd-grid { stroke: #e7edf5; stroke-width: 1; } +.dd-axis { font-size: 10.5px; fill: #a0abbf; font-family: var(--font); } +.dd-cat { font-size: 11.5px; fill: #3d4f6e; font-family: var(--font); } +.dd-val { font-size: 11px; fill: #8f9bb2; font-family: var(--font); } +.dd-guide { stroke: #c4d0e1; stroke-dasharray: 3 3; } +.dd-hill { transition: opacity .2s; } +.dd-chart svg:hover .dd-hill { opacity: .55; } +.dd-chart svg:hover .dd-hill.on { opacity: 1; } +.dd-tip { transform: translate(-50%, calc(-100% - 10px)); } +.dd-tip__row { display: block; font-size: 11px; color: var(--muted); } +.dd-attention { display: grid; grid-template-columns: auto repeat(3, minmax(0, 1fr)); gap: 10px; align-items: center; margin-top: 6px; } +.dd-attention > div { display: contents; } +.dd-attention h3 { margin: 0 6px 0 0; font-size: 13px; font-weight: 600; color: var(--dd-ink); } +.dd-att { display: grid; grid-template-columns: 34px 1fr; grid-template-rows: auto auto; column-gap: 10px; align-items: center; width: 100%; text-align: left; + border: 0; background: rgba(255, 255, 255, .85); border-radius: 18px; padding: 10px 12px; margin-bottom: 0; cursor: pointer; + box-shadow: 0 6px 16px rgba(64, 98, 150, .06); transition: transform .2s var(--ease), box-shadow .2s; } +.dd-att:hover { transform: translateY(-1px); box-shadow: 0 10px 22px rgba(64, 98, 150, .1); } +.dd-att__icon { grid-row: span 2; width: 34px; height: 34px; border-radius: 50%; background: #fdeee2; color: #c9803f; display: grid; place-items: center; } +.dd-att__icon svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } +.dd-att b { font-family: var(--font-display); font-weight: 500; font-size: 20px; color: var(--dd-ink); line-height: 1.1; } +.dd-att span:last-child { font-size: 11.5px; color: var(--dd-soft); line-height: 1.3; } +.dd-details-card { border-radius: 30px; padding: 26px 28px 18px; } +.dd-h2 { margin: 0 0 12px; font-family: var(--font-display); font-weight: 500; font-size: 22px; letter-spacing: -0.03em; color: #243a5e; } +.dd-row { display: grid; grid-template-columns: minmax(170px, 1.35fr) minmax(120px, 1.2fr) minmax(170px, 1.5fr) 44px 130px; gap: 16px; align-items: center; + width: 100%; padding: 13px 6px; border: 0; border-bottom: 1px solid #eef2f7; background: none; text-align: left; font-size: 13px; color: #243a5e; } +button.dd-row { cursor: pointer; border-radius: 12px; } +button.dd-row:hover { background: #f7f9fd; } +.dd-row:last-child { border-bottom: 0; } +.dd-row__label { font-weight: 500; } +.dd-row__bar { height: 8px; border-radius: 4px; background: #edf2f8; overflow: hidden; } +.dd-row__bar i { display: block; height: 100%; border-radius: 4px; } +.dd-row__text { color: #7d8aa3; } +.dd-row__pct { text-align: right; color: #7d8aa3; font-variant-numeric: tabular-nums; } +.dd-row__st { justify-self: start; font-size: 11.5px; padding: 4px 12px; border-radius: 999px; white-space: nowrap; } +.dd-row.ok .dd-row__bar i { background: linear-gradient(90deg, #8fd8b8, #6cc59f); } +.dd-row.ok .dd-row__st { background: #e3f5ec; color: #2f8a62; } +.dd-row.warn .dd-row__bar i { background: linear-gradient(90deg, #f7c9a0, #f0ad78); } +.dd-row.warn .dd-row__st { background: #fdeee1; color: #b86f32; } +.dd-row.empty .dd-row__st { background: #eef2f8; color: #8f9bb2; } +.dd-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 16px 0 24px; padding: 12px; border-radius: 26px; + background: rgba(255, 255, 255, .7); backdrop-filter: blur(6px); box-shadow: 0 10px 30px rgba(64, 98, 150, .06); } +.dd-act { flex: 1 1 150px; display: inline-flex; align-items: center; justify-content: center; gap: 10px; height: 46px; border: 0; border-radius: 16px; + background: #fff; color: #243a5e; font-size: 13px; cursor: pointer; transition: background-color .2s, transform .2s var(--ease); } +.dd-act:hover { background: #eef4fc; transform: translateY(-1px); } +.dd-act svg { width: 18px; height: 18px; stroke: #4a7fc4; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; } + +@media (max-width: 1280px) { + .dd-board { grid-template-columns: minmax(0, 1fr); } + .dd-intro { border-right: 0; border-bottom: 1px solid var(--dd-line); } +} +@media (max-width: 900px) { + .dd-attention { grid-template-columns: minmax(0, 1fr); } + .dd-row { grid-template-columns: minmax(0, 1fr) 44px; gap: 6px 12px; } + .dd-row__bar { grid-column: 1 / -1; grid-row: 2; } + .dd-row__text { grid-column: 1; grid-row: 3; } + .dd-row__st { grid-column: 2; grid-row: 3; justify-self: end; } +} +@media (max-width: 760px) { + .dd-intro, .dd-overview { padding: 22px 18px; } + .dd-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; } + .dd-board { border-radius: 26px; } + .dd-details-card { padding: 20px 16px; } +} + +/* ---------- «Отчёты» ---------- */ +#tab-reports { --rp-ink: #1f3558; --rp-soft: #7a879d; --rp-line: #e4eaf3; --rp-blue: #3f8df0; } +.rp-head { position: relative; margin: 8px 0 18px; } +.rp-head h1 { margin: 0; font-family: var(--font-display); font-weight: 600; font-size: 38px; letter-spacing: -0.04em; color: var(--rp-ink); } +#tab-reports { position: relative; isolation: isolate; } +/* атмосферные ленты: правый верхний угол, за карточками, не ловят клики */ +.rp-waves { position: absolute; top: -84px; right: -22px; width: min(820px, 64%); height: 420px; z-index: -1; pointer-events: none; } +#tab-reports > :not(.rp-waves) { position: relative; z-index: 1; } +.rp-tabs { display: inline-flex; gap: 4px; padding: 5px; margin-bottom: 18px; background: #fff; border-radius: 18px; box-shadow: var(--shadow); } +.rp-tabs button { border: 0; background: none; padding: 10px 22px; border-radius: 14px; cursor: pointer; font-size: 13.5px; color: var(--rp-ink); } +.rp-tabs button:hover { background: #f4f7fb; } +.rp-tabs button[aria-selected="true"] { background: #e3eefc; color: #2f6fd0; } +/* клиника отчётов: у DANA и «Очевидно» свои звонки, отчёты и дашборд */ +.rp-head:has(.rp-clinic) { display: flex; align-items: center; flex-wrap: wrap; gap: 12px 18px; } +.rp-clinic { display: inline-flex; gap: 4px; padding: 4px; background: #fff; border-radius: 14px; box-shadow: var(--shadow); } +.rp-clinic button { border: 0; background: none; padding: 8px 16px; border-radius: 10px; cursor: pointer; font-size: 13.5px; color: var(--rp-ink); } +.rp-clinic button:hover { background: #f4f7fb; } +.rp-clinic button[aria-pressed="true"] { background: #e3eefc; color: #2f6fd0; font-weight: 600; } +.rp-share { width: min(560px, calc(100vw - 32px)); border: 0; border-radius: 20px; padding: 24px; box-shadow: var(--shadow); color: var(--rp-ink); } +.rp-share::backdrop { background: rgba(20, 30, 50, .35); } +.rp-share h2 { margin: 0 0 8px; font-size: 20px; } +.rp-share__state { font-weight: 600; } +.rp-share__out { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0; } +.rp-share__url { flex: 1 1 260px; min-width: 0; padding: 9px 12px; border: 1px solid #d6deea; border-radius: 10px; font: inherit; font-size: 13px; } +.rp-share__out p { flex-basis: 100%; margin: 0; } +.rp-share__actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 16px; } +.rp-grid { display: grid; grid-template-columns: minmax(0, 36fr) minmax(0, 64fr); gap: 18px; align-items: stretch; margin-bottom: 18px; } +.rp-grid .card { margin: 0; border-radius: 26px; } +.rp-upload { display: flex; flex-direction: column; gap: 12px; padding: 22px; align-self: start; } +.rp-drop { min-height: 230px; border: 1.5px dashed #b9d0f0; border-radius: 20px; background: linear-gradient(180deg, #f8fbff, #f3f7fd); + display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 22px; text-align: center; cursor: pointer; + color: var(--rp-ink); transition: border-color .2s, background-color .2s; } +.rp-drop:hover, .rp-drop.over { border-color: var(--rp-blue); background: #eef5ff; } +.rp-drop__icon svg { width: 44px; height: 44px; stroke: var(--rp-blue); fill: none; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; } +.rp-drop b { font-size: 17px; font-weight: 600; margin-top: 6px; } +.rp-drop small { color: var(--rp-soft); font-size: 12.5px; } +.rp-drop__name { display: inline-flex; align-items: center; gap: 12px; font-size: 16px; word-break: break-all; } +.rp-ok { width: 24px; height: 24px; border-radius: 50%; background: #3fbd82; display: inline-grid; place-items: center; flex-shrink: 0; } +.rp-ok svg { width: 15px; height: 15px; stroke: #fff; fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; } +.rp-link { align-self: center; display: inline-flex; align-items: center; gap: 8px; border: 0; background: none; color: #2f7be0; cursor: pointer; font-size: 13.5px; padding: 4px 8px; border-radius: 10px; } +.rp-link:hover { background: #eef5ff; } +.rp-link svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.rp-partners { display: flex; flex-direction: column; gap: 10px; width: 100%; } +.rp-partner { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border: 1px solid #e4ecf7; border-radius: 12px; background: #fafcff; } +.rp-partner__head { display: flex; flex-direction: column; gap: 2px; } +.rp-partner__head b { font-size: 13.5px; font-weight: 600; } +.rp-partner__head small { color: var(--rp-soft); font-size: 12px; line-height: 1.4; } +.rp-partner__files { display: flex; flex-direction: column; gap: 4px; } +.rp-partner__file { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; } +.rp-partner__file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.rp-partner__file button { border: 0; background: none; color: var(--rp-soft); cursor: pointer; font-size: 16px; line-height: 1; padding: 2px 6px; } +.rp-partner .rp-link { align-self: flex-start; padding-left: 0; } +.rp-hint { margin: 0; color: var(--rp-soft); font-size: 12px; line-height: 1.5; text-align: center; } +.rp-start { display: flex; align-items: center; justify-content: center; gap: 10px; height: 54px; border: 0; border-radius: 18px; cursor: pointer; color: #fff; font-size: 15px; + background: linear-gradient(95deg, #2f7fe8 0%, #4aa0f3 60%, #8fb9f0 82%, #f2b58e 100%); box-shadow: 0 14px 26px rgba(47, 127, 232, .28); transition: transform .2s var(--ease), box-shadow .2s, opacity .2s; } +.rp-start:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 18px 32px rgba(47, 127, 232, .34); } +.rp-start:disabled { opacity: .45; cursor: default; box-shadow: none; } +.rp-start svg { width: 16px; height: 16px; fill: #fff; } +.rp-progress { padding: 24px 26px 22px; display: flex; flex-direction: column; gap: 16px; + background: linear-gradient(135deg, #ffffff 0%, #fbfcff 55%, #fdf5f7 100%); } +.rp-progress__head { display: flex; justify-content: space-between; gap: 14px; } +.rp-progress h2 { margin: 0; font-size: 19px; font-weight: 600; color: var(--rp-ink); } +.rp-progress__head p { margin: 4px 0 0; color: var(--rp-soft); font-size: 13px; } +.rp-clock { display: inline-flex; align-items: center; gap: 8px; color: var(--rp-soft); font-size: 14px; white-space: nowrap; height: 24px; } +.rp-clock svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; } +.rp-progress__body { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 26px; align-items: center; } +.rp-ring { position: relative; width: 190px; height: 190px; } +.rp-ring svg { width: 100%; height: 100%; } +#rp-ring-bar { transition: stroke-dasharray .45s var(--ease); } +.rp-ring__text { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; } +.rp-ring__text b { font-family: var(--font-display); font-weight: 600; font-size: 34px; letter-spacing: -0.03em; color: var(--rp-ink); } +.rp-ring__text span { font-size: 13px; color: var(--rp-soft); } +.rp-stages { list-style: none; margin: 0; padding: 0 0 0 22px; border-left: 1px solid var(--rp-line); display: flex; flex-direction: column; gap: 14px; } +.rp-stage { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; gap: 14px; align-items: start; } +.rp-stage b { display: block; font-weight: 500; font-size: 14px; color: var(--rp-ink); } +.rp-stage small { display: block; font-size: 12px; color: var(--rp-soft); min-height: 1em; } +.rp-stage time { font-size: 13px; color: var(--rp-soft); font-variant-numeric: tabular-nums; } +.rp-stage__mark { width: 22px; height: 22px; border-radius: 50%; border: 2px solid #cfd6e0; display: grid; place-items: center; margin-top: 1px; background: #fff; } +.rp-stage.done .rp-stage__mark { background: #3fbd82; border-color: #3fbd82; } +.rp-stage.done .rp-stage__mark svg { width: 14px; height: 14px; stroke: #fff; fill: none; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; } +.rp-stage.now .rp-stage__mark { border: 5px solid var(--rp-blue); animation: rp-pulse 1.4s ease-in-out infinite; } +.rp-stage.stop .rp-stage__mark { border-color: var(--rose); } +.rp-stage.wait b { color: #5d6b82; } +@keyframes rp-pulse { 50% { box-shadow: 0 0 0 6px rgba(63, 141, 240, .15); } } +.rp-result { display: flex; flex-direction: column; gap: 12px; } +.rp-download { display: inline-flex; align-items: center; gap: 10px; align-self: flex-start; padding: 11px 18px; border-radius: 14px; text-decoration: none; + background: #e8f6ee; color: #227a52; font-size: 13.5px; } +.rp-download:hover { background: #dcf1e5; } +.rp-download svg, .rp-btn-ghost svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.rp-btn-ghost { display: inline-flex; align-items: center; gap: 8px; align-self: flex-start; height: 40px; padding: 0 16px; border-radius: 12px; border: 1px solid #e3e7ee; background: #fff; cursor: pointer; font-size: 13px; color: var(--rp-ink); } +.rp-btn-ghost:hover { background: #f6f8fb; } +.rp-sum { font-size: 12.5px; } +.rp-sum th { font-size: 11.5px; background: #f7f9fc; padding: 8px 10px; } +.rp-sum td { padding: 7px 10px; } +.rp-sum__total td { color: var(--rp-soft); } +.rp-note { margin: 0; font-size: 13px; color: var(--rp-soft); line-height: 1.5; } +.rp-control { margin: 0; font-size: 12.5px; padding: 8px 12px; border-radius: 12px; background: #f4f6fa; color: var(--rp-soft); } +.rp-control.ok { background: #e8f6ee; color: #227a52; } +.rp-control.bad { background: var(--rose-soft); color: var(--rose-text); } +.rp-sum__total td { font-weight: 500; } +.rp-info { display: flex; gap: 14px; align-items: center; margin-top: auto; padding: 14px 18px; border-radius: 18px; background: linear-gradient(90deg, #fdeef3, #fbeff6); } +.rp-info svg { width: 28px; height: 28px; stroke: #7065d8; fill: none; stroke-width: 1.5; stroke-linecap: round; display: block; } +.rp-info p { margin: 0; font-size: 12.5px; color: #6f6680; line-height: 1.5; } +.rp-info b { display: block; font-weight: 500; font-size: 13.5px; color: var(--rp-ink); } +.rp-recent { border-radius: 26px; padding: 20px 22px 12px; } +.rp-recent__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; } +.rp-recent h2 { margin: 0; font-size: 18px; font-weight: 600; color: var(--rp-ink); } +.rp-table { min-width: 760px; } +.rp-table thead th { background: #f4f7fb; font-size: 12px; padding-top: 11px; padding-bottom: 11px; } +.rp-table thead th:first-child { border-radius: 12px 0 0 12px; } .rp-table thead th:last-child { border-radius: 0 12px 12px 0; } +.rp-table td { font-size: 13.5px; padding: 10px 12px; } +.rp-file { color: var(--rp-soft); } +.rp-file svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.5; vertical-align: -3px; margin-right: 8px; } +.rp-table .bn-edit { height: 36px; text-decoration: none; color: var(--rp-ink); } +.rp-table .bn-edit svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.8; } +.rp-table .icon-btn svg circle { fill: var(--text); } + +@media (max-width: 1100px) { + .rp-grid { grid-template-columns: minmax(0, 1fr); } +} +@media (max-width: 640px) { + .rp-progress__body { grid-template-columns: minmax(0, 1fr); justify-items: center; } + .rp-stages { width: 100%; border-left: 0; padding-left: 0; } + .rp-waves { width: 100%; top: -70px; right: -12px; opacity: .7; } + .rp-tabs { display: flex; overflow-x: auto; } + .rp-tabs button { padding: 10px 14px; white-space: nowrap; } + .rp-head h1 { font-size: 30px; } +} + +/* нижнее меню телефона: 9 разделов и выход должны помещаться в ширину 360 px */ +@media (max-width: 460px) { + #app .side { left: 6px; right: 6px; padding: 0 6px; } + #app .side__nav { gap: 0; min-width: 0; } + #app .side .side__btn { width: 32px; height: 40px; border-radius: 12px; flex-shrink: 1; } + #app .side .side__btn svg { width: 19px; height: 19px; } + #app .side__foot { margin-left: 0; } +} + +/* ---------- «Звонки АТС» ---------- */ +.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } +.px-card { border-radius: 26px; padding: 22px 24px 16px; } +.px-card h2 { margin: 0 0 10px; font-size: 18px; font-weight: 600; color: #1f3558; } +.px-head { display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 14px; } +.px-head h2 { margin: 0; } +.px-bad { color: var(--rose-text) !important; } +/* карточка АТС */ +.px-ats { display: grid; grid-template-columns: auto 1fr auto; gap: 6px 20px; align-items: center; padding: 20px 24px; } +.px-ats__icon { width: 72px; height: 72px; border-radius: 22px; display: grid; place-items: center; background: #e3f6ec; color: #1f9d63; } +.px-ats__icon svg { width: 32px; height: 32px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.px-ats__body h2 { margin: 0 0 6px; font-size: 19px; } +.px-conn { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: 14px; color: #33475f; } +.px-dot { width: 9px; height: 9px; border-radius: 50%; background: #22b07d; box-shadow: 0 0 0 3px rgba(34, 176, 125, .16); } +.px-conn.off .px-dot { background: #c9ced6; box-shadow: none; } +.px-copy { border: 0; background: none; padding: 4px; border-radius: 8px; color: #6f7f94; cursor: pointer; display: grid; } +.px-copy:hover { background: #eef3fa; color: #1f3558; } +.px-copy svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.px-state { margin: 0; font-size: 12.5px; line-height: 1.55; max-width: 780px; } +.px-ats #px-check { align-self: start; } +.px-ats .px-setup { grid-column: 1 / -1; margin: 10px 0 0; } +.px-setup { background: #f4f8fe; border-radius: 16px; padding: 14px 18px; font-size: 13px; line-height: 1.55; } +.px-setup p { margin: 0 0 8px; } +.px-setup pre { margin: 0 0 8px; padding: 10px 14px; background: #fff; border-radius: 12px; font-size: 12.5px; overflow-x: auto; } +/* загрузка звонков */ +.px-syncbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 14px 22px; font-size: 13px; color: #33475f; } +.px-note { font-size: 12px; } +.px-btn { height: 44px; padding: 0 26px; border: 0; border-radius: 14px; color: #fff; font-size: 14px; cursor: pointer; + background: linear-gradient(100deg, #2f76f0, #4f95f6); box-shadow: 0 8px 18px rgba(47, 118, 240, .25); transition: filter .2s, box-shadow .2s; } +.px-btn:hover { filter: brightness(1.05); box-shadow: 0 10px 22px rgba(47, 118, 240, .32); } +.px-btn:disabled { opacity: .55; cursor: default; } +.px-show { height: 44px; padding: 0 20px; border: 1px solid #e3eaf5; border-radius: 14px; background: #f4f8fe; color: #2f76f0; font-size: 14px; cursor: pointer; } +.px-show:hover { background: #e9f1fd; } +.px-period { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } +/* поля периода и календарь */ +.px-range { position: relative; display: flex; align-items: center; gap: 8px; } +.px-dash { color: #8b98aa; } +.px-date { display: inline-flex; align-items: center; gap: 10px; height: 44px; min-width: 150px; padding: 0 14px; border: 1px solid #e3e8ef; + border-radius: 14px; background: #fff; color: #1f3558; font-size: 14px; cursor: pointer; transition: border-color .2s, box-shadow .2s; } +.px-date:hover, .px-range.open .px-date.active { border-color: #b9d0f4; box-shadow: 0 0 0 3px rgba(63, 134, 242, .1); } +.px-date.empty span { color: #9aa6b6; } +.px-date svg { width: 17px; height: 17px; fill: none; stroke: #5b6b80; stroke-width: 1.7; stroke-linecap: round; } +.dp { position: absolute; top: calc(100% + 10px); right: 0; z-index: 40; width: 380px; max-width: calc(100vw - 32px); padding: 18px 18px 14px; + border-radius: 22px; background: #fff; box-shadow: 0 22px 50px rgba(31, 53, 88, .16), 0 2px 8px rgba(31, 53, 88, .06); } +.px-syncbar .dp { left: 0; right: auto; } +.dp__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } +.dp__head b { font-size: 17px; font-weight: 600; color: #1f3558; } +.dp__nav { width: 40px; height: 40px; border: 0; border-radius: 50%; background: #eef4fd; color: #2f76f0; display: grid; place-items: center; cursor: pointer; } +.dp__nav:hover { background: #e0ebfb; } +.dp__nav svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } +.dp__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px 4px; } +.dp__wd { display: grid; place-items: center; height: 34px; border-radius: 10px; font-size: 13px; color: #3b4a5e; font-weight: 500; } +.dp__day { height: 40px; border: 0; border-radius: 12px; background: none; color: #1f3558; font-size: 14.5px; cursor: pointer; transition: background-color .15s, color .15s; } +.dp__day:hover { background: #eef4fd; } +/* выходные — серые (Сб, Вс) */ +.dp__wd.dp__we { background: #f0f1f3; color: #6b7280; } +.dp__day.dp__we { background: #f4f5f7; color: #6b7280; } +.dp__day.dp__we:hover { background: #e7e9ec; } +.dp__day.dp__out { color: #b3bcc8; } +.dp__day.dp__today { box-shadow: inset 0 0 0 1.5px #b9d0f4; } +.dp__day.dp__in { background: #dfeafc; color: #1f3558; } +.dp__day.dp__start, .dp__day.dp__end { background: #1565e6; color: #fff; font-weight: 600; box-shadow: 0 6px 14px rgba(21, 101, 230, .28); } +.dp__day.dp__end:not(.dp__start) { background: #3f86f2; } +.dp__foot { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; } +.dp__link { border: 0; background: none; color: #2f76f0; font-size: 14px; cursor: pointer; padding: 8px 4px; } +.dp__today-btn { border: 0; border-radius: 999px; padding: 9px 22px; background: #e6effc; color: #2f76f0; font-size: 14px; cursor: pointer; } +.dp__today-btn:hover { background: #d9e7fb; } +/* карточки показателей */ +.px-kpis { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 14px; margin: 4px 0 16px; } +.px-kpi { position: relative; display: flex; flex-direction: column; min-width: 0; padding: 16px 16px 8px; border: 1px solid #edf1f6; border-radius: 20px; background: #fff; overflow: hidden; } +.px-kpi__top { display: flex; align-items: flex-start; gap: 10px; } +.px-kpi__row { display: flex; align-items: center; justify-content: space-between; gap: 6px; } +.px-kpi__icon { flex-shrink: 0; width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; color: var(--c); + background: color-mix(in srgb, var(--c) 13%, #fff); } +.px-kpi__icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.px-kpi__main { min-width: 0; flex: 1; } +.px-kpi__value { display: block; font-size: 25px; font-weight: 600; letter-spacing: -.02em; color: #14233a; line-height: 1.15; } +.px-kpi__label { display: block; font-size: 13px; color: #33475f; margin-top: 2px; } +.px-kpi__hint { display: block; margin: 10px 0 6px; font-size: 11.5px; color: #6f7f94; line-height: 1.4; min-height: 16px; } +.px-trend { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; font-size: 12px; font-weight: 600; color: #8b98aa; white-space: nowrap; } +.px-trend svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; } +.px-trend.good { color: #1f9d63; } +.px-trend.bad { color: #e0485f; } +.px-kpis:has(+ .px-cmp:not([hidden])) { margin-bottom: 8px; } +.px-cmp { margin: 0 2px 14px; font-size: 12px; color: #6f7f94; } +.px-spark { display: block; width: 100%; height: 40px; margin-top: auto; } +/* таблица каналов */ +.px-table { min-width: 640px; } +.px-channels { min-width: 900px; } +.px-table td small { font-size: 11px; } +.px-lost { color: #e0485f; font-weight: 600; } +.px-total td { font-weight: 600; background: #f3f6fb; } +.px-total td:first-child { border-radius: 12px 0 0 12px; } +.px-total td:last-child { border-radius: 0 12px 12px 0; } +.px-ch { display: inline-flex; align-items: center; gap: 8px; padding: 5px 12px 5px 10px; border-radius: 999px; background: var(--ch-soft); + color: color-mix(in srgb, var(--ch) 82%, #1f3558); font-weight: 600; font-size: 13px; white-space: nowrap; } +.px-ch::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--ch); } +.px-menu-cell { position: relative; width: 44px; text-align: right; } +.px-more { width: 34px; height: 34px; border: 0; border-radius: 10px; background: none; color: #3b4a5e; cursor: pointer; display: inline-grid; place-items: center; } +.px-more:hover { background: #eef3fa; } +.px-more svg { width: 20px; height: 20px; fill: currentColor; } +.px-pop { position: absolute; right: 8px; top: calc(100% - 4px); z-index: 30; min-width: 220px; padding: 6px; border-radius: 14px; background: #fff; + box-shadow: 0 16px 36px rgba(31, 53, 88, .16); display: flex; flex-direction: column; } +.px-pop button { border: 0; background: none; text-align: left; padding: 10px 12px; border-radius: 10px; font-size: 13.5px; color: #1f3558; cursor: pointer; } +.px-pop button:hover, .px-pop button:focus-visible { background: #eef4fd; } +.px-foot { display: flex; align-items: flex-start; gap: 10px; margin: 14px 2px 4px; font-size: 12.5px; color: #6f7f94; line-height: 1.5; } +.px-foot span { flex-shrink: 0; color: #2f76f0; } +.px-foot svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; } +.px-table select { height: 36px; border-radius: 10px; min-width: 180px; } +.px-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; } +.px-grid .card { margin: 0 0 18px; } +.px-grid .px-head { margin-bottom: 10px; } +.rp-source-opt { display: grid; gap: 10px; } +.rp-file-period { margin: 0; padding: 12px 14px; border: 1px solid #dbe9fa; border-radius: 13px; background: #f1f7ff; color: #355e91; font-size: 13px; line-height: 1.5; text-align: center; } +.rp-file-period[data-state=warning] { border-color: #f1dfbf; background: #fff8ed; color: #8c652d; } +.rp-pbx-opt { + --rp-source-color: #4389e9; --rp-source-tint: #eaf3ff; + --rp-source-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='8' y='3' width='8' height='5' rx='1.5'/%3E%3Crect x='2' y='16' width='8' height='5' rx='1.5'/%3E%3Crect x='14' y='16' width='8' height='5' rx='1.5'/%3E%3Cpath d='M12 8v4M6 16v-4h12v4'/%3E%3C/g%3E%3C/svg%3E"); + display: grid; grid-template-columns: 42px minmax(0, 1fr) 20px; align-items: start; gap: 12px; + padding: 16px; border: 1px solid #e5ebf1; border-radius: 18px; background: #fff; + cursor: pointer; font-size: 13px; box-shadow: 0 2px 6px rgba(31, 53, 88, .025); + transition: border-color .18s, background-color .18s, box-shadow .18s; +} +.rp-pbx-opt:has(input[value=bitrix]) { + --rp-source-color: #8973cf; --rp-source-tint: #f0ecfb; + --rp-source-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8' r='3'/%3E%3Cpath d='M3 20v-2a6 6 0 0 1 12 0v2M16 5a3 3 0 0 1 0 6M18 14a5 5 0 0 1 3 4v2'/%3E%3C/g%3E%3C/svg%3E"); +} +.rp-pbx-opt:has(input[value=pbx]) { + --rp-source-color: #4d9c7d; --rp-source-tint: #eaf6ef; + --rp-source-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m8 3 3 5-3 2a14 14 0 0 0 6 6l2-3 5 3-1 4c-1 2-6 0-10-4S4 7 4 5l4-2Z' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} +.rp-pbx-opt::before { + content: ''; grid-column: 1; grid-row: 1; width: 42px; height: 42px; border-radius: 13px; + background: var(--rp-source-tint) var(--rp-source-icon) center / 23px 23px no-repeat; +} +.rp-pbx-opt > span { grid-column: 2; grid-row: 1; min-width: 0; } +.rp-source-opt .rp-pbx-opt input { grid-column: 3; grid-row: 1; margin-top: 2px; } +.rp-pbx-opt b { display: block; font-weight: 600; font-size: 14px; color: #24384e; line-height: 1.4; } +.rp-pbx-opt small { display: block; margin-top: 5px; color: #6f7f90; font-size: 12px; line-height: 1.55; } +.rp-pbx-opt:hover { border-color: #bfd5ef; background: #fbfdff; } +.rp-pbx-opt:has(input:checked) { border-color: #83b3ed; background: #f1f7ff; box-shadow: 0 0 0 1px #d7e8fc; } +.rp-pbx-opt:has(input:focus-visible) { outline: 2px solid #4389e9; outline-offset: 3px; } +.rp-pbx-opt.px-off, .rp-pbx-opt:has(input:disabled) { opacity: .55; cursor: not-allowed; } +@media (max-width: 460px) { + .rp-pbx-opt { padding: 13px; gap: 10px; grid-template-columns: 32px minmax(0, 1fr) 20px; } + .rp-pbx-opt::before { width: 32px; height: 36px; background-size: 21px 21px; } +} + +.rp-pbx-dates { display: flex; align-items: center; gap: 8px; justify-content: center; } +.rp-pbx-dates input { width: 150px; height: 40px; border-radius: 12px; } +.card.wk-board { --wk-channel-width: 220px; --wk-metric-width: 205px; margin-top: 12px; padding: 8px; border-radius: 20px; background: #fdfdfc; box-shadow: none; } +[data-rp-view="weekly"] #wk-status { flex-direction: row; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 0; padding: 14px 18px; border-radius: 18px; background: #fdfdfc; box-shadow: none; } +[data-rp-view="weekly"] #wk-status h2 { font-family: var(--font); font-size: 16px; } +[data-rp-view="weekly"] #wk-status p { max-width: 740px; color: #87919b; font-size: 12px; } +[data-rp-view="weekly"] #wk-filter { max-width: 100%; flex-wrap: wrap; justify-content: flex-start; gap: 8px; } +[data-rp-view="weekly"] #wk-filter input { width: 144px; height: 36px; background: #f3f4f4; border-color: transparent; border-radius: 9px; font-size: 13px; } +.wk-board .dr-scroll { max-height: calc(100vh - 260px); min-height: 280px; overflow: auto; border-radius: 14px; scrollbar-width: thin; scrollbar-color: #d7ddde #f6f6f5; } +.wk-table { width: max-content; min-width: 100%; border-collapse: separate; border-spacing: 2px; table-layout: auto; font-family: 'Segoe UI', Arial, sans-serif; } +.wk-table th, .wk-table td { white-space: nowrap; text-align: right; border: 0; } +.wk-table tbody tr:hover { background: transparent; } +.wk-table th { height: 42px; padding: 7px 10px; } +.wk-table td { height: 32px; padding: 5px 10px; border-radius: 5px; background: linear-gradient(135deg, #f6f6f6, #f2f3f3); color: #263649; font-size: 14px; line-height: 1.2; } +.wk-table thead th { position: sticky; top: 0; z-index: 8; padding: 7px 10px; background: #f1f2f3; border-radius: 10px; color: #142236; font-size: 14px; font-weight: 600; } +.wk-table .wk-brand { left: 0; z-index: 10; min-width: calc(var(--wk-channel-width) + var(--wk-metric-width) + 2px); text-align: left; padding: 0 20px 8px; border-radius: 0; background: #fdfdfc; } +.wk-brand span { display: block; font-size: 24px; font-weight: 400; letter-spacing: -.025em; line-height: 1.1; } +.wk-brand small { display: block; margin-top: 2px; color: #8b959e; font-size: 13px; font-weight: 400; } +.wk-table .wk-period { min-width: 110px; text-align: center; } +.wk-period span { display: block; } +.wk-table .wk-metric { position: sticky; left: calc(var(--wk-channel-width) + 2px); z-index: 3; width: var(--wk-metric-width); min-width: var(--wk-metric-width); max-width: var(--wk-metric-width); white-space: normal; line-height: 1.25; text-align: left; font-weight: 400; } +.wk-table .wk-channel { position: sticky; left: 0; z-index: 5; width: var(--wk-channel-width); min-width: var(--wk-channel-width); max-width: var(--wk-channel-width); white-space: normal; vertical-align: middle; text-align: left; padding: 20px; border-radius: 13px; background: linear-gradient(125deg, var(--wk-tint), #fafaf8); color: #17283b; box-shadow: none; } +.wk-channel--phone { --wk-tint: #eef4f0; --wk-accent: #93bfa7; } +.wk-channel--pin { --wk-tint: #fbf4eb; --wk-accent: #efb26e; } +.wk-channel--ya { --wk-tint: #fcf1ed; --wk-accent: #ed6659; } +.wk-channel--web { --wk-tint: #eff4f1; --wk-accent: #83b8a2; } +.wk-channel--source { --wk-tint: #f2f4f8; --wk-accent: #6d9de0; } +.wk-channel__icon { display: grid; width: 48px; height: 48px; margin-bottom: 9px; place-items: center; background: transparent; color: var(--wk-accent); } +.wk-channel__icon img { display: block; width: 32px; height: 32px; object-fit: contain; } +.wk-channel__icon svg { display: block; width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.wk-channel__icon--brand { background: transparent; color: var(--wk-source-color, #637a91); box-shadow: none; } +.wk-channel__icon--banner svg { width: 32px; height: 32px; } +.wk-channel__title { display: block; font-size: 18px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; } +.wk-channel__hint { display: block; margin-top: 8px; color: #566575; font-size: 13px; line-height: 1.4; } +.wk-table .wk-value { min-width: 110px; font-variant-numeric: tabular-nums; } +.wk-table .wk-heat { background: var(--wk-heat); } +.wk-table .wk-budget { display: block; width: 90px; max-width: 100%; margin-left: auto; height: 22px; border: 1px solid transparent; border-radius: 4px; text-align: right; padding: 0; background: transparent; color: #40546a; font-size: inherit; font-family: inherit; font-variant-numeric: tabular-nums; appearance: textfield; } +.wk-budget::-webkit-inner-spin-button, .wk-budget::-webkit-outer-spin-button { appearance: none; margin: 0; } +.wk-budget::placeholder { color: #89949d; text-align: center; opacity: 1; } +.wk-table .wk-budget:hover, .wk-table .wk-budget:focus { background: #fff; border-color: #d5dfdd; } +.wk-table .wk-gap td { height: 10px; padding: 0; background: #fdfdfc; } +.wk-del { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; border: 0; background: transparent; color: #9aa6b4; cursor: pointer; font-size: 14px; line-height: 1; padding: 1px; border-radius: 5px; opacity: 0; transition: opacity .15s; } +.wk-period:hover .wk-del, .wk-del:focus-visible { opacity: 1; } +.wk-del:hover { background: #fdecea; color: #c0392b; } +@media (max-width: 760px) { + .card.wk-board { --wk-channel-width: 112px; --wk-metric-width: 136px; margin: 12px -4px 0; padding: 6px; border-radius: 18px; } + .wk-board .dr-scroll { max-height: calc(100dvh - 190px); } + [data-rp-view="weekly"] #wk-filter { width: 100%; } + [data-rp-view="weekly"] #wk-filter input { flex: 1; min-width: 0; } + [data-rp-view="weekly"] #wk-filter-reset { flex-basis: 100%; text-align: left; } + .wk-table .wk-brand { padding: 2px 10px 8px; } + .wk-brand span { font-size: 20px; } + .wk-brand small { font-size: 11px; } + .wk-table .wk-channel { padding: 12px; } + .wk-table td, .wk-table thead th { font-size: 12px; } + .wk-channel__icon { width: 38px; height: 38px; } + .wk-channel__icon svg { width: 21px; height: 21px; } + .wk-channel__icon img { width: 28px; height: 28px; } + .wk-channel__title { font-size: 13px; } + .wk-channel__hint { font-size: 11px; } + .wk-table .wk-period, .wk-table .wk-value { min-width: 96px; } + .wk-table .wk-budget { width: 76px; } + .wk-del { opacity: 1; font-size: 12px; } +} +@media (max-width: 1400px) { .px-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } } +@media (max-width: 1100px) { .px-grid { grid-template-columns: 1fr; } } +@media (max-width: 760px) { + .px-ats { grid-template-columns: auto 1fr; } + .px-ats #px-check { grid-column: 1 / -1; justify-self: start; } + .px-ats__icon { width: 56px; height: 56px; border-radius: 18px; } + .px-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } + .px-kpi { padding: 12px 12px 6px; } + .px-kpi__icon { width: 38px; height: 38px; } + .px-kpi__value { font-size: 21px; } + .px-kpi__top { flex-wrap: wrap; } + .px-range { flex-wrap: nowrap; width: 100%; } + .px-date { min-width: 0; flex: 1; padding: 0 10px; gap: 6px; font-size: 13px; } + .px-period { width: 100%; } + .px-show, .px-btn { width: 100%; } + .dp, .px-syncbar .dp { left: 0; right: 0; width: auto; max-width: none; padding: 14px 12px 10px; } + .dp__day { height: 38px; } +} + +/* ---------- плавное появление ---------- */ +.reveal { opacity: 0; transform: translateY(10px); animation: reveal .7s var(--ease) forwards; } +@keyframes reveal { to { opacity: 1; transform: none; } } +.chart__line.draw { stroke-dasharray: var(--len); stroke-dashoffset: var(--len); animation: draw 1.4s var(--ease) .2s forwards; } +@keyframes draw { to { stroke-dashoffset: 0; } } + +html[data-motion="off"] *, html[data-motion="off"] *::before, html[data-motion="off"] *::after { animation: none !important; transition: none !important; } +html[data-motion="off"] .reveal { opacity: 1; transform: none; } +html[data-motion="off"] .chart__line.draw { stroke-dashoffset: 0; } +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { animation: none !important; transition: none !important; } + .reveal { opacity: 1; transform: none; } + .chart__line.draw { stroke-dashoffset: 0; } +} + +/* ---------- адаптивность ---------- */ +@media (max-width: 1180px) { + #app { padding: 14px 16px 14px 12px; gap: 16px; } + .dash { gap: 10px 18px; } + .dash__heart { min-height: 560px; } + .summary { padding-top: 22px; } + .summary__stats { gap: 18px; } + .who__meta { display: none; } + .vital { min-width: 110px; padding: 11px 14px; } + .vital__value { font-size: 21px; } + /* «Врач или услуга» — второстепенная колонка: на планшете уступает место остальным */ + .leads-table th:nth-child(5), .leads-table td:nth-child(5) { display: none; } + .leads-table td, .leads-table th { padding-left: 8px; padding-right: 8px; } +} +/* одна колонка — только на узких экранах: на планшете композиция референса сохраняется */ +@media (max-width: 900px) { + .dash { grid-template-columns: minmax(0, 1fr); } + .dash__heart { min-height: 520px; max-width: 620px; width: 100%; margin: 0 auto; order: 2; } + .dash__right { display: contents; } + .summary { order: 1; padding-top: 18px; } + .trends { order: 3; } .leads-card { order: 4; } + .who__meta { display: none; } +} +@media (max-width: 760px) { + #app { flex-direction: column; padding: 10px 12px 80px; gap: 10px; } + .side { + position: fixed; left: 10px; right: 10px; bottom: 10px; top: auto; width: auto; height: 64px; min-height: 0; + flex-direction: row; padding: 0 12px; z-index: 50; background: #fff; border-radius: 26px; filter: none; box-shadow: var(--shadow-lift); + } + .side__shape, .side__logo, .side .switch { display: none; } + .side__nav { flex-direction: row; margin: 0; gap: 2px; flex: 1; justify-content: space-between; } + .side__foot { margin: 0 0 0 4px; padding-top: 0; flex-direction: row; } + .side__btn { width: 40px; height: 40px; } + .side__btn .tip { display: none; } + .top { flex-wrap: wrap; } + .search { width: 100%; order: 3; } + .who { margin-left: 4px; } + .summary__title { font-size: 44px; } + .summary__stats { width: 100%; justify-content: space-between; } + .dash__heart { min-height: 440px; } + .vital { min-width: 104px; padding: 10px 12px; } + .vital__value { font-size: 20px; } + .card { padding: 18px 16px; border-radius: 22px; } + .leads-card, .card { overflow-x: auto; } + .leads-table { min-width: 640px; } + .trends__legend { margin-left: 0; width: 100%; } + .page-title { font-size: 28px; } +} diff --git a/internal/api/adminui/assets/README.txt b/internal/api/adminui/assets/README.txt new file mode 100644 index 0000000..81980d0 --- /dev/null +++ b/internal/api/adminui/assets/README.txt @@ -0,0 +1,4 @@ +heart.webp — прозрачный рендер сердца для главного экрана админки (1025×1400, альфа-канал). +Если файл убрать, на его месте появится пометка «Нужен 3D-рендер сердца». +hall.webp — фото холла клиники в шапке раздела «Слайды на главной». +lobby.webp — фото ресепшена в шапке раздела «Услуги и цены» → «Услуги». diff --git a/internal/api/adminui/assets/channels/2gis.png b/internal/api/adminui/assets/channels/2gis.png new file mode 100644 index 0000000000000000000000000000000000000000..d391ec661a477329f7c9907629d58d4dd4897ace GIT binary patch literal 4236 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 + + + + + + + + + + diff --git a/internal/api/adminui/assets/channels/google.png b/internal/api/adminui/assets/channels/google.png new file mode 100644 index 0000000000000000000000000000000000000000..17d3f981b3949bf1b65309d4812cc55401caf025 GIT binary patch literal 1319 zcmV+?1=#wDP)} z^UWEB9Kp;503H^R`vFt}CHvVs_9);g%Q z-oeb}Lnamxx7K=_);gLiu}vZot@T!B&KvBkCu^-w50%&^k>8oQ%#KUhkk1j3eMB@W z)2Ag}I6GmktsylY;Et7>s(5~q|>^N1)mASE0k@@6J^@T*VJz2%KTlgGyE4I4H4!;YL`M3|O>#RpO3UE9dm)T*#j%p>)=LOC97eSHY%*r}vd7@9 zT^sXff|;XAsS0x`VqWu{6Vb}_&33)!Nl0JF@}WtXU%M90x$|rj3m-p%?rodkT2zJn z)oc2#5Hs&E45LPgh>w}uh-i4i8K`>+!S9b)J{VRp1HR35P~L?2*+YT>D7UBIn0Nv~ zrBo4#$g*^D>1~0lVmn-SbtHpn#7Gou+%_P&iCoBIV-mRmETy=%r)JQ;3osb&#h2h& zejdsx@I1E~hZ>ak*J|Tv?*zcf>oHkqL{+pAPA4Pe!zLHDL@#F^uf@!GYK8kKMTrM$Jc72_Ngr zN}$E8Kx4W^72cQpy|-@yo@gz_p7TFrusGJNvuu;cC6zvbMvC`vsRYKZKm-nK?YR>h zy5}GS1Mi>u0@p%aIg|H<1YaMGSZueAA{1p*z~nY7u$!5awNiN{_|UTs--Jq%ft?{g zYJb@bf4F-<@`wgDe%pu6fOW$?6g%-*QoM)a-Rqa>&J{CP!R<(@a}0l6Z^6_1Ud6%I z-)=~Lw(A0(-@OYBEn$m&gh%0#*;#w&bEQ;I;yNxDktU+lPTPCoAl4tLvn47iD8_=K z*_fDLWR?+EdavQcl_oS^Jp+Nj2$jLz{vuTLU-{nf_;4(ml3IUej>NCy=H#N7-768j zl1{Yq%y+0e`5A@+rM<{&e*w?Vt;EWEofaQ6zik-Cx>#I#8_vu}iD*W8aQ$D0u&Ln# z1fzrNiVS6-rs65o%zQMHypf3JE2TnqO=+2k93~=*8np$l;=RV5*nja5c%Y^fz}zv@ zQ9J7yOf9rrGv@g1M6^&T)snEO%#TH67BkmpCSl@opd*$(Nj@9VF9KT-kz0|Nv@Q7kcce0V+*qq=40X(K1@!Eh_xI8ftmBP*6*0ltD7JW zYpvfB5$l;AWFlo+>yNe8fk6-lwAS01`Hq3c&+eIToQT;sEX{bH`{HiDd9fs(=cdA$ dB9qIw{{Y>{ewQAhP=5db002ovPDHLkV1jMLiZK8H literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/instagram.png b/internal/api/adminui/assets/channels/instagram.png new file mode 100644 index 0000000000000000000000000000000000000000..9b3aac0062c170d46952f79248483c6c35d2d73a GIT binary patch literal 17025 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 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/max.png b/internal/api/adminui/assets/channels/max.png new file mode 100644 index 0000000000000000000000000000000000000000..40c8a913d8270954f3010e6308c65a4d697164aa GIT binary patch literal 18115 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 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/prodoctorov.png b/internal/api/adminui/assets/channels/prodoctorov.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ebdd41b2e9a05c2ab9840b7bc0e95086a4cc6f GIT binary patch literal 1031 zcmV+i1o-=jP)6lZmc!g%p94t;79kXyEB`e9}L5A&%O8j z=iWK@o;yMVvcWpN!btRW;119)97Esa?zSHb*%}&*)W9EWjWpT77T|J_J0q^VQ_Q}h z1l+bK+l@M7pez1Y)Y;eqRHbtu_pHK)K&*g8Q?aU17MSE)XNX0v^QS z%K4!e&mr%1DrlWvYc&G2JK2rOe()!})m4CQUVj%VAC%g2G!m&j*%ONFo7Px=RJ*!b zFz5D%#rX?dBj|H6nZ|J0pN|9Wgb+b@7?57VOS`ulT|70AKyp8)Tw2vM^Z z<8D)BJ8-LS9+n}H6l*>|R;N$kVa3brYRAsR3;G&pj+lB$7c-spPT!seZmO#QO>g)X zSXHF>2Sf*Dc=D}^dbiZ1BzvDAFKF>Ls}fLrGbemhqP-hXc~iF}w^0d}xOS#(s99TA zD?I7v{>v0C5&5`$b$RMiI{VC$x$#W=)mfEQqP$I@0v0EZpqwsQTiB$%$)%Ut>6F#_ z`%x?2r;rBunIHtn(2#?0s7(C`Gu&Nm=i{09=d5gYf0AL z9lIZqoq*%7fLUjxf=ympJ34ARk`Yn6zjd!swgR~V7Wt|K$fr|Tl(911$GN)cc6?yW zijN?XjTe6x>iu@DIwvk4ja2W&C+z-~uU*ZjfZ_RYD{@#>y<>W+T8Z_Z)Jh`m_a%*l z<1#$q>z|iODLVr4iG3XSuB4H&Qq#$*R@nL+tCU`q&!e?PqhJNNrh;&aZ{LCc0!Ho6 zLBA|%Biurx3J`CWn@A7J0pM%_lfMvN@!!4j{0)O0E^DVP**5?H002ovPDHLkV1grp B?V$hw literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/sber.png b/internal/api/adminui/assets/channels/sber.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6373d25d003bf9ff97721a1470213b1d1780cd GIT binary patch literal 1506 zcmV<81s(c{P)d75}~JuIio{Ecb?h_d_eUFN6zPBTjqrj8Ci-3FjZk+6NX#Ijr}B$Z6ImxS%x( z2!s@Qd`fN@DbnVY@d_cL9AZaG5J(6&5ZLSicD&&3s_N-}sM;UcyZ)Fp@RLg_SJ$ie zdmmNr4PijcUGGMCES%C|IH3c7+y*Mv%8#s)Fv{y$UnY4V*ZRI(v0nb}+S8|cgPcpp zZMgB$J8?eHIt$dWQIbp`p_LD(TnCb^+*&irXR9Px-$&mU9nE6?qo-@hi3luC zFGgMvv~68o%>qd}RIHUG?UV8fd zh&+)L1zHDrgYI8gZN40vy6S4BMs=tt3w&gudd<}O*Jl=H28!#mH*d#}E z9_7BD*z!RVwD+I&C+FfK8t$ep!9s;BkV#q(W-k5ZcPlsnT7SQD0Unl(^y6Gg=Z$q; zVsbADu~%TYq;5UcaU;`sB-QK6X04Mdcj!)z{F~FV6`DK=Y|hHqu{yXrzIr!$fIx9} zwq4gIu4e{~%p|qEn7sJ6KOC9CgJ9*-nM7%B)r|ctw3d~%v0uvez7S-mXJaXN-OWou zFrnWX`sDQ!TYeCJee;WE?u)Jo6dUOggU0@G@l;ZIKniXXz;5o9+!;vuN4$WKdA%BL zehmJ4>HJDy?Q1n{;f5^wSKYecc-qBdy4uCewxmPv>^?A@TwD#p@E{_>;(9I9R5KQV zDW;p<^+*(XIOP;GKt!}u`~O=%I_`&I`8Q*|z!oc&7mSI*TuuovPdL?;QGI?FKQn69 zE^QIWIkfqNPlWNxe1L@~SAT8->R!I$*9cTHyuLq9{c~6Df+=!g@Nd7p?_Jmd~P-n9KbR1K5*wA!za4*-hYAfzW_Tz&-^OJV)ioq+IQ2o58VX0cZ)h_c+E;@k_X%oukcJoaO{{l*cY*h2|dI;gAP;_NUmGQ33Z zzc@39+~VAyW8(#%>Y!?3)&w)wwoYWug>B(skXpi*8^D*}3@j3E>SjGN)X)ZvRIdq8 zd8_(DM4}XzABq4mBXcC~4JlhHB!@= z>CE&VD97%0m4uf3-&;RvIsRh&rKVA`tHZ!GvG}&3##+nQw#u<9Kt!lYsw+!W@}eX4 zQz>_64Q*zNjnueK3$@K`-YIwAc6&lsmP;F~GV^Fv7n#>Bi^(5b9r^TZTo89lPsHxG zgX=RN|M1nZo$q32;&Q>3)B?7jWFf|+;BJ0;ws}(JPPP=>1tAti!GX5PP`cSVvMuM+ zvvDcVW%u@1@u<+T1?C6!Ms<@Krd@D3_roF*4}SChhmMYN8xcmUR;AimH4Pmmy5*I5 zmu~-Q-$8ul_oLvvS$H%J#y+nRkT~xiWB?syNpbc{n;C6)J$H$0d!-U)B-N_mXt%0v z?{QnIhk~&nvf4cL7J4W8ahh#i(KeVbZ+l_8f7x|R#c9uh%)}cEvD7cHa9r4qC9rE$ zG#3>OJyn)v(-U+7j8O(oZJokB0wrLq6=iu#>c;Y67T=HW0dfPu`VKHvfdBvi07*qo IM6N<$g8D4sE&u=k literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/telegram.png b/internal/api/adminui/assets/channels/telegram.png new file mode 100644 index 0000000000000000000000000000000000000000..15bdf0a6caa788d9d8ca6e8c95c048657d37b56b GIT binary patch literal 4642 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 Y56a)g5td+ZYybcN07*qoM6N<$f^clo>i_@% literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/vk.png b/internal/api/adminui/assets/channels/vk.png new file mode 100644 index 0000000000000000000000000000000000000000..7034d9b2962161469155c0293ba49f85cb7d2f3f GIT binary patch literal 6400 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 CF%R7U literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/channels/website.svg b/internal/api/adminui/assets/channels/website.svg new file mode 100644 index 0000000..71ace38 --- /dev/null +++ b/internal/api/adminui/assets/channels/website.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/internal/api/adminui/assets/channels/yandex.png b/internal/api/adminui/assets/channels/yandex.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fc7fd3018474ac2ba35bb11f840fc5182714ee GIT binary patch literal 4300 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 N002ovPDHLkV1h>xJfQ#p literal 0 HcmV?d00001 diff --git a/internal/api/adminui/assets/hall.webp b/internal/api/adminui/assets/hall.webp new file mode 100644 index 0000000000000000000000000000000000000000..7659e60dd4eee564bed59aa8e92b47c052a4028f GIT binary patch literal 221606 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þ|FBfO3 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 enM_KJjm5zzessg^Dda*p&zEGr5lECPHeg@? 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μ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%0D}wif3S3n2q)4Lj=q5$Tw&*T#2WqfO)~z8~F0x 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,?Hu4FGYRG2+LyUmmK9yUnF5rIZHSLdj1uK>6WU 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

BXqol%~(7mD8f8<3rFyt+HiozcE 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

oT-%L%l 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

E->~qSWyFQO;mCR=h)y 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

7{}bX0h7_y7BoXoG5x5VmI! zeOYQH7W;=NPjUu7R!%_S42c#~ZUMh`sCt*KWg#L;8B$M0J2}{5!U`9j6%}w!`4ga3 zjg#O5*e}_y9px>vs!@O(k6zJ;ZVMfhgP;gmd(k}Mtm!u!C&a@<{7_FU7xm2#7OEP8 z#xn+gO@goISeue&^3RbZ=&ovmgUroU;~SDIr)+kt{Clm{P`TSgPt#LHUFM?ui}z%R zl=9L8F`cR3A#24O2KW1o{h%O>qxy*{#;3yX}!23#{uDHn~{XpA4v!2{Qoa%{nXv8dC zVBx@55POL4h}O}ABWiFEC1bqkp3Tnzh>xG3$iv^rw$kxo(vJUyYwh^_mH%_Y;{kJF zx2wGJ1DKrt7p=f+yh1J2v4Wa+^uO1MQ|K zd||v_e~`0P@4CKhAL&+9>QCBJA>DM~_>S8j_}nptm{moR$TjHxvrYS<1Wpa_FZ>hr zDAocVO~mhwz7v78OOHIVS{S%QHA}7|1?$sv<@^j z(cjs0j7$cBt+MvkH!uFi*VFhHbB2qgtr{9*BY?TD_jV}XFlN?b3N%Imff_imeQG_1 z(qe&juqvN3CV3?wT-N`%NNApcu_(q@^D_MSeY2Uj}$>|HJ)qjt<= zU8Oqk=)COg5pQfhjzcBszO{V^)022`M^gLl51*#1c%1h()F14YqJmncljaVPvqo$B zfi~9t^72-i7k86xHlo6(K`y73Y$v#r=Spc#&`|jI(ZM&% z+mMCqe~2DoGJiC+ zp3X+nu9lD&R)WHARc++i?X$7cYLb~)A=tOtLz_W^j}LUf9BZIK zXRbJF1c+Y=Rs*rPA4#YX&(w}{Pzr^6hhMMS3TeH9k>Cbc)%|7RI)Gi)%nNMUpxnjQ znc&&iOD!;+QxHe^yj%Ji}?SDd(24_|%z6)1370qWFKQ z^dTF$-=617tBrj_{bVSi{6PKRWN=-de(6Evv+Z$P2x*?Os7>apa8acYBXC-_mMe{H zv{uJ}YmtF1cP_FnrypwpDhlV0+6NXdsCuP;9nUJb+gb;;QY$I}#knPT)J)}gTS+bj z-V5OB<&jow&6u<4M}`s|4tB>%UTQ$xFB&%;rO9}%A*3nD5FW9fCB&z-8~z)EVV~Tt zC}7tV2pc%Zz`c54S@gc`>|WxQNtnYyJ%Km$D;!`11>TmfMOP%u9H^GAc`R6oKr52K z%0Oena#{QHu%|{0CD^LBhD^?63 zV+k~HqFZVn#je@KM`c0|fjjL7D3ho=sUbi14=8ScEW^6DJzP;LzhJ0nksey6J?Q_U z@V0moDD5FFA9!VJv1q0Ukspg;QDSzH^1RMzFm-1EbdNefr3*A49+TILP29- z>Sg{@@^ceD5ZXCf8b4I8q06hV@t(0drk`|Q(qh>+xRfZMX?|u_y<}&RMg~xngMYtl z6?V%aL1V$^i3Wj9AA}Ty3)u6lwbt3#rSJ%V_Fz*E{I)Xs?lhi9yltY{NyOl1z7V~G z%M47C3mIk&@B}QUx(;S@=t?-wLEMRtM(zpFYEEiN=@hgQJ0}Q{b8fG&r)$T2V|hvH zaFP8_es|lFl=I|GD|jqtv(fz9Yd-qj-RZtJ{vxAmDpg%oGJ{RKE8H2!V4~?&#|7$N z)za|EY?$;JY)mE|CcSNH`M&j^Sgu>d@gvjxASzg(1tOe| zVSw2?d!WspZ6^m5*|Z07GJhq6i<1}BN$I^5IofOS3kIOr3MF=F+a z;o>kN-D;Vb4qN$mkJw<6xBfGJYV11B2t`=9U3nEB?k6upa<>YKROzHEg3n@q^W;4Q zYoXD&#`&Z6<5GVyt<{&??JsfBc^<78)L{)Qt_~EKGT}m@jgQ9uXVDJ-%;k$@+C9o; z?Wv)?9*D=Ynbq|}FV|0>2USbs`3MO8ZuO5}s+l=jW^%E*d&^eBGcPwXOnwl?i@aTX zbVxPc@&I!A@q@Z;c;nM!C@pihf!BYSF27Q}lmFpZQlCA&v_8U0sT~6uvE%Y00aIm> zEeTeaSYv`OB*T=hcEw3eCdj+JE@@X=+HbJGBq4)moNa6fq++$X@=-GGWNMA|GhG_D z;0$AJqV+l~3H`sf(7IkB(!Y!^wOo(I%CDnDC3_=^HyZ|+?-CyD?DV+txL_)qC(^|1 zPq25OXrj))8i|3l?w-{SyMCstDM18#a1;dM~chWMuSKgQy3|8#ov_%SEQJs4{Q@KyRa7`pA?&&sFKIH^g zoZQW`g8>0)gii*488muGeO5MfoR@W4Y;m7l;*KL+TRgt-# zXVfT;t;pl`R<=Kp?l$)^+-#aYpA}Kh9n)HJzRg^aG#q2vu&B!BwzbD7B{%Ez9kK|K zOyO@bJu#|P6vD^6@^`^38XGgv=E%wIvndZs85Yzm5cM7%Ux*-dcB~t4iM^~PH zn`qg9Jqg%E(@twa3RkOPA)y!q^@}c_jX^3gXc3{&hODy0G2p~wK_QLH7$pVJm|fXJ zcYyV1ual!)00aqQ<;D=&8UJZQKr)^fXsIl_aTe=kt(#(lXX4)c#J~o|-F4bVTi6M+ zdwx6-6r->NN@>(*hM+doU#-9|xv&F0azDr9;yrN4Jb_GZfKA>fl~9pbEl|rC%7G0T zZyAx30-NSPzbr6`(ul0|7w=H$p@rVjD%wzV>u)Q^&tCE^SebArZ9%&8)%J+Z;g{IX zoQam&(j+`78}Ek;!I(_T0~^k9C!09@Ak zG4AK_1{@@`b?%8}IYX_M_tGgk(+Fq;H3JK7?+l8iA;;?y&V6pfxN^zHKARRk@G%2v zUUcW*!hskGz(WkT@8Uy>wH!q=;-SbELRj$rBW3K~=(=Svhto#eR4zx|)%nF3=9;4o z`p7qdEAv%1naTAre)qnBQ(;<1yXB0b-PM&4)-V(~gTX(Fe3q8~4b+GqoUr>lXwtwb znqTindW)dujo^jI5 z=(t`aK)OS~Tjwi)zMcMg**g*>A6yL!ujF)D2G#~)<(`L!OkL)*Ff+O>pgnOJ3b|XI zWT~>OlWDWE?&u6)?9q0c_ED$GMK*AP!Cw}1Wb&2HYc^#}2SLD&m2AMEy*6SjqnXqUD z@66Q|R2~7_IOMZspd&EF0zet=5SQB^j3;h>m^|5F^B`>>h_X(D&FSq!UFUPDcHeLh z--5k{B_&Ak3ss0hPCd2|c;^RLMizAO#F5PKCYS_n)dzbxy+5 zvCORRqZj$Z6t_T(!GATTkf=-|Nrcgh`T>n8wzWepR( z!D{Gce}W^r!iJ$TQ{|zmVzi0G?w`O{SqFZq*2jHHOqHQIQrwQ+3s-%>kI%gmhvB9# z;Z}1}0j#;fB}v#3MUWgenBcNTBk#xPz^}+RmK~>1UNKH3O7VGD))&RR%vG-|-8CZ> zqpKn|UHAH0Y^pvt%cX``$IVVW9I`_PRwe;toNuHg95FV^X#)+7A&lfi#Ntu&WfWN{ z%pTGUkF_oZ!8^PEc=f7r%YgP4I_1hS(tB!`8#lHcT=O>Tya-r>$n-oEjjG{_lLpJk zIy4Bjr039bY6|R9{Ek4}FjNhm>GzFuD4dpRQ>Ezkvb4s9)eK zPB8UmQx4(`r5#H}lypp{tG0q63Ew&{mA-XSj(FJ%-WI9BCdlAwQivT!J1~V@0*Yuv zHE`h8`93mQl{d0_B*VTPing+XzQbq;@Z%9{7%6Oo?kAM|-T<%416>iXEzXpDMQR|g zy({gRl@IVG*9yc^KBsm8lKeO!$YxKP)P~dOap&4Y9EvH~vAy%Zd8_R9P%^CteohV< zCVkbhO%ELE5y{($3_8JIGl4aVCg-;lcWQv(#vJHM19)^rL!9hS zt@gRDrhNP=j(#$5rnTYFLWHz3WY~@cy#ovIbG0FA@;E-l|AR|{<{^HvCHD{MJw9AI zNg#pYAFb&JgG1q$mcn2|TL$2K-BmdRHIwS?DC8~l^e@lum&v*cvbBGkkbrZZ85M3; zQ^j>r4yUkMs^snyeuCQdoNb zdZ7^KrzXEqTe@v?y1dV$VP`1A2o%)N)i>+|&WtOfkbUsxrCWNxiqleFjP!r2=bWkV zg4wGiaxdvntx!Ww4&?raxI|4%8o(F@hA@7dNWz;0WdM!PC z`1zosBOP~m5yw~UGT9f;xjKE+wkZqHQ5msi9sRetH7GAT@@rFci z6X?|%ir_#Z3HLK1o`ae647@kYE-D=x81PSyACQk|mEm|-==8wM&@{3MNT`w{s)rNA z@wkdw`GH^oMW79#g9s=AO|;}E3`QJ2SjyhEXTdV(LTx`O?e|yK(X-}q2Oh=Tp5d(p z0~CGRFy}jCTWZBAUPF)Dhr_)Zo#e_Mkeg|dbCHGcyt_jvu$rw`S6E}aM0BWR(nSZ} z(Ga%otj~q&tjYt*l19z-&1#2Rc0A2!*3B`W9U~{%Nl|*CTJ$2aSAF?2`356vVU_|j zpM^I`*#a6dRwFa|-SXMyI4H1xfwh4Ab^*^9>v3&zHOap6F93b@1xP!3?p-1{ z)K^)4pIDrnRQ&a@H$M0!$d*oF=n{vN@YFVaW^q4Ugmp|(9r&+Dpa%b!j3Dc|N{pV@ zoxd$aPu>5VP}aUtAh$id!F%LO(VoQi`z9rc2^?zKo2Y6l@AAIQ{so9rdR~W|7O$n8 z^+WZY^wa4&55Yb;0oKAz8B-1x78A7q3oGRuFyIX||;-5sO?tsWx4t6tpWlU!|ZXVz_m5%kM8rd?=@W1#!} z=~faZ&JlH);IL-cAxNyt(?%Y}>=w*Z$@#>RFR9{LG_(nm%viUYhfS;^@Z*mlsyU}L zM*`MFpIF4f3tRw&$)GQUPA`)Qlm^`Nd#S-AdV1dWIw7}LPU0OPEI7j%10p;c?TW74 z|9Vln93A?IK$~!sJb%U4lV2Ee(o)f+wxTfJaly;8dHK=lL~uBT2cDw6$x%zQP6iY& z&JuNwCRJ`@OksxM?RYNxHy{^`>3gu&WC$6g>u ztrs_%$s;E0hdKCdv9kF-t#z$e+5VUrP}K2|RDZ)HD-?3|x8gndC>?scKYYXDgjlmt zG^ZuDQHB-rBJ<*yf3NIMZH-BEvzPf4Pt%`WoBPn5RtT9D)`1R@{2-?9`LJDDV*J_g z+8!~vywDE8vhO{)cJwX~OHVp4n404Wum+$_8e&Z$-RTbGc#<||DSatYjdM}YN+@<8 z^fi<3wAvNv=U=UcAUbVzpyO+1aZKPq3^u&qYW3<#t`$R=wi$>*8n~&D$UBYpqrIK) z5ZHXaL~kkfC+7n|UW2eoC*u>6ZCu>t<`Tlfwhn$dKBzSZu^jD0)+>z6m(FEcP@~JZ z1E{QXWWdQYW@S%!*{m#5T9XZwtOp^ za$(R&IuM$Lajxg@CGPca==a}Zo6)Jo3=`MVQdyhq)6~v1jTmF5m6CW4nad(cD#q#W za2(O@CS58use@B0M$&TfQI3`$ux93(;&lZhkQ1*NhySBe?CuOl!H^em4n)e=uZ}?0 zrf89ystP{@Hb{8Zu+D`%eM^DI=k*}Q4nU9+!5+Bl}y3GP<$@!dUAEK5G=zGhB$@(qb~S6a#3=*eD5<{X2Mp( z1EYtnA@vXnO?4R5gm*1LV7|43UY%8x=1d!a)rp6xLtbM8gG>JeWXttTJv&hE8In7I zY~!YlgKpjm&f*10-{#X6!u*T{?|M;Y0PQNjTBvb{CEm!<6|s7pfbxqTF9DTqTFoZf z6eeELLB?37UsFhJgN-i6B^vJC8BTe)H9E9JWgb+f`T&|up!AKWRg?EDH2Sb4=PeA5 z$R9}rO;DvEYcy3iEhQ)%v$~@2^f529n*2R_;>>9j4+V97tttMbc&GRaJc5HZMJNwO z?`6XWrjQVdotyT88t7}gn#CH{@j4*sE$E$G-(d-Hsy9pzYf_6eGXK2$Z7Jb)t zV~pkE#s6ap8$y>2i0<>l$j&JW?yb+a?1n};hd8+)M|wJtLVVfSUTLHL7eG~=ABh`* zc_L*TIO66*3Zd5iPsJ5N#=tM;s0G>hArU}W|3=&}4$SDHZsKNZNsy-`=JC#!$eE|B zql4Z%Dw6Q=e-a6g@ScW_^U`w<-PLPG^D_Gy&9Ehi;q{6{B5ezEmnoXLAt(gg8r^L& z4^EY&2n@(UqP2$waO=~JANAR?cv&x|XGD`QsmMqhp;UC(wR!%cBm`Xpjc8%>CZvV5 zcpeu#a;5&kBz^;F;V!1z{N;8A+FIBeIp)yTVh6^qK$x*We1#u4O{WaUA~kmS)2TD% zpL1cOg5BfSd2&3F;53XEp?f9YFdy&Y`BN^GqDNAna#0m@$xv&q9MM9wkM0k#L% zK4?pyeN=$&yNl}l6sE%Me>2)qh!qMvDkY!iflnz^W7$|y*^~{d!A4iQjZrf^P^@-J zyf~uOw}u2@Aca8nn>U2v+_yzZv3j)%jjB&b@co4KPTKW<0H0PSEnjC)%W)fwZ{;-# z{k2Y9S^ID0phPV3gE=FmC=BgcVAvoaQz+cllO9XQBpm=TZMh|Fqux4A8s`E1odS%Kp73yGvx zcKQW}LagRbzB`6&uZiQc6S-`~uAmu#!@PgC>I1H&M8H8`1@SvKT^a4f%lR`%;EaTX z3x-QTg5-^qIfH`--*w=3d*?0_FD0b0rGj2FM8M(1++a?aZUjTq`7WN0!ncrLz*=x} zElKuUFc?yRS7XM(0u2(qv4T1$n`}Xj^WjuCKlCihiNwS5{P2|#n{zj#8utwXM5Z`-6&oLM--vZ3~ztNo^V zF~z@OVj+K-C0(>s=%WyLB!ecUO#+?`Fx z^NG{*qTh-i_ecX;QW|7n!6WX^(u-=KX=cs3Z==khHUu!^cx%=nfxEfYRG3%R*q*$j z(*!a>V$;d)kF42ReZw>pSNQIgJ^vSU-*fl(A+My;TQKw8vhO81@-YOcC)0C!aZJ9k z7U4LHJ8G82*S9q0+DG}P-vgr$iCgbi(t9DBQeKK<^Yf6cQLv(Yx%1nPIcyIsuKk=p!f= zTDpB7B{UojI1&w{yE$B5Fu?`udAHu;r;yr8cbsll`Cst?Wf8Am4y&4+JZ#k>W`o1E z!g9PgORJv{xaOrR%l_*G!Ui-8@%E$=tTDb~k`om@aG*}5L~gkDg|bWw&=%y2*VE2;IB{K2GXuE92|;kL zy+&F42SkS8PS1YcJGK?SDlSdAIk8QMmh;t8QlP5$*9`{r0Yj%3PemU$;(H<~&fChxjBr0(*Z zgGC@jbv$EoB-_tWuH*A4G{6;gw(ISW^KY)r{?Si|Z-vQ^8-JJDt$lrsWY42uW1MGk z72Bt&zv9FmPm&4EbEK@n?HIyqVqw{%d%8C6S4lT^^BrT4ygnu7zc zY)tR+7&f_Y_(4z>kebW~WEC>`W9L{e(Y{|vz=g(KWkoj?(&2+A#CQJe(tg>D`rimm#B@$yLRZ64R2D|VufpA>V zGuSlh@U>tJ$v9nE#bNlFxM%J!JA$``4?+4(!b5iE_rIyoi#V}SRD&P%)3;H><5cVp z9}p)Bn3YsN5LGh&vMt|tEdy(Fe8dnG;SlssH~ZkG&|cu$evgf||C;1mtey88w*L4= zw-KWjDgPVUr$HVw+zNS9CeSxt`Zx=amVM`LWl&5gQ+yc`<+}eXMS2lP*8_pU=0YSrmV+s}!>NkR?^?#*!b)M8bc*PElGdtD0tP?-H|o^C3ch-PeL3t#L}~;h zV_Rx(o7r9*(A#H;i-(FxTAHB<*ZcizHyA=(e1(#-U-k)Bp8V`Ie`iezx(l_yB1W7e zWe#~SU(9}dF`;kr-ekf-6qo4|Fi5-dZ{5<9Bq@X6LmpCn*;u{DLMdh!%xxKck;@OL zjv?lzHp-m7s~0QvHt*iij_U4(7LWK~>_lp?r9PNxqyeM$@V5=Nqw_?5{y6QJx7o8n zjD~dF4AU~U#qSWT)>tJMP_1`uUep1>ZNp1f_O~VP(cSV!K>J@Sl;4JJIpSPhk##}q z*Jj!n`(ygzR?%Ebboya$4PP?l=^BKctnE_uvAQ~wm)6HD6U)4Uq>N%liN)QA=wmsk zq8aks&ZHP>>{dS!OK;aI)Z6pK91#?2#uuf|9d_GAcRf%JJIJ-%&?y9ro4ywt+#{DR zqP`BxC;N54fiwj8ax+b<)VO2(&1Zw7FidAdLuDku?;=4eMne_2G4__3#Z&b)gDnY? zGeL)xN$)U#8*J=m*MQfbi?OaYeQWqe4>6Gn8C(x|$$E|99bX-O(T##*@!&uu^1tj* zRD7wX+EF_-A+lNm9udfY;qu>yx*74?vaL9fH8iYlG{&!p%59+3 zBs-52@Lt#<9C!`Fm*D*4_3i#%<*=TY6bzMe(Q~(Sr%Iw(N8qX*_0uzEnm>Gc+?$lj zOCF7=VqfHz{500N+B@#*sDKdz5x?DX%@FUol*m*8~;N>Hw(h zg_v0@z*{&0VT-k6c>}tjb||4+ehu?IVamGIXD6#LHQggkQYS`0->!8q^#MWNLOh}+ zE`Z1nbJ^11fvwg*^?d=lY zbvW3alk=AyKMW&puvqjDvdHyyrAiuwE_#9gm7~5xp>i{+I?lUqs{X0et(V(`P(1EB zvHP!;ng__N&->|OFYpb+FF+_PFo*}+$@okp@KOH?+PGEMR}Zk|_jCr*2)Sh+2kq_y zj?{ZT9{FM1<<8haPmq57W(qq7c3~u(LM7vUvc%HbQ26y*p6S`vNN?&=ITQ@v8;*L_ zcLXUr;A%61XU*3acn&vKMuz=Mu_QaL6rY$M!D_M z$>G_A%kf#vNBHA;n7+Ff^%-WgXi{*Fj{*?_2gTNle$RN1(5rUJx8MwkkUhO*(rLKW zm%8oSNt?m{02kkW%Btyn??XZS{KvclfMZehy}QCbsm$&7R^Ii#q^*8{eKxjLFBIXB za)6E?4g=Y2d-kanUCVC~7&&@vhFRswvaPu9uIYHp467_~`u77tjnWR9O-APU9o+93 z0=(Ld8}lVfLSmA6@_T_)yRcGm#X*MpBZ*~d<6M5p?vGW9eK zuR^Z^dP>9!;t)|_5;nBi-D69STz%#LKPv_MZCV56oIsfUeLRc&mxZbh*jPQdGpEZO zt25i8maf&<1Uv7Qpl`UPu3f4`7dD;Ej@vS+>GJL7P_QLmF@+FRW6_f)f_|Xw8v$Tupo46E@G%wZ`D6O4$!Xe%>uz9r*j!6L~;Dzmi?_) zu(hZn1j>w9EUP;g_;jLy)Sl(!1|4Rs=mY;=I@Wbzbm&O*N$d#)pQozY`2#szN@%^#~_aG_+ zMuqRpg9sXNX+hkQTpxEw0mk}*ooa637Wm@w*$kCDJG5>V->qM;Ml@SY*QAOki?3H# zG|5YOG{Wa=_2EI8zBlouajZocIn!vo!RvX|W|=gu$@25D?*QidAq@bp9+XcA?r)jW zWlJJn?v(EEsldwFel$e4+^UO}ufn2+p{(nYGwy>RcaW9H^UtF8q|{qVyX!is=2?oh zw7A*0|0+^RlwF8-dy8`)*iP~**gkuSxYiY#Uk0z0rtN})&Wy$L)+K4fl#O&w9Z#z_ ze7>@AYQ^3q7WEawVw$m63L62rb4rbfhFBTZcbS|=3ur5tqDbnzn?RSDC7#}wVB-Lp zEQV|XGdFv!$pk2v2VR{0B^17G_vzOCSnX_(t<9hJ4dWbK7JRsX&LdjLOvXd7b9#)7 zX(1^J4+l(xzgXOhrcDer+MNfC*TV#5gC2%5DpfIZUIU6tnk^hoN#HEcx#kZz_wp!y z@i7m!#@l*-h2b7&#SEVgHl?oo zqo_a8+xhm;h$e^+vEYbx*&Y%fsLyA;gG!C<3w`v&H-p8p*KuLcL$K@&+Rrq=w>lTn z1m^tya-qH%+m>r;Vqy@(8LpxrOTjHClAkIoD_o%++Wx|b*VxsLrs0IkJ|Bd4QCr=b ze}ZGm=}KY*;OGP?I5~%DAv9W?xfQT_;i$ey8FUoTf6L7oYdq#<26>=oG4cY7Bl)YN zIHRz4VtcBAz3hOyzdiKp!wsT|I>~n-s1GXFfHP{wlf`Cea2I;A|5(?3;@{!w5s_$R z!1CGVbc(cOuz7E#nu)6mtj|cwI%0D%0M}Tj3o@o8kD>c)oG1U5Mk%I%?gCHe!zE~y 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

}}Hfr-|m0)VNq&aSPy|QGQjROtVJU}VgJLDGL zY-f8r5i$d7P6vfSaYE|uuAxv!j1oG^ALhlJ!42}V&_+pCg(RW)2t-M;*mP!4&F9#l z0rx~NQORLi&GE43kjq`aS>^?1wne%Q6wxx%%i4y42w_Jr@G;BVxb4x2{mx}hF;Y0UH_ zGgx%gMyXXU{L~S#-wz-`IT2Y!PE5l-rGuH@eh^4@Chze^;|O99Nbt}BAt|`G-#TER zmoBW49(D}ABe$@Z#__|&ESm?nojA52#CJuDd0s+;lYOL|oCa+Q`2SH#H{a49WPLav z>IJR-H;#Z`mqEio(hYd@)mB--@^Ccd zv`>KMb0PdYJldM@GJo-vGV*WZcv|r4;zli?U7_oL49y8&7qAzfei@irSUG`9Vy4(B zu2fq3in&;ybOi=}*Hr(lz3RM;ewG7W9sp8h5}Ly4JgeJl0^Xtlx(-ErdLQ}ntG=C<-;SxcXImh z%TsdnX{D~os}1d`Eb~5<)A;Q&_IH}rw1N8{3i^hQ2BRZiEcHWd%iP_N=UpbRw*;r9 zzRJjI_UmFiY`uxlhf-M^1G}Y@aD6;!|HWlyifi4(WR!E&#qdEs=i3%>sFcE{r*Qn~ z%cx8<_?gU|0ZOWLPzCR)%yTvuj6m;RA%-cf zcS9MeF0evSlJx5<>AXTzkeq|N35GsaQyC_R7O9J7v(n~G6?&KHYnykHeD9;i4f$Yo zx(F6_b74b1AGa$kub9YsH5@!h)LDm(%-9$*k2@W#XceK}PwfOhQu@U1;DnZ!*z2LZ z`bW#05tc090RMk*-Y_?zlhx-H07Q1Kjn1aHCvlJz0b#?xHhqVO-xewhAW_9+qOX+x z_}56Wg32aUgLPnzBYzrkP%@#lY|&x$%RzN<(EMO_Q)613oa*uH@uza3c<&zDs9O+F zjq>|}_>xQU_T)*VO`dUQ@7LXMhO&ky_mkH5tY`VaXY0>;u=rXd6}lS>7+d-h?Naqf zs-H8u<*Dn9I!zQcewu&9n&y@;NG9KMFH~8T?l`<&uye1POIs3#E+_v*O1^x~I2I)y zY=6StJE{x&&X=&Pf@-zU@uDh)Wc-$)D&d@=%(PA~IBC&5XKZ2jOYj(Tm`xQLSD9B; z7U0kaJgYK+%{GOanSC3z&`JbKd(mqW|Gx=9P@XhKR_HWLUQzu+RN^0?GhuN zr*!a{rTn-3@@eugb^Az)Z-3*XA!k4XYHU-gGq-=Yd}>SHe2&_Db2DmG6Ml~JEtzIY zbt|TB06C(S)b}Ypw-Dx?9x>9)Bwj#B+k`bmTI7k8f*Jh!v`FKD%No4!-nte*7pLuEpmQ zXt0(X&h^Si5cWsha^#>Jqo~Ty+#jcFL0)&pi{IDc5ekeLQLvJO`3a|5z~zkD>P@E9 z0l5c@8ygo;mFmbp(or-pN;Q?;7CmqZM{k7@VK>DzrVR0_u@ zFy%7sNY07;0=_WdJ>f!_k@C|8O!oNFm8-{Rnovz@7qby1@LH!dlofgy)7!hR1;(Xe zB?Mks0qKzCf4kH>Wg%!_w-4xkT>GW|5gS~}9-3>>tB*HhiO-k~y)Fo#qtC^V&ionu^zpYZ-lC=Ms%PZgk zumv2FyLINXw!{4Z#wcb;Xn{wVm}P97PWu=X#-srNy@_1(qOf|K8ZNyJjC-`*luf5z zG2$q?W7^0G6JZud;LkihxZPPbO#n}qCN}u9fz*N26kZ`ZdkEI?jFTe?vYy7x#@eu-n@JXj= z!UC=%k!{E=zAnlA6O(sZWsXQ?oSngIHJ*XVg3TJjf1CP%EV8TeNH+HVY_d|X8p|&C z8g!}Z%=s&fR^f(GdMthXMcB@r!T z+$;%BkIRU7@Ly`gN z;4re9*4@9YR#o9*NGWzQ`qSW-RfBp?nJn|QF6mcyy}QNYBbAf~(oA~1&9whVPBDu( z)wrD=-*ppatILbkkGb;CUh0Q2r^eJN7u73cFu(-@nv?w z%3d};Znk*V)M^a87${yKp4nhEj08})xUED}4IKbvMbC%1tGJ1?TPBVs>SU_1KHoQ= z(jT(62tUCqZO~|XeD2NH#-TaK)EpBE-b(GklQ-{1jp^^<7S)9b7L_ul&M?U3t{rm& zE$OFGmKKjOqv1N=?uC&$)SUDt=L3E5iVoB<|B;s?W)j0|NR0*NT>{GUiy%8~^M63jvU z?iKN?3!d0dD&zvCOd8DI}ckH@bS*Fl;q3`v*BeSSv*C}FS*;Yo}AdRzsnZ%`o ze}wYet<__l?`oDzu2%t(*I<#)?8Zfu0kpGJQ$X^MY( zczI0ZTFA-=uhT6^P0XVt?m))h#6$f`t|EZ@H3p2+p+?90gDlImu)G_%Z~%e;;MN=o z{0c!SV*2R{08X@K;RxX*WT^7=I2t$o3MMr)S(mT)u?Z_Cc56Q*)hIA>ERrHZkQEu4 zhJfneiBi9*>@=x-di8BIGTJ_Jl}HQbt>qb`p7w5)!iY@oX}g+PM%MizAk`2p@5Se^apNyUni>#ndX5gKAAAENTK zuXxQ1O^Nr)5v6$kgqLcrWChstJmbe|dl1oJ+n)wrG%}(dtmJ_*GW_m=5P6+-Q;l$> ztboy)j;DE+V2Go6(c}s5;rF0`H6bXLIM6HKZzZwepS_1HX5Jwu#2ANM^)em_IJYZz zLQxCK0LX6hHsR03ak4M0=}sG?sWsilNw?#!o+j!Ck{T=_8WO%~B`v}f>2(kmZG;HE zqk3M!L$Dx7lK@2YlD3pLZ`dRMdi?W>Er4eAR;x(NqcK3YBGd>y_P==u=`oQ_{W@Xo z$40gvPYr_`e_(kmz_y$)b)0+!59sEr!F;FKQ;3(ZinA|07C82dK0k=?Li5`9in2IHM~gx!{T1nCJU zIdlm4brGJWt-ci=spU}t^~gN+P6mc_S=khdUg!PZ&1I!xpmGaEmQf2*L^7w6nAH8( zNE5OAkraa7)cul2ufMx}@hRNt2oTb7=Aq+C-DoAThhzMm-VMSC9>#vP8ulPV}gL9>itJHe!qj+uXy_B@QqFoTJcZ5&FWr64H^ zy9=#_;{1>9Z`9mnS8&;7MperRI{F$3(h)RM$eGBU7kwLtSr?JS2a|`^Mk*5S6ChzB zUkV=4ATcDh3#nl5nrQr1nd%;`b_%oD;T4#g`OV4xlI^{t2hU_fGo21s_d&`ev!?M^ z(Qdu87@`jlDH2TE!Pbhno}I-$F$@N>WOWJ#WW2on|3)OQ`u2=zjNi%Q?^#3C^Rg3N zIvs3q#NCt5yCzVhjJ>HkjBz^UJ*$SKkdI@v9@e0iEvs~jQl#O}cu8@rV5vYLPWA26 z8qD?ko(K6L%4(%&Fn=xr%}D~-V}yco)Aa+5XL$h8TH7$YCA+{pNI*JKef-3$<Uba; zf1A?Ml+t}>X_g8Q78c#-LK?i#S3^SFCN6h^5-&l(VHt9=T0KNn`*L%Xya!;tfwZ5^ zZk^|{_-kbZl=Ms#UyW8<+gLqFzYU^1X|0OH%0Cr@nyK>8ae zk94du4P4AR`b2i_r(Yh!xiif~_p>DFsZj&2dwGTldoSD5b?aHb{fL7(&!W{zG&^wv z+6ZI=YA1d6y2MGgV)RoPzkF-vojN>$Fb43wn2%S{XRX+|5swstIC!T3*|C}&6sd%` zYO-*@N-IxTsas!7U2*a$z&`lUM=~4)Eaotl#VL_z7LF52gqo>!OeJLSu+o2e8AYyW znBcIO`vtv>zK`PLyjnwwkOR&LmRxfq(JxJ&etJNL8i6av&L{VOiB zFpFa9ra9&y2TzWADk&ewpK3$q^(^>($7SNWHIMq+niAQ5HakiE)NQ*wBz;p(KT7=t zzIXUuw)CaM0z)pYl?Q#9fY{8NTOyF&WOm6+k@q1_R|z*Bxo>|$>{`^+D#(IMI{E}` zb**RB#V58&;EY}V6-h_frTNFsQ+o5kc-2CXwQUFd>0wFTC^U%5 z=3Y5XnvF|A**~(&oAYFgSuY zUmhPV=F6wVu4Uy*UGkm({bT!6eYQbWXi(L=OL92D0QLdsv6S7!iNgD%l5^^kbfX-HYFm`{q>} zR;|Ts&;57`bXl2C2}?)5ZVy2= z9f^P{QL`P7fch_Gn~lX$tL4chZJE^V11ve`p7A!QAdcL)t*d80SVJ z_6Ilw&_d!$O7)l3=i5a+U|uGuRR@m+8LpD(cgmg(PSrdTYDpx!5L`tAI8HiAXWR^~ zFSv_FFf>4>PyMIm+hUG`V*mK3A%}Qz^xf^rR+W+$H>s?Iy)aiao|NI%=|*_Or39fs660ei zz>^pddzt|#zYS)LwWE$W({}k9muMnbt=ikJtAJ zd?`2?*^-NXb%`ijmz*NKrtsdH9}1NXP%|cOHN(mBa&@0lOxz~)t-Rool~-506^iGC zNP6zZE1%|)>@#i9^PtYk&+PHw2Xnq=gR;+XBMJ<7s2!S)@dpfUs1qocVLHD2#)0o) zF(WX<<5*~mp_yNs!ZB@cV~T8RJ%UCOmKNfN#06duJO{vVX9`^D?wU)tiWp#e?#UVv zMJ_Nz0ksVVda(&i5E9rP6>AU63xy4~xI+9VZU(^t8+G%uz98hx5}F=g)0I#^t_uu8 znQs9yrs_ykK~aoG*O$NdhsrsQ06%`)w8@XZZzL-a!D2;On^|3{a@ckTe!rSqWNXY|b%zi^(G`gH z_g$$z;`Y6r&KhK&1sS_YB*9%BgNi_!t^%r9cHmq6!gbM*+q=yfIT)!(j_|M}bSM0< zDE6I2kILpb!86VXa>^-23*S+UDzGAYY z-4uFshxv`{1P~WDK0^f!+<7*A0)VR~KYt#${s0?QX-J+ZVWF;+;^gC@F)vXE+E*$S z2QZzjvRb-~CfGChVfN80v3%4}li-nt#^Zmy%f2gI&eVo(VL|LxF5(i1ms6PZtCIq- zWmWx%fci_vBjTao&l$u3Z%y14iYt(?QDbGY$4ynDz1%PlKQAfKtYA%AX@0nr) zbaxT6OMm;4BSgtL^r>D4*ttqdnUQd2bgxbf(n4hB7Z`C0>~^ltJwe{Kr*?O?Wmn#UkxIZsOihtuM~Fo| zt1SGj9dE<0GVlx3i3hIH@!3JlxI7+cDFd~{oakgmy=?=|tgg%O{IZxOA~$>!{u1l@ zP?K)Tv+C0kZc52Fw}&FC_#1QVCE|Qy2eQ48^TmATe9Wu7%=Z-}hZR;ewVEJespPn7j zjM9cF&K#BVWOw}+1UA)5;MNUZu>iNN|H<<5qtIhP#b@Oj?x#gt{IiG+(t56!_E#$tN7!TX{iyTp)gl*%yCL4u|B<&vP#2;5FbAjg{=;SS zo$ywRymiB`k=i4UJ1D@LS@-2%gw8v0(TC0H%H1KUvm(mFT z2>lNf(lak+Vm>vtsBgon1`@LhCU4FF?lj0bh9(V8>Ge$GeLb|E{_Gj`Nt3GN*i7{X za?oWizo=pyPqWzPyYY}W!Sv~ew6~J6nfgm*ctfc&Y}~VdXs>(GVs(JCn_iRa_p5wD zq2EWXM^U;T^Lbzf_jrYgc;S6WFGwaY(kk({Dre7Z=M@?4G#p}xvpsZzP9#Uz8>%f( z=3uAis)SV{Kn?}P(36YNbSnqOu>FlL0@$9`1m;Z;*x>DHFe;+I zb-bGqVgou-WGXk~wtm$aEbIORrYAPtc(@&WY3T^%BJ9=)=$oFXk;Sc8Y8=rD`M?X# zS)9i>)=)vQ>)qL~>qG4-bMsx%^n$+3@OdL|f{b*|5$v1V}N|T9pe4Rtek9Z2Bhl;~zhF3z<;{C-N=vUkKXUH^+AS?_@Q3ekn?Dj2_RdGAyuQ8?fuyQ39 z**zM)mD+tS&4L@Oeo+J_ii)%X$j>p41?I+O;2) zVBhf%s?eEn{NC;_(faqLpYcrJ11uf}~c+fu3r3u*`;HXWe+qdT=v$GSinnFlA%RYCEAQ{v%slwu(%RRO6dr7h90>}HP z8AcvmP0l0bp;m!k10H7JM@B{hLgrhtEWpi*6K^}K-2*t@F45LwiX9ECmfF*&5#T_- zb=43|C%iD|B!QfcwSRf!V{kVEA_L(*x7_W?96BFycx0k9lqt~FnElJ5dW_@)^U2!G zN!eMSg!F@k5l2eyQa6qX{)busaLZ$*2bV96B*8aaF4wmc{bEs9@FVk?$!*A1eZKX- zbxcw0h}3YFbGDpyM!tk6YzCLrR+bQe-@d}%Bqg0Er2#P;?ew^|jTzWIg-dB@7Uny!Z+%x(jJKqcLu=>hIU*%wKJQT*_3(aYNTI5Q39L-k*{jQ3Giq zoC7fCT9(S{YzCLnn(U;F$F^gSjs8w9z${#Bvj&DI;cl3xvHZifbE`9$`mt5J%Ec!C z#am>gIVq;^q=0cXc9|eYVi;m3V?jf9+5F&KAaNNE>6v?wvTVTU)p?U=dm-Z<|ByYJ zgD5QKI{{(W(nnarbvKtND)wd!@zQX!CveyPYHzO=cQ>-daYaUkjOmDh-}h4yzX)rS z>YL%0L4^qEiFVG@AgDhD8SXEWZCh%*%|^qSxvTmrqfj_Oo>sG7GQpw)V(FhWG8wcai-z7w_ zenU{#xo3yMZpC>qk;feXs@M-Bbcu|-Y53*17+_(RX{MqF+R|Sn;xFB3Gw`|8XXIn| zWE>oBvA{QnoT>o9OMP$TAz0g)uVbr zfL5tr{q3rX@tvrc=;T5Ir5VLKC^bSd_6ZJ?gqSvo_lnwSRJA@mEP=5RA<`))TZwqC z^(q%>>p=2CX@FO4qb0viHL*GIhvDSDF*)X#O}F(@`^5@z#s%f@Q>+%4GYbQDo;>f2 zy+4EKwix67od;^)8!6E3E%Rec6gxE~>`pZ~@r>76(T%53Ba7yFV`7W%e>ZZ-@ycCv zr;s?xfP>_S2Th(I??}6G4qSMVP(u19WI^_sRoC&IK@$5%N4eSspS0ZzSMft2YfuJa zC|Q%@TIJWYRTDzAm)kqX@~w=0OJF*jFwr*b|1%v2OMKOgmN#F#6=?t45Kg?7kX8Ym znPHFP1;SNRTgYK(fEnZm*$+SrXXiypvBwvVu#uo!7gy?nA8*i}41X!!e~gGo`Wm)0 z;*KtT-LqL#t3InpZCp79)98m01;TeL>PH}xy!QCOgN9w5)g~W z#S|~uX4YJwI4u80heA07W90)>NKbqdJeS4vq^@b8Rw!tRPUs@$|5yX@&+(iEa1zER z6y~*vmSo%Yy=G_nr1%DrfD6BAMa$Jq=?XWE^(>YZv|}#`iGYtl@PtP@`E~S_w~JWZ zx}Tohs2j;;$g?6^%nEs7{TPnBqm7T-`j+!_(kld&RDc)Pd^TJLQjk+68F=vZHm_t} z_Vsm8yh>g$!(gn&vOmeFl?t~qBf@s-c+*lHG00E{p;<;l@_&zB)H;hF>SERx<_aNSevv;M3B=|CCi%fL0!{; zRpdD%wy<45cjMoEsrAu(~wVfxBzbWXx`b;&`xT>;i{5`UOO{ z2Y%y&Uf&)TZ`K#s5Mjq`jqhJe6|80x6&8831f4%t_Zl6Z0?TLy`theNLnp%nIM*Nxw244_{dNJ!(L(H$bIh-Pt@U}8 zLZ7gk_A9Qxu5#p-m##2$dWskPh-O}mX0()Z&2dK3EW zEfq=eJ$XUYDygQt%FASB!tmCe0C~NXsKyiK1@yhrJUc~&^Z#=K@$%d1Q+ynyK(MuN zpJJZ%#$DWA9lM$iJoMXzgATV@S(;p!I*629& z2ntqj(Y!9R2+XdB0FJXU*W(uniBc5jf>)YV#T=vQh~pwKmCzuJVe4^{iwI5dMnItn z`e(UTa@o^SzhpAtNO`e4H15&-3JHo4x1|$uW2?xXY{s$LBh7@bn{`^}6^WhPJk^~f z1ImIx;4xCwvp+|1@I-c9r$YxqnQN(x26#xqREzKE330Y0b3Ji-nuKW(29EQ`Fi@;T zo$x#2A@nnVg(D#YfrUUV9>IOW&a0>B*J#;W6mrPVTWtKk9>=-|G6-LJEJ)L|p3c24DfGc7wR-Giwgr*Q${(|X(Y>}B*dpQrTPrVm z(3Mk51m>oqPYv;BwJyHw5%``ncJKqY4AVd3+*^~Y)N=U8czs140MRw@-weXtO?^PZ z4{M;R3haJHf2UE^U*CM`CpeO3tinPlq1o=gNOx#p10U!FZ0{7@_x7c%yFjlZ&QCIT zW5=uT9b`4|#sFF0EI}aOeOvK+3;fx&OYe*kLwJs0<*lpTmkK zl%{+tlCWs&&b4ABLTN6pVRc18?~$N7SkE{vl}kwI+9!q;xIA)Py*H}aIN9e)y&FCY z)3Sd^VPnFZvCl8bSbov*?Ufu_oY)J6Oa-$0fNeTfWI`In*e2K?+ZD=YH&c*sK7*QR zzU7k=G}CA*D7*V@h#vI~bB?E9oHV1Rdy*eG0~612gW9SPvSR(Ox0ooMF#d=u1d$*= zG+o6Pka3T)26+mXryN~j%XcMsod>1W&bU;rLDEe-&1>T_uPQA_@7 z^JKcc{EU714L%X(t)p%y5`(=vjrmk9q?f+e)|`o>3}h!z3v&N=jw`UgzbQOTLIi`Z zXrd*6sFpH{l9pc7nv{SFjQ{(I$Di{ZBa#)51gWt|8Le5-Go8#Qe$gmoHDe&M_iIj2 z=khd6OK|9Xj~G6Jrh(fzVR---FF_M13=#wZ^-V&~;nl-m!h;NkvE5FS#f7nCXRzpv z4;}QcphX$fK$9zd(*H*Kb-H-HQEketKL2sdJ7fBDvd7%rfG`h} z+t3m|;6Gf!>?#u?#ZTJwOgnwE=-OGnrK?;zZ~v_Ce2vN+&|i_ERIDYQos~cv z3APZ9cIU`>xwF>68$l3cKxI#HA*c|ow+07r8%-W2*T%Xjuy!~S^^Ce8wG1QdAd$D9 zm&?AT^v-OI-{dLx(W+}5pah9lp#BV|Gr^l6bsh#LSx1)!W%;tM??pZviaxrQgx;c$ zbX#C?sQ$v6JWOh*+Xj1S-=UMq$Np{&-AA&xmSw#|odW${FBRmbZ%|7*oHPam)P;Hu zlY3=t8SCs56YU#INE|Jpn80=GU7uwev%k}+S7pQ~GS%mwtTtG$&CO&{H*B)4n4eX0 z5pYiJ8Pn@l9O}()&qcM=T@|EI&{)7V_-+~mXumgtoBbx3b_o=09YPzKdxRfz$w0$l zFFQGIc~}F>dmw0M2V>c6>phM6u=IC=2NP33LXbjx&`Q-1qE(5)^XI!M3Mh90lmEy} zirS{_Y+|;EdIvPSzzp<2Q-;w%9@KijFg(opM)4p}#Fz>fGa$Fu9LHsMVj%=T^;Ui) zpU)!BRbmgQOrZe+7v$zte`+ARaQ{Q;ySpPwe+UA2Ur^LZiz^nY0!JpM9riC(i>NyNA75p}% zR@UXW&Gk=y9BSn1StZ(OY#-;z;?w&JWkHvYO8uz&zd}o}-dI8%WmsJOXPiF?2p&I} z@#$)OUXBwUYN*%ZW0g7`?8$8;eP(WMlyHh3C51xxCnW#5ZXE== zo;IRAnf>aSy)>ohM-7(0Bp@L5$PSkc$?_zL()HJ7V+R)#{9 zmM(jsKh^mlmW@16Jhq;6afAa%-UQRJ=k<#$OFwLwP*v4%fcO1!F0mrFm)_GRlfHtn~TSxmoDbFrf!?XsC3aO~eW%mizL-Y%8hRRf`bQGNI*Sx`AL`*F# z0;y`}@IkDuHwT`~_9ROJdoaCgdd6jX_cwVFh@I%l}j|_-AtXZHL zcnxl~{6%L5ol=D=uZL=Dz~2y7UyuYQ=f>?*$2JY;OFyVN=f&!}O^GZJ z{z>7A){j!ET{CJYEv8pMnpm;18zI|y6C0S|rapY+vTiOvGk3s?fC(-l-Icd>ov0KQ zK&#eB2UdzjC#*~4ZYl)%(I4XCR!vCZ3-eQBE!F~NaM6r%BfbKL$R3kWFL0MbZ+QM} zQ$oPZ)G{_{b0r7dd6}Gxh?9(Go701FA|k)edc;}cu~iqS&~b=E6F1jWV7qJclXzRscs$2pVzMDd2NsR=>I1H0Lyup6Tky-ItP;7B%#ZS4c&!S$ zGbl<1dB`Si2q9(q5E8(U+-Lb8`r!+y)wMr$iQ*J5U4K)8RJ=D&=_UIav5KG&Iitj* zdJujpCmCxwUwq`*s$3*Ys4`3ZC0^`-Zp$##J{<0N1r0)&1qi~0e~E7RxYQ&Sv=uyh zfScLhfBB?(iD~*zXntbWi}V*~^Q^g;V8fzb{Mndv;_2rtOMeBDq9gShnjP1S_*yA2 zy93ZF!4}J-qq^Tuy1U%y#gIsdn|gifIU6^f{1==3zg5SZ?ryn$nn3TmjciRt`B~i-LFCyb8ngwIBgBlkKqn_4mdX>_u<# zU^cA95Gb&Fo>QaM;4rMt#N0eo#aDIAp#QSGQPpQ(9v-d4dCzy@&F^qej z(X3FpB$OTHoletjpk&@zz=6y#Ek|Ae#C#prA(c5sE@kIv#={MtQEhkxn7?>hLmq)+ zdCwDzMLXL50f4vl!aGZSo{p}Yb!4{dka4B)9H2ZY@r6(0>D#H)`tz-#Lmtbr*HFr& z0k#2$Q{G2Nc5Vy6V!{gMd^k&<);6c$Ac_|I9Qu47D|L9q6gquf27s@8dfSw-E^*4hz<6DHx4^dc%k?_mnD14BCR~aypq?DPvuAR5#r$ zo|{IBxd7H`#HmXD%`4qo(n95uyyTY6%Q^-;R75t1sMS6kW5`h26%l;&OeKRPN^3v6cE(Z!4wU&c{wl?V8$6u z-~}!-v1tOM(zh>>gzi2mDVc&)i9?-_28tMUH(6Eg_DHH%6A#GTc;s@%(yHPW+66Sd z%J!)d;5Ove7waNcDOH7f(I-Vx;g)~3Oii}bgzN+(_?LU-^MAnmwM!&n3cOH^mz(Er zE}qu%K+FbxbdD8OZnLSmySCNX&E#u z`=0?Xxw*yYO#{MTK_y+X^i0s>xp!_I=-SAj(+N zO9__%B{3z4;h42@(B_Rs+R)|S0hiH=QHK*#QC|00n3c**`vkzKXre$6OSsjmSAs5J_eM8V40?JKVNt%xQ;U>H3fgD1PbKAWvC<9Tr~IX(qOV~29qUVIY9gq7=#LELzQAJFZ`13 z-&^L2X@;Iq`0YWF&16{_u|V~K(Q^Xmae^f#+{UK@61L{@>9u$IGI3Suy91N6cO@%z z7nJk64J`p5j2&eR{ji55SrBK^7WAcE0#`1#$W08SE7A9$z9H#n0uPg}KDco!u4L=_ z>AAOaqR^4=i$4v4Bxs5qE05xExs}HTsptxTt`3wmKSLbnjdR!%9T%1bGyMa3^)#IC zf2)FZ@}A;bCly(HR`j6Gtzgm3gOL3}`~D?dgv@u%RR&xKsJcSZI1_1X*5w(qs8SCP z;q{Q%xx{ksb~Sa-=yaVRiuGF_!>q@7LtP@D>pY2QaBzMs?}Dxn@(-|?Ty+qo{0uEP z)glW9!7i=f_Jhz^nas}z{zQe^6CqkX;GGIf?Q%p-UFi1RaM*5qp{+HS(g`-NWGTcsSQzw|N`&sI`V=QWU?FFTBh^&e@@*RYWV3cWmClteQMNQ(Mk?0g zPGJ|{K|=CHrtGmJl&<;`mxf`gL_3iLrX?en4sK+eAdqMtPJVegBW$;j0mEOFIBUldme`jvj z{U@RFghb@oh1;SKB~*-IpV^3u5+pyC8%ykJ4qtPjQtgZfne8GBo6Q?+HMk`Mdz)%n} z5k5S^xLmh-%5cwNVG=e&h4}l@MPP+Te?WA*)&uv<9=A=b6Vxc}m}Iz1`?rw_*a5+{ z5*S>_{oN<+698Q$^{}NhQWRdTQ!?0AWq~E?r*qa+{Pm(41t|V!%E2V5_*`k0p%#7T zc@R_)<8P1#kz}FhH1r|gAnUTAJ`GJ$x@Npg1VK@FKt^Wi3-JRQpVE#8J+|iI*4$%%F0K?BP~%AC>V}c6Etl{YM&_43 zHY6$3EAzfSo4u)_IUxIc-vGHOELXLlPOOm!h2B-G3Rw{8>BdC{R=bA*Ky?1It9ud1 zlOyXPXX?Nkys{Ls_r&>T(T0X_@yI-RXHposE~(cI^`3u7;BqLC^@~f6SGzU}O^Uos z^fP?Jp4i|;TYOovh)rcg(mwS^j^UIWlDgnL&Ds5W1FqH45r=x}j?}HgQ-}%UsT?zzlPrFU$z{tXSFGk})e>s08^%r%w zk4Y?KLo1@MD&}{nFfh+Kj-q}DY#V+9e3+C}VWJGEY0~ws?ojb^8mP3ZNipe`crRH5 zZw1;Mf`eDB&m^RX6$J&im3R&w^f2Yr!V@PEV97eV^#WY42G>K)kjb;bz6|N`Q(_DC zqR?&zm%CQ`8-=8?bA_G6r?wXziu|yI>%xnfek8v6Z%}8yLgQ}mJ!n0K6N4iDdP{=E zK87O2Z1usoDG#&|q+HmAKg6X9MYG6P59ckO*cqSIlNt|THBN_14w6F(n@vaN6FHaq z>b1J-ZkkdOj86rZI09Y!FIpA;RPki>V9N3p$}1AA#lkl2#1pZ6wd|DDyRs4}byQH%cZ@A` zGmjjEH)$v1c&xg(N$@Ram>T4%g2K(IL9W@c3{($B3s-gi}n% zUTTTyXJx9h@U7N2m|DSaX~)zs>T~&w>TQU$eG~d;aptSXQdjQ}UKpD&y-lvwh+NP< z5A>jtwTtgEZu*bBV5h1Y3hg4vh4vB@(dsNp6_68_D&;mQlkQ3qUH8S+S0Amzn4Frl zFT33iD$1Ly@#nUl*O`O`TJkQPNa{p3tGydOpg`+gx`R_j&LfpMTw%Tx+~PMPjsRtU zd3YY`0}=qY1uKfz_>vtj8XLV>)Bfy4!!pkT9QwJPLWuUrp2$;8WwS)$SNJiboFzk2 z|0xTdHiZ@RDnUb=(y-u~lt)7w&N_H8dnY8dbl=XVNv)|QTg|f6znL0V34pO=CBD-; zESw)D2N%E+d71fmcq%~5m<-l4iB!u!rE`a%80KcIZN;JdB(dMDh({;WZtpDX= zTY{p&kO7_7ShENsrMnA(sXi1d9g~Q%SW@O=4Ch=hx@ELwiOJdELy)eoPKL&O{D;{* z$o6`iIm3F|u578iiTxH#*U72zfILdFE5>r11!y)*oeY4I8rJpbFlM|iyo+T|VLg4C z13O_47Te@^% z#W^Ft-P=R5{z}rtAC_heFSViX=fc8sNRIC;=|Rd}y61Q2y-8i=&_?j~f@I3ae_gc( z>oB}FUl%N9Yln8}?PL(bWeR#wIjPM%vgPYpmBh$#MQF=eKqC+PaBJphQ~BZenc>S# zH2}S#KsjIoiuC+n*`?o)zbruy@a#lE{}3}N>sngdE#H`z%Y|dJ?5!as&17V7J|bWU zsJ-Dm`|00WV5a0f)NI$UYrOvyl2mQ%ms(VLi z-03`M7T{Ra|7dEs#A!UVIF8IVa?T{!>KG|T&zeUnim^d^iPDH(6^RI&`GA$Z;og)u z8#Mtuxg|!GMHpoz&jc#CkuG)34q35$F`v$KLM|GzA=cqZ8NzK^Gl)WRaP3^@iXUU+ z5o_pB(+wsY-2UibUW_2$`(?&Nlo(99@fKQu^7LNZn@%XC==;QtPU*#9mD1Jgt8^ZT zVK6hi+q7Y?$5NmSlA1FMVA4#W`8_B7Rr2rx>*-(-$>80P2OwM@7wh0UGx%vEaY&G+ zgzci(FsYm9K(nZt!5@Z<#jheJ#1+nII*y)lwW&g9iX-}c79e4}A*|6%M(no5NETd(PDExx| z*R6QramG2*^2qvm{UZ(fyF97319yB(t@q<@f-sU*t%5-Cv-){K+=wO57O1!nFu^63 z&C^%!C64@-s%pYC2~rxvn{Zci9x^G(=H^N95-uz=fayli^6Sbui)EXQc#8PTcLVQ7CE5ZpHb%7@H$iQD1D$ z95HC(M7CuJOo`21zMER~WENMNZ#K{OY4%mNfgmo`Ep`nRD)YR+N7F!L;%Nq=mR3g? zv4&sdl%#(?rmLaf%MyR~~JPqf2A z<*E7wOol&nx9b?56G<*`so;a1(kZE3ykBwepa<&mVe?$^^po!=#Hv{T&$uZK^sjff=nSdW#O znKzlsD5cISRq+QDK4LJP!SJ{uz<3c`sfzVvWJD!M9a+GST`L(-A2`(60wPohKMv=U z^w~pof&YFkmNbJI7_h<|E)*?OvGJ=a!#GNJT^R#mrm$hM9&WDtIG*l$*fL5W7OPWy zx{6eQUDSk#7`IZ=?!D(ggW}V2zq-Iz~ayE z@)wHLsQxOp-iJB&|v)H zWUC%^oe?B7VtHgUeJ|!X^1*s|D*rkNIJhW#y>tk82i7Dsp7k*G2S4&@mlS@J6lJ9Z zm4*1+^9d|(u$)&VUF%D3cAwUR4e}G$qMzf4(x^MfqkO?NNt0%f*|YBJKT-C@_IkM- z!>6p+hcn$ox#%*Vkc-4P``z;Mz^RRcwEu{xwOyT{!u%6`FxdOHo3+pIVM{uoZ1F3?O<$il+=Z4*8uBkp&oqOb}P=( zits9ieKWu(YhwiToCUtTH#@LD%6aTmnz}#^NDajAKWgrR<=E$YIPBufw+zc81%fF) zpT1_4N6Znz(MAefl$wtN9=~ZYDlu*gpm}zhSl}*q>m*mi2meWhL~U-9{Bmg-$Yiyl zwt=k<8z#|2;--QXQn-H<%Mi`C6|fl~>~bY=G^6DF$nB|tOkiS z1WdO(`5`dC5`M~eBO-8T(gQbwBbgwdO4K~0JlC4ZgySJvgxnS9;bSI-%~)Xp4cD>i zCEi;LB2L)?^Df@Sv>Mrl48Od<-->c9BTwm;_oBAC4fM_swQT(>NM5>A6;CnyJI{O% zO42aInKjVa@pFg@?)7ok)e+w3Wd-y^mR0RXYvtGmk3PFzSqfB~ z66smv8;*Z}%1buK zp4ET6u1=u3`@M0g*L3?n-0-Sl?44D!mDjuaw_>ehjLWQt{L-K3l>?ypLR!h^Ve1$T zNYuJNN`37n`Lgz1qQ(nL@7U9VzjF(`BpAsE@m#=kLAvEWQodB|j`0o{m~+|HIEMc& zGwop>ROM~f<(2T2T@-oB$5A}!&pesi&-wbH5~BOsVT`!qVRxyfL?>k?oQiVjj$EGp z-dVuY(AX@WOlgNETrD%4y+3m!Yq7xZOz&_8ovyw=r)FC%98>UM!yQcX7{`%PUI@dH zSZkFYQL+c2_}GeyGj!5~c~A`wLxJq=PVgQGjjw1hdL@x@!2LNmz%9^oe~>-ZTgV0k zJP$o;7B9}kS%h6VdafP)XSW_=_NldQcopKxL4v020qH?$Bc%G@pg(Y>*E*%&^?qmBpo6f55i~UM!5fE3)sw(u&|5+ev zN!n;~@1`5T31ot}jaI+hus=;96PB5@tIvsq+ursuOAMu#@%js3Do&uo_2=oLNH%-@ zh%x1%hw7o-<}S9Hz@WF{CQQd7t+^=uc3g$Dd_(MhVL^B|pOURD?ma0*@;bj;>!pd< zdk14R7mm5&|2ZP~{y=KyI!XN`j*TEmyXRsoMnj4<#V#}*n?{!y2_0DiNeM6cC`xOD zOxE-M8|>s&BHs;D*inRLkkVn{W3mhGrW1W}%;7`Gi?#6;+Ue!-qbn_V@3p8{$7~S+;OTcgoxq@S9DM@}Pf_=fXi%HP#eTR4Z@7ExEVSo{d zqW$q;k4qnGfP~6dKpV3PM+eT?7g!-0ECeIxnPijhkV;UG1 ze_|d;P;ilOrL%M6`EXuozp-aYXpaP&sN4nCiZUGNyQNAvzM3M*OWy_ksq>E*t8FAP zp~pN=e1LlrgxRpt?0*_34-(VLXo?wxw$bw0iZCS#xteJh)|wrVQgRQdAD5naCBOsV6VdyU0V(YeEt=C+ArTl0_u8~+r6*h4B&ub znC!Y!D)J16FIE+E5MIJn*Ai?6awDB>Y)aU6#OO88atV|U>@Ka$_^HZXv?i{Iwo6 zf>CgN)fYsOpKe9X8%_lyN_FMDU3H+NthmZs5pPW)no4*Cc^*JJ&BdPXy}_5DhKqA_ zeMaJLzyCaeUH|kZm5!YF4|LfP?Dj^W6JbNQV zhla1+|jC!vTrJccE1?T(u80QNYCu zF(qO__5Rja;CB?Az4lA-h`IFaelk=htN5pJ2(wCb(|6@})ad2)6?;7J7WW`rLnI(T zSR8~~QPk4c%_X6=tMWH3mnKl4%ITIc6s)fCC+hOi=j=Q|rAULkYR=U4Y@5FZqC_^_{JIJQUOn* z{Zpi23yR79Q>xM4ktBE-yOQ4JqvVu4LM&!b6Ia;)Caq60aFrJ5pE@z!Np3WZ{2IZ?Rv z*65+Ghry2~%&FzNwleThn5TKke&4KtegIeFd^(hP1eekE);C6(J|zYNpk(Zh`$#`8 zYg|w{vD4*3R*~E6P0>+=$XtryW`W+WJJ)l~C^yB zurV~nJ#55(%Azod1MbB@wq!z;hh69*FSbubE@&ZfH#!MLTMDmaP(@+61OsSKyS#c= zc*t5bEf5$!22VxU-XhVOgip4AcnGyQRvGtN)<|iX6w|-<`9>8ej{uoE#H?VLN$bvO z57*HqRGU=%1P7UCmse8CM6`ViOoADT6PiDrk1oDBNs^LW^or>jyR^JtRj&TUS=URZZ!1F-;`% zTDQ-M*F-e}9f*S~RqP;ArE9KNTB>oZM3tW0o6b!wiOjaq3g5p9-wy5U0JpShwfF9>{zFfSaKS0{aC_0U}gbT7YBSYcQ`E!*A!p=EzQmKOi1!NE|DfZWRH1 z)M^5g=b4|hwmxAqLWl4&QHhc#wq~^IisZ)<_Wgy4193yn>0EK4{QTb{pH;O(&VDXpH)139q6 z|MsQ@@k*sx?co^HpE)z+C8B#b;@n3GM$sw%HRs*oWFriI=g;h_1sU!gIjyhJr?QxR zWDySss$Toq$NOhLk<#f*ukrR_Lr0Q{8fFI8y@A%oQO7{B1{MnGBMO`J zQ>zr9B5a=pbU;MSz3wJW1#3(NZMo!Xj}$R#q~z+txV|bp%P6MmR@QmBg^S#W*y+9} zC7OX;!<ScP(%7{MTSWGRT%45T?`nUjO7z zhq2;UUuQ+%!;pNMAkKuNP|tkB%B_;)XHXHnd5BTox|t6&r!O$9!Rxf+q8)p`)yMl3 z-DYa}<^IRh)lfS`^T}JCs;SEB6gj7`{0rxX<;|w&TK4#O%6p*(>(qjO-E!iI`#5|t zjOnjww{<#C-@ArTjyRM`BQw&0Sh?irdS?~a&+!U#e@&{PXz?S^RxYYJ2R;=zg?8%K z52645KDxno(toY^nR-wg7uHRwD%DkCoxi!3JOb+I5@IUYYVU_an9Uk!;2gN!`-mS} z2L)ckq$jp(8@VVMxX}@oJMgwd?MhcXTPzPgiSj`usr6Q)o6#Nuj$e%4LQ0bFw+ivF zuJU1%1G%}Mem2~~BHF#l<5}$aZJ$smp`4Vfi~Zw){yT%lgN!V3w0D3s!H{8gxLEaG zzM|v@GOs<7fH;*;oLU#d@gQGDuam7y+RnxW6Xqdl`5toZ=WNoj&!n3$*ejw6yz4e* zN~+7!7sJS}hKn%7&%jG0nfgDar`PAm;kU8AE~4?9cmi8mv@}1Xz8^Z?A5(sXz<(I& zc=kOA&>$giF8}WF@brO+ow>kg0(P$8m!xGBIF{}ZWx>GZg5@z4WqWBknC6uv$(Tzs z(V>R@7M3Iir{n?1btv3YE)Qw1ZMyFvcfYG34@e+`{I^gs~OM+NT`PDeg|GqFY zR*>558J0YvJN;fOiD5)sLJy8h{OCE28uPpe>Jzn!M|BW1o#z3?q|rdUZe_oM>yK1a^SIGt1E^ zz~v7*io{O{!;~5vqebm_lwr#UI41FNm~b45focMiP|w+QRT#3KO3%F(psdPD@RpWG zCIQW6$dfR2{p1JEr;&bUBH3J zA#=D9?_O4B+`j^(B^S%I zk4J1L9(re;;aw+xPbA{|s^XzVI^#~!c07Tri=8AF+J1!@ZOC6E7}_MG*VKDB!+4EQ zt}$KC0!%8$U^gnJ9N~&MHdFv5mB1EwW)Zn-j=c?01}H%5tb}kZH@JDE9x8xwR3&%z ze@*vKE*DS4NlqZsCL(*HI;V%T=c%NUwjWw8-YlYmA!xdnM%fnCR7CRuL&vChc7x{Q z4hcQ9Y^<_7O;1VeDx=VF+zyvk(agcZVZH0Q*Ls$Y`P^*F6drM^Yqhj=sG2se&YU^5 zF1o|STK0wiul;TXlRFZ8tJR;%+oGdKY~jxbbh2@q(@{s0-ka|dmo5Vm&oqrp-8;a< zfNVlrCXt^qyV;en#Kbh`kdt%6&!k@2V^r0{Y%0SJqz0iK3^%~$Z!jBNHw~4mjajA= zrqM}QOiLgl&95kpt{T~&lR&mHH1_pWNXuC1#~aMP_1Ib9VnKqOYLGLx*A4BL6~iWq zZ3X0bb)ikkb})~Fh)-US4Rl&oV`yl>yBqt2sR>fi^HpO*z*s01bO>+eD6DieN!-h^ z+$URhN#($bUimg$U2lb#|23Ox(^ZFo^j+Z9j8l-9v`ut@IsYw;g<`hEnLwCYmN2VM2aJl+Z%YJEk6PNVGyT| zegA$fwKRN}*tQApE!xa9_42(|oy1oq$V|{2LYvL75nNQ2q!|{?hn_XH>8sQ?&=Vye zDsm@s4ZFuxcbv1mG6}|ib?XM}j-;fia;3sKOHx%Ho(0sEOb+DqJ}dV80eK>ttwQVj zwo|G+ye|%>5xhmgvy`9KrC{A3QVX z-b~4(gPR$*CcezlaMaF_@HZNXN#H@ZBi^JD^Qt)#6}ky=eAHw8=utpzKk(7q29C(PAax$GA0p_CIfql^I8mp0bO`+L{Kg;zq(*8s>MtcfbeXw z4GLf@S<7{pZNPQ=kzdq%ZHu z{gOl_=uA#7=_n>FDzK1$bHHMgE5MBK_|Q?OSJFY|q~cJlZ5ilW02n6EI(rw75%%TE znED7nCCa4Z)Y?!&f9I{Q}ALjECq9$wbWO!0U(%$hB?rxd(l4TFJSJU`{o(ciA2h{FJ$Q5oNu~3_~$6F*lB!gKk$rIYT z`-3zFmF6}1GWzwXYAp+;SIbN8J>I|F1Aya{)v<=jt`6nlV{b0JkF0>-P&5`!V)Io6 zAi3L#T6_=O&^oJ-M>1(b(e5l^-7~EuEjhONKZ6skk}0PlZ4;kK9?~G9}9ms4^yx?mfu~Q zKrLAz11C?xp`Y`eJji=W*?a+Dntr8`m#%Hbh3RSYYR*k5*`YvmyRtm4V$Wk$dB5Xf zWbW@}m_pF{7&2374L&ks&R#W$aBl#Uo=+f1XEHn!j|&R&2S`K@c%yZv!GV=%P8V zB~q?0tM%IHL9smx=?R~QX?bxy5`B{Xk`jja?!VpRasZ1$Fob75aGO4_YJSX_!nGJe zI=73gz9I)}b}3gvp1^Wdrv=K6j!YWe&CUUW)R8Rp^v=z`j3+X3@t=Lkl(5T2mSB!{ zh9|GvdsKB2@4!RTh>F6{Zl5;-lyKEclwzKvk9e+Sd@Us-ER;u1?@+zw#u{q-g1z%9 zKH`nkJ-X`ZN!b>?XPK~+-fRmR#2XRqzh!9Pu9Yu(S2U-@}g;9%kfNWUqU z^C7op7|=~3U{?zH02w!Qqho@PA5H7uRYudiT+YdxN4oZkSSpvo5}&UcTaA#0$7O7lDpR-$Y8V`%MH^KT-x+p#bpOp~b~tc#WC)rGaYT*EFGo-1<3N z-kdQ!q(XjLHg>Vuqh{YK^j9)>MPNLHVVfI>8u?~fPX=->>$|HBgrVvf_`>1i(jjzZ zkbn^H^w}J+1wloO_D`YhR%9Gk#s|yE2|d9gmv+=J+$BI*&djnA*4UL77jK#7#_9^0{YuiFb075P zDpJc|JpP|?lHltASsdExd6&qJVxzB`Ie?`ZQOtMBHpI*waJ8tZdk8YQCg-#3V(S?0 zb6+OQs&F{EyabCK?}CX#-=o=l1ooewaGVVCJVH1uAfnffM(wXbAM}K4l?}>Pl*nb< z0z{#`3Gv&TCxn~#Na6s<^<8Cc#W({x*=VGti#+{{ZVkXT_=z>$R-Uc;j`z7(c`a7J zFsF!hBe|0DcLQgPdB-OqOzg;=&wV{tSVLWY z*5D9y+0%?Q@=mGA#rUm+To6WJnhAD|NMrW`MPrrZ@J{@Fj(Q3|(nTA?7@dI| zXRC(nJKFiRW{O{$jx^-c#q7(mW6$pc-hiF^fOXbZQ5Fd4>{J$&u`U!{@nkC0wB4`Lu zq(x(x7TZNIrzBnDCWh29>4c~uPyZN%B$Ydcs7p!4gfYiUlRG^xoJ;QHyr8jeh1EG9|0`Jsl$hU(WKcK|Bws}Dwl{qP{pEq32 zoY?fi;T@v>P+~G35(cP^Jh20vdL^_!kJO3il-OKO{in2Tzrh_kw96kZ91v^G$TO zlfO?(is+fn{6#6$il3Q9{4?8=4>v9P=zPTp>wj^f$6yoGkL=ikYMclmNn zllO-$YQ_{yKMO7Uwv8cPVcbXA=(1(y7zs1tga6sMFoMuEsr_5)_JVxeJZ`>4Pyx4N z@{~>xT2L6FON9Bu?z~l0s>frN9|HhONmi${9ZrBlzuwEq4&twQ?SOv|u0@+{KNYXme8;qk4kfp`tDOx&Fu@!cI(k<^-~>%3DJ=PqhHVmVIf9e3W<{T->_ zl8(qDok{%@Jq6Fj^FpA7W9W9sy1VY!}^!Hd6IXQc4Lo_Xl!<*xVMZQ zOjDx~m;?-W;=#_GBsW0WQgCzIEZ8Wb`tF(M9}mvk@(~zq$~>l8 z1!jTDqbwflJL1T=xuo&WgkpPYFq}4ysg(J!0XSkOzb4Vybl2k}s;(zOUZ1Pe%%Yg^ z_lE|9$CR$1{8jP|sFJr5M;Q&%FZ+R>P?=^YZ^Cmda*CmYOue7s7KdbEjo^{T7mkX~ zyW#=_$&RwK@v`OmP=<LQ?Sd8Kt7ll~1uOz=DsH`7cCJIY{`LT z%SDs(plM8(L^RRAQw%h)!s*o|3)i67yM)O(kl>G#LiFEF2flcG4w42RZ??Z%CFhY= zZoHz^9pq}`-vB#rb-^QGo}5?~)m@4C>O#|sh;C2Y;j`i<4&MHD!~mo^fe%qeW7~>j z#}k#kWQ(i^#guNaw7&80y#SbHMh+dT*6~OdMiW&!eVbWJx>x069R$zrLe%iW)|W zZz4v23=E%gy2Xc=QHd>@mkA0BV9kA=)wK-sm`0J6m|jqe%<+wx7v{+F z10A<3H^hp1L%8N8x}Ic37KX`9ynfeqw{Ctq~QN19fLv@8w70;44|0|vdk0^|aG z7DEy^6oW$`#DFa;e@Oo)N)7841Odw!e%nxUmpmAH^(jaoXM) zv0jk^EiDp&M@pk{vwonPvsTeT#Qw=a2kBbWdW{qg8PF1w z_e_91l$0TkC)m%89^&oBt7wpQ)^5y3Rk<{bKV3}i!ZSTQze_+}Q{Te<4fnjHQRuZ0x5Yzi?mv@ty8M=*{7zT z)R*J@DOuFss)!&2ce2S`(=5l<6+d|3}cbD|qeR<|uZ1Y;lL(&hV8jLe_tM*MBl{IeNa+ zp9&QcD4H2{ki5e@5SQdYR=;uamhh-hWdInVM3kfCiz7Kb((`qn-aHy;+$wf zK(ULRtYvW>@@1ndbxJpidJ4nVAiQeLT8078YGsq{kz3S&!wfKG>%nxFmCxzBm3TR2 zTVLqxn8o@~_D^$Z&&01RyTmo>ZNqgw&nFo^we_ieBD}0;SMhmf71DK7P}I3wS40|C zq$HP2UjZqjGj&T;>zk(NYhr3b@M|*s`C&jl3T-|!oi6sash6u+RLL1x|DHB0Pc_{k z>J7Hc-YioU`QKmkr_s|G0=oOHqeEtRghiB}dM_6-%pS+*nOs@3s(GR@x;`u^WUjuv zySGbR3xdPL1ETLs3WdBnUs@(L``JyD`5B|=7@vri?w-e%MU*r&#@}?VK93ywG(XT5 zBEtd?mUF!JI3_n}bc;&z2X|)dBL~yAoA__@4i7L`%v%hn%K`QI)8qW(=y^!?ax{p$ zeoK^X#=nodaA*@%1)#xy)cp`V~T6>x83C-gYSX3}1I(|1PbH<0q*e)!HBLzH!FL-qP0)hgsUFSy5un%WRi&^RwRD z)DO3{nT7D#Q0G<0)bcreYyi^4@{%lvA-5;7R0j8t)p>NG76cy>=3LRAY_cxQBs}BV zs{)mqeZj*|@>e@&P6aV$o*bT@PW3;l{Jq=Sb*w&h{#k=V?^N%>%$PD#g@KHA=UUl; zs3uL>!0KYXtMxe9%ThW<{#Q(|^x$!uMuPdmkNx#b7^KegkJbpRnEdUG8Ao^nCJ+I{ zN6L%%wjv>no;#VjgCfIs@CY1tBT8}TT}q3kOhji@d_^>f9PoT4LzLl7>B6c!OH{An z-@~|HwMehGidep#)tq6O3rBlpggd9_Jm?P-8`Vzr*M1F88hR*8H(lSDiq#rW^8+54 z3idW|$8|A4Rm?K030Vq>6<((6qt`JrN4`~>eh9N(Ed%HZ)L-O#WS zH2ZQ|y%ZW-KNJ~#dWvrmh=7uW3(>;NrXlhe^){Sooqi(eTJ564GHCs}2G_^TD2FwI zI!BS&6|n-S@qs}u{hpyVAk(%6zFT?UiZB(*Glm|3gyza*OwlEAA6xUNe)jVXoobV* z*cBH9|514bVO!-4;b`{AG;f z!8WZd7ktdltUAu2q>{ojFGhC7JUzLe&C4$`PtM5F37}0iP~>vf043)3x}<-1D{VlS z4cY(J!U5ez0DqhIcB~ZjBeXWt&xbkAiSth21Ze;Y~13u75w=e?R z3|2texTyckqrQYtb@z7Yy)olGp`#+QQe)&UTp{%7Q#ELYCl-YeOvQ zG*)Yd=BYGueEo)Gjt1B8v7Q$z6iLs>L9VZ6_cYvxGv2dDIIBdXCJM~JSoA`h=d9g^ zl+=@n(;d|6A@UE_RCX%iq&YSl_jHSdE>{=bMOD~73WevZXZy`1#Qt|Ira`jN#7Lft zRoCN$_$c@1N8vahUhe(T$W(90^&!Kdd=>G&{PGfu8Wnrm0P}N=$m3X z9rC8=p7&me>hELk^;f;qbn)E#Cfl(+5--RMRj^pp=ya2;8y!8$5!0utgYsc%!p}WO zRk(Hp4yZK6OHGklHCdd1^(h4yvN$WNo6JY#h=@87Fg^b7e;o+g`J5{~bCmVZ^}4z< zO^EAT2mcCd0Fa2DBApKeUk2=~(8p$Rh=5xH@`^)ucB2-xe$M59bC}=oKxwPJb=ac# zCqA*Tx=qOj1?24K_sFQm7>|L6K(+Jg>hYBA2Vb>=d7QGQPD#6U(g{1qdVF)g_jLoy zOouzjYr5756xl?wx4k~IWKrgfBVYNT5?5h2)gNS-Zjxk?9kdsqwQ?m1`qnntgJNz* zt>F;(*?l9#F_}(py+M(ZHgsT8$dE7 z(?uKQW7p#3k4bE9jR7Dq5fyptkSB1-d^+6B7fQanPR^FO`$h_LUg%dubf&UWmI~23 z{@qM#o)*mr7@`hbA$^g3YdjV5(Gq@rn3%NWe{96V_@ttsRy zv+adE_fICCIx(U0&b{f4l{Sn2`S~2+K-=w-zZpk10{;lQv|RgED+kP*H!B`cYM zX&CC$z~`-qLl$(1*nuX>$WsCMFFysN7*LMx6&*>Fh6`o2+%7WfdPO;@qvRve6j7m? z!<8S)2Bt2X@fGts$|e77@XG=rZOu!=YK4d*H%k3nkf*=H$q__jI0)` zk56MQ8LdjOiZ{Lw3xp=9%LD~742c~q!jH_Ra_LcbW1{@+tdZ#a1Q$vQuOfEbNBvnI z!2B%HJZZ2#YVj_*-1*0m4~2UdH;A?s%>*z@0H5eStlgAOJGY#Ko(bJ>5-=742E7Zq z*8D1B@Q8Sy5O#EfII$!`L-;u<2g58o(q0MNwtx`qqNCsQg>6u;Fr#mB88Ic*NF~!$nh*(#(4dH^5tP#gusc4VSLWD+cjf-s*!(sN6v-&u)Wp?+rN> z`(?59Qi?=~8vS_2)Kp15)Y2llbQyi=v|MdJIk*DPHZttEp;ordrZGkhV{fc!*n7CKMa%8^1=W$gzsJ0{Q2yl&)D6h)<2 z&{c|3Fj>E6|DUk!f;K&P?y3t;5Al{NG zqD3WCSuDz{{sX!TJvq}Tg*#{tnv6Y*KB{LDa#pH*R8Q)vCwmiOsbgj&6V$&eC>DM7 zF$KC*62sPY0D(WEaJ}7Gm3>~P?&M{$+=7n%op|#`1iRt?$`reIKJm(ejA-gk)PLM} zYpXP|MLj=foc0jnoa0)$3+YfPl80}G`t~G#yzAQ9kAW=wo|cfxfI*5PO*7uH+G(B&9SwB zr=p?4CcbyI{`oacN1* z%q*zn2YJWx`$!VHQ?Eysh%>IL&wL;i+tLGIX$ws=>^R(e%xhBeVU+%lpp-b}QnwHs_f-_5#ab4hYV}f z6eCjBRR0k5M*0h1;Xm=d3SB|5y#z!d|F&mY)kej+e1kxTpgHAIXSiZtH+L)6qDi2r zR7r~1QCIx;*|n|8X_o)|-muyBx0{v*E{QgvQR?8Itd0wKAcUrF*wwWMnb@A@)mAUS zcGRaT->PR$&wwQ0v(DmBdu;s!5A%Ek7pu}4)=(0l%N?jVooEG;JY4d;--y|D#$Anj z0MP3F2dAB~clz{=k6Sq(Biw1r*&)aemGJZCjLwM{{;QV<5n^0rrslsE0J6YR9V?;Y zb)TB#`C4ka|5l?~!JA;U8K2N4tbd4SJzF7Eh2Nifr`&F@HC6g;b}h)BAK*VuW9c|G zR{r@Eth0pK3UEm!3SmXKBw(?^u^1x}vRUlbkj4Nq5!J;%Aa9MS=QkAk+vS}mb+^&U zTDLh|@v6E)p7fDu9Z`h}85=AGr{!ovzKC{*Xvx`ptEO@C9l8U2jWM|!ze7gHwP_Xl zz5S&phqz<2lTyz_M+J!U<5!GbK$^!1oR|O;-ZG0(V$h`Yy(R(%1TNje;HNa1!J#JH zh8?NFNWSQ#2Mz3^Q=Oc<)l*f%ffz`F*4OSPj<3yD>$!T6VNZ&8xYj-^nf{FQ_Q&X#Ft71uP%>H|sF z7EADPM<$6rGWajHn-!sYwfmU5Ee4kPKpx7RSDX#?*?4YW-U0>C>i4c*p6K*?HIWq6OK$v_YDpk#%^eE`Ja3x z&+tukJKHeSW|xX2ynLE0Er%=p0~oR<8PU8x??(@hD|LAvy4SjqNv^TAWkPT#fJ9<3 z(TdvJ@4A0j&V>(Sl=HFR5`m4%@#tP^u!0chnYSS0Lq>9paDx);D zaYo9e00Ih}qRfpAeyAI4#~MIP+$Laa+hII~ z1vgO*B3&8aBR~tRK@eSt1QB583SIWf<%IWmv?lF;0_UdRgD+2NkQ7yd-eA9)f6tuO9B!~J~|2?jLnr?oK=2>oL_4EW2Ume3)GI62?~wB>4{CMcq7G7j|Jt-<)Ccg61u#xu##k zc4qTqEc$F;qzk<_DQ+`_sRka;&WMth9f|`aebDuuf6GIbzM9G*ih^6lm;LFx%IJx{ z58!wwdEI77jyJU0Kdrvy=M|@nU2D_N;cK6Ox{oI^Mfd$DKMiZMd1cN#s*$QGuT>&qJe{8OQJE1 zX5Iq&E&T*maikZxxs93(JBP}$W23kYfAw#)oH}R8)>Hch9?cq^|@Wn z@kb)>HC`w6`LDY3DsMv6dJ{!B+4)Z&qhRb_F}AdQcU(6*P)o3)ifozKB=Y+edQ(a> zJ;IFZ%k%mTG{GeLUX1ca3|86Z+#f1ywZ{Ht9h3r~^gT0)P50hw#&~JcF-j^#?gKCi z=sddv&gwhy34wHIHjh^_ToT=eU?B;%1c-z;uS(P>Brjse%3uNTyr~zm$h|yC*!sFP zu&}j0$AP&Q1esdaV_7o}^F3yqmo;D4#;VP<{60F5;X%Ur5aBs8`Oj~LXv)<%)0pAE)>@#mRs z!>#zY(9FNnU!7~+o(!H0CTs2z*2kwvtWAA50cQAf`#QZ*# znq(ydCn`ip#iia=uOe{5X&pm4Y8t<$WjCaI4g=?&qtx~-J`d5Qqw}*x{cg->@av1|VubWv$Wgf)Ub#_0sg zzEu6a1O2Z*ir6}%RA&XRWBQJ2B-Ni74H3+%QY;YF&oi!2JRd0Clde$KFMW=>;HIW; zrfyc@Ev7?+9k<(LXt^F(AHmN=>&dW|WcV@(WKw~@79zx+n$l*=Jdeo9C2U8;aMo}n zX2W7L5r2c}-OZw(J5yU&4p(RDPSxA`iU##Im_?tdTxxU937!b@^Uw#b17sa{&S7zn zmy`yMbTHKqmz4GJ94R}yAZCib-WibiMM#S@4%TGn=3DRM$o{3PPTi635t$H>zVn^L zT}qq+F}!w%7sgvsZsZmAxDzAu6AAM!s-Qn7=f&Gi8jU=epWuILMvYT$!4c2yx?#L4%;?rry}u&X0YGB4~Q%gU0~L`oQTH zb+{S-2as#$6OD}m*s^F!2+ISXQWEJKdb|KNN)2=qWwxspw>s`cm%{YZ%gyj{<-#a_ zzkqqC1TZk*6kZ+jE_coMqzbw_4mz@*+oUD@j4)vY6eg~))6Wnh6Jez~ef{_L0qUMZ zUbCX&%0%cNxR=)p?@M|h4Q-L;%-0t#BG|3A{@Kg2^wpP9q`kPWxGiggOM z)2U_`p&MTea@@!tVqXvdgn8|l5%OGrdU>~zYW^yaStvEGj)Jocw)GRjv38|w^*4P+ zFiJJLcln3kKMy>xOvi5(v5}gaHT*52T?D~7oiX1vf>DAbPp~LkO{)z%sh;+OI&h)y z*5P6|Z)BG_`U*HULOoEo;m;BPVFK5UucAbEP+ttR* zHmG?0jKJFp8FDPNFEMY~^~SLwq6~5hQhgDS*n8``Bpx#^JKMe}et|F5q2J~5G(loS zSp&QXx|yNT>C1jp!%jX)Qzyclz+M9%-&06T@r^ZmMERheqiyO5j=ai`i)ICql0phL zYR2GVgj2M3Xsg&f%BkIB9v@suu+e8Y3#tMGB*=Vg_!_o?GH{);UB_Qz zKN?5`cuC$#-vluxZ{Ni%;If@kL5xLg10IpV3P0%{kDt=vxL^q=7tSqA>Cn!?a6z0# zbYYjE;(RIeBD;`_4XrOAIU&ON2z5L|Ihs)bEJ2PGfy3Lg)}<;M7*4_pw0DIum4IEBUpk9s1+bNjfPmH3UtB5m8f6jATDr zjr*OI_2x?sI)z4wU{qvm_)p0T_QWj9FKC9LvgosS*A%ZMkM(}1bgSY5%B6)nQK&&P zxYO%>*YlSXQult`XJ9wdXw6Q)G5pC}k5p^7tD~$c5XvA16n^h`N)=GACu|6uFKR0q zg+kLSTJ_8i^lObNT?GA2CTaDc%c%h32$DDmYWKDyLPYur&n=s@UUf8(hlKF-1*(gC zzj~v9Mc}fLv~vnY?}@k>7IAs}f`$L|$9KvqhtKulaVsnW2mqQ~;;)jiWl=Epwj3Vc z3c+_DUbCoK+xq5Bk+24R2@%+Yv>45Ax04ZVh9a*+#%AM%OVdwD5`?_cURtS2cUP#3 z3d@LqOACJd*-9b_xktuZmPlk~mE;wOp?J~O)8X6NwC!$YH`_mm%jbk@Z3PBZJo8@Z zExO?k6dn`rnewQz@5&>oEzzBu40615CraAK0fY2+Y#VlOqK>;}TrOuMwDM)Z3;{3W>&g;! z+jZQdjDF|2nLOzC7SVye-Eto=$C1|ah3B$<`JzWtnt7TF6KRULt#1SEu;Kecx#QA6T-|zz)vS#1O;cb4_*#b2lm>KFcyt3{gvOO? zH188iD4TCjfYUuq06m;ytJG%GpV>5gIDV-bq4URPZC0$P{P;6mTJ&*e*lq4KgLZ%X z9k-ep!N_hBJd)i-c8$_65svkDt3|FLMYUo9q$*!h241B(Z9j1#efc^EPLN!LtQE<_ z%I5kT-F$%pzpa4{Y~k=7)|R^rkj{;#vpcHgXyf;4cM0Mfw8(B-8n$lA$xH?AF9fE{ z8CbZQ)QZezu_Db0_WHD6UdfJbd+)QZC7`uBaDQkE`x9s?SRP|9>U|0A_zg{T=y{2T zp7hVL1`$=$sig8eG=G;-h+5JiYVArdgT7w8>H;*o^_vp1MAPa(dKHfeWsuV%_sF+Y zjx;2^L53a2E$_$sn?2wZ(e{kN1Lz;asCHn6F7V4-9ywF50%fTR zf3z0i%67o7J#0d0`@40zuVgLt?S;48`v6phD>nk@JuA>W;mJNR`+pSFkWAaHYlXE) zsup&pijWQeX|Y*@S4LV3un7Jfi!ahx87_bwC9;8I|9Ac2aO(H{yH7YIHM^0-h_BG! zoW|J>@pNfQr6Qrx0>bfpn%#!B5C{@JLWAM5wsnMSV;2uixpEJwYb6&MTF89Uf>ti} zh|#n#~}O7No;E7lpp;pLauvw&W0!$B&d+4LO;DDrUw3n6zWz1xVPd@m(9 z0Nkm})TZ0`?Q4kBzY*rZDHJn-Ucx6k65}(5VzSJ%+E5u~CiUzjF^H!(ARL;PpZqU_ zwS0mIa3xy5qa9Xv{0uIbZ;7nvJEe0nV^ev&v2I39ov3BdBoG(i-FI7&ZJ=l9PSN5| z{@~pr6~ThSPE=wBApQz4<$x(q9jx3~2E3>@gg^}s$?3GDl-;u7Ay)st}T#eJ4#>3P+` zVzl$iks87nz$#UhPI&SMsiYxeumqd1`sRmbm?~U&WWYBhIIxYtOC(>{@_4q*n5hRM z#AZhOwvpG*g)q>NDkj@A;nkZ_VImtXHcuQ~2il#afbA!feVRDV3P3f`rISdw8IRSAOGQ@?-n!H5s0jREAKFG(5aIy|CUHlb`Yqa&OoRr_zKFQ3 zA(grn%)(gyH79B7Ll*f#pdyF1E{!=b*FGXXNe$h_N*jAL(#Hr^p*rd?_k2bainJ-4 zE68FuGW1BP6hl*ER{aoClyU*{(b#pmIx5y}^;K*~Z+=lYZ}+hXEmNhk=~#O;&KLx} z??iV6!R%tGaD|<6i};38d#*Ls?Q2{$xE~Zkyz#X(!HoJeD6e;Lo=#Cd84j=qY;qF# zX9YZRyCagn6o*My>%mb#vI1(T>}F|qq=Q;lTSjG#&d(-qI$>X?kTP|9WnS7CTx~l> z^p}(C%;{@U|7&NZN!R>YPIcp<4UyF=F`-1TMU(;yWA`PtqX@S2o20MJDlv!(OrjL~ z{?wv5<N`^|dgbC3UAi#yj}#?%{9373n=e`}lQ^F;J>}!gSs9Fo7&}{9BCS`Dda{j~!c{KR2&xh)eoisRUN!aE~ zGoEDWjtt9jUy(6wqc8gk%J!950He|=!)T&Av@=+{0AcP6Hg7uv>CO|T3FCqX%e1k) z$M^~Koq-M+7O8^`gdn^#EaNH|p#JVtqnUN@)7kFTrKk>3|L{cfpH&~V`4g>Jq8d z>Y=xygDPqhqKUAYHUG6I4=ar?zK36c@R-UV%%7>~A{nB;AlAx?q5fw7cHu%t*E8u! z0%<@jPe1JJ7rV0!_m}t&E+aOClG^kq_jsCm&6GY0mQ0p=q4B?w~MC!EpqX;cN1HgA1qJwZI6$9 zQjzb~cFGgjcX<#%>=5a})JiFPN!Vv;?ba?B8+NJ+Kux&^# zFdq1l8c9l&xUB*=w!Sb%R7{uIlX{Xyiuc3Kw>MSK>If^QO8>{VRa z(a)Bh-Ef5JYtkZv&>ZwYtgH^VGA^!~&qfk0i!0vh_pkCvzGuD_cO6Y>gv@F*Rs>C* zu@Kt4ub9&lnIrm-OPazB0^%3qS{UWKDoXk@oEhO)f9jCFANG64@ie?^z4i>35E{N>j7$n6UexsLqTu5b9!fz9WtC;^W zEf8@^(`Zc?4_JJ%jysgyq6PFGCe2hm-my%TqC%D~omjm@-KX&e)@VskP_z0lSCT!T zAbgtVbDpiOI>x?c>7OcRP+-F=@DHHOj+_>u(D#@iD)O^%UxvoY7Fs`c0GotijtAJM zfiBoQh||d@$&jdb5y3+Ub&({zZ@|XM0If{g$B@b?1u<>vDSPwQ{Aw;ih~tWC4ZwWl zI8!6@5tFZegB0*^C8eF$7kgK=)-)Yk!AvWhIoNrNE;$MXPcrS%+?O2TfUzZJ6P8>s zitF+d!)7N%3+(b&CZaZZvN*D?bnfkH!&}7{Zs8NE&D;&{UIG;;_lAtI4>V>Y)TVRy zJFLk?oIPvR83Z95z+Yh3dxc*eT=Vf|N}O6~L;z79uZL{h!c?bOCig8?{CFG-x5&aB zIfB19W!EKMO(kLpo98mn!3FzO)f)R*!$$i;OS@Jf_Ra+=khQyJL#Y?ksdn>Uf6-$v z%_7x3{NB8=u4^h^?xo!EDvC>h?jVn>wDh3XHHJr3*KNTUvW)e+HO z2FvY+^6jwIpZweTSr-c7!hWg{;(5>;ZD(AvjSEi1xHTSPW@1ZI0+7qrV)CobqXcAY ztoy`zGmyP7WA6}d2`!zxiR~92?E@Sa5ja0bT~L!6e>FRtnO^a^Q=bgXKhdEiu}9x` zq+YDIL*|Hs+&#RmDPdkxk~DX4>lcwj5-?II*UqSWEXBiO6j^Hd43FzSB30mX zR%Ek;%G(Xs`{?lg%2fPavh=xrc#&qElKeCzjNw?>q(AA_@PfT(G|MiIv;h0zswZ(9 zQIUm6=<_@s#y0tagCfX+T7rTye#MVIx#&d$IY2jMu-+F4J33aqam67MP)*|_HvdQ0MTfXcB=B-8zKzM81?7{#i~tc< zz6`iHDAl(LkUxhn^pR9}9J4xp-_+mAVs5Wg`DfT?1>AVyP`pGByhUfejJa=nX!&Nt z*lhS^D9yS9h^E#kl}5eyyd7j%|(^5<2wx$@*x; z`|JQL=^#g24@d&8m7#t39qkJkNLYX+Onn*Iy{ap|`2pKF0OtZZDNnSKeNWP?Z!P|5 znJyg$jv-hCF&Ha&>50x_{YEr(R4e2QC|EPRwet>h(r|{*p^Tcf2C8 zWOBNW*Ik}244rR80ctalg$d(#h z961Ck`{4WE63A&CpQSLlEu z76i8CPk7KBG*jadjN(&&;KI4hc#PQ2APA-T;|nChby) zUkYbreQ@nt3IU)SeEk{`a>vEVDWME1Pjsj)FdL1+MmYvfK{DvxIIcav z)Z9&>7L||1IhdfL-irM?;k#b{nZZBdo(6l!E4YV2X&r*UwT*_<0Dcu(1esA^3UF3= zbxf8ogL;*p+Fuv~oAZ4+-$e)ny>_mrK7x~ca&QqDG5t=#XIh20BP}ux`dZP_)tn%( z^SPyL*qth)(v^w@Pu?gE#bCRZLzJ0}xPmcWUa0cm($Hfg06@pE)PuCApgrTg{fnt& zkj-m4l|1HKC`n82*txG~5DkFe$*GSuyqW1g%#cwF5~u9>7wcO^A)s zM^1(Otf61PU4e%+T{1=NTx#Gl+GJH0_1UO~gfBc4E)^nlQcG!E!n~DPalL=c*1AMh zFUxT!uhl05;qImI>Q@3*7H&Rd;)8z27^IKXt%VOW8nQq7=}z+QHAj+WSV$m*&Vky= zMjF8)U!RW#?1RyrDAPb6c%E@LmO+KE7Vh72fVb*wc%MrcN9P}o4sq~QJI zw?Sd9$_24UqaYxdz>G)~C%3EU~yM`O3#V`+E_b~k@q$hPEwoYBtB|9SBkn9}T z7IMfPMRBQ4YZ&4JFm>H|2zdNPQ^o0!@~*>1{yQ)s%hPF z)GQ5vAhEjfMG1*JVEVA(FpNwagCU_N3%v$DTCd5KlFW{$f0?h_nq5{UvfVdWjHeUa zh4~`N=bcDF2EGKL?5H{xjh$Wx)ewYi;dlI)XrF=ggc?;GXr-AAvMR8>G*Y5^JUkb3 zkO5CP@Jh>VSYR+gTQSMMhmOmnI8XoB@pR*mAA9BvFtR9=DgUIFLa2QRFvlu!&;0lH z*0N20eNwSRc31TtWBnBmp2RDE_Ssy-eufW*lE^XXGu zqW0VIS_~oTbv$pSmbB8I`Q4W0){t&6(ai>Q$2HxGcjxL*kq>pp3EF_Z(?p*wl-GO` z9c;HOMAwAi){C$@?F=LMfXjmP6VgkgU38UCW}TKTJ-`%)%!bf)bYT`;g`R=`8ScWY zR55VWiYnOH7kOP5{=TO=`1FwJfV9mp+oTt}3NXJ_!{bmYwQlLOE;;la;GQnSvuWE< zzp{x*>W^l1vynvU(Q@vq;#Zm5c8Z!>=AB&w(wN;A46z`7WU zi{zGqWX)&`ZCHv3$Kx#OPuEPDDLM{eo;iRFtol^>)l$`xvXeR3;8p5P0oiT7m(hi2 zrYrRZ(3-dim178pzZy<(=?+}y;+YxG>E;H+Y);}}gwx^%Ci@D*X;fh5*RolV`^t>k zw#<)(rV*-Yte+0Kr(!eV5q(E;3RC$R8JkoR6ffke*?gtf_V+RFq4@%o?_=%F)9#RI z3|xUw{4#)}Wo_T{pTKLA<((z>x1Pdj{YM*WLJejgQZjgu2p_wpFt*CGK5O@_s#fIXVd`pk8jxbV^u>Oq@xO3AX; zXW}(HZ#q&!_-O+I1g(IkYuZZ9&>l}+5GSOwgF_?s%_|;9w{ahIXn9vB0>f)Yv(q2? zVAV6P$5vvRG26KFmB^iiesf!heZ=m>t>|;?!iYXO-0KM6e@1lgW~l{K1j638rB_`M zG6}Fb9e)lBKP;Hd$?KXm3UgVGyyE?Fb*~Q_Gc@^e#u<9hXp;<~&u=;bInZK2a?<0b<$39(|(q zx`yVLEOvVo%Z3&_mKPCTnzLvUt4zdx^HSp$aVq){>OiwH4hPmbHzk2W&zt~*qXS-T z*o%nbJF|P&+);VH>7n6T)`44a>|Qu2U>p*5r_d~$XU?V=n$*4DCCAEV1MdN(JUy5e z2KHw^G3g*Uv_Z8KRgA^42s#Ecr#?Vd9<%a`De8P1^XYxMHdp{^SPTFCSzXR#W@HPhJ<=*(SKXRnkE*gu}Up| zetN*44&~?$iO+@y#8PNX`-Yr?U;dn`2HHAhDo^!aAQI);(7q)Pv8^SJ33;VE!UrH- z1ohy+pTZ77-~dn}*(|IGtrEAeUopgsH%7f`u|YSe80R*_v1NK8RhNII-&dQPfx$j# zhY{Lj06Tt9@&%r=GIh1Vx&sX|UvuYtAo*BY#0lUhArkDWs=rmp#^;S|y##?6iy}dY zxT}+eAqYZ%1sOhkEufR@YqQgnO_~-lyz0gI37loe_FEy7v_=4Me|_Wylu!jqCaIsH zK*i3#da=3II7D#@M0|9*-hJFPyB7oSKnbYQa_nb~B?Yr35rBwpBS}x_tAP<0d^z@d z^)+Jb#K!NCMf~wxjlT;GiaOXI=GdcvZEw2KNC%x96_jThP~dYXM3_soViT@lzL$Ey zkY2Yn;Xx0lOvMHD!hlg7h@^0E1)AQ)S%MYf<6c96lTlwQdLDazfByp5Ddw&)5H0LYj~b5N z;;=%gbS(X}MgEu*6ApU5M_6oa^PPQ~&$xI-3+?%Agg@PptJvQOSk(#VKk8R*--h4C z-)BF8tx5E_l9s=3EBYm7J~H-rNN!X&`fdp)c(Czxk|&MZ-d zTAI_|ozk=A)>FHz1ec&BNISRHCM&h5r%R|gzM0EC4EYZK(~SeL)I@m9 zw|b(XDOqo2*bik~CBbAp8Wzo92=5mlw2`7Ve z-7rtnYo9CRR83@(Btp&<@9&u$ImFFnO-|Go;=}a{nUv}!db2s7xMeT6k=PknPY9Ij zcX|LlK*GNb`2I1~6pubFS{OJO-ZP(i4FD0D`7IuTtIZ9B)3F*vfLl=)uX(~r9 z{*kt_4e8FuPI&0C(dwBcAsd%B>bm$I{Pap1?Uhaj%XDbk%{Hk>2btYfQT zU2BPQ9P%bLtqNZ*&^Y@-NN?WcV!oFs&YUHffH$`0D(wOGN?rff&M)IGJl9bm@c+m? zTD%c|SOib_cpu7)^%%bAYNRm6Ifdgwe;kiizA(TEFO4}bx2A?;_S{J8) zxM+DpoTCiK7PpdCq^tF|_yH;n4fg$)E-qqTX!HML>Z2E|0hwsg>Hg@qbY~ z7~K>$XOA3^8I-^Jtslk?WI903?Z+QIaBi2D+nx5Lx?vu4?L} zEEiHS6p-K3wo=CheULRDsDb2lm8v5yZvKz@l|r(p<=C6w_1hkL{S#fPuFPZ020EzkAwlCw1h(LWeE(7)665p z7pR|v8`v< zlwARiquyK&sBfdR)5%_xKSoqvy32DF2^JJ5$tKyfCqW$iEzzq>eDkui3*wKJww3X# zExO3XG+&8ceM{t>&Ua~5hG!xV)>XX)DnQ-5{&+2jcMV zFD`*o^VD==?_@ZgpK1P>lVkdbZO)#C1kmOqGZO|f3aKw4zPoVjGSPGw86_o5fW@IO zt=FT|z7M0@eb+KIx&u=_D1-^SUeIP9pjQJS&5=Izl{wC6RX<^FDLnENfJQ|q6nX=vhai+A8hr~`jqRmJ|px#^FuHx|q%3(i${?zAuzLUpKAeb2a8s_qn;CZ=7|f6n<3JD@>y?{98V|8YJpecj-Qcw(>ytM>#==tc(~=$Re~~}SIK=14FjD0o;H@b)GGMvxAPOnCuT_<5as_W) zO`r58dm;WO=gKjs(zMF38s2fCRxl*0@XwFD!!?14u~S+00MWmM%?j!1dFD9e>@2mt z>Duvd3dSdadyhwqfZx6g>zkH+Qu57Bg}PC1{m!-mml>3q(ai7Xd{Jd$F~bhCXbK11 zQ@cONq(W1a0}?o_KU5v?4j6&M#E+yXJM~J~E76W-A#9I7gN%Q^J z%RI)Mu?OY4O!W%KKBW_)Q@m7`w%ZnV=Ag6rWABkS z)e7%a3ly0(m&YerJ@q=eWQ{N|Do`SFOmF&GK+KM} z?rXI0pL;DF6zz@Faz>Ph#WEvE!*4oc-L19OW=s&S_nZTEBFXbo-9s-e_NhZziwli? zOYEl;P0uyMZ`5WVKKs^%8ix8M9tUXWEh#dtc&Q8 zy}cxlq%YVGwLlYIhRR%XYn<%Y)L_Abcf0g`@S!x>5c89IN1tFz79cObau*x>m}dOD z4$DZdY8wBAqH9pso&ly;IH+Dhpg9!JKGeCJNWwb|>DMbME)BO+0uK0)a$pBv@lPY2 ze_RJ!(kD7fQx3W;Ag)@NC(5%PJpufc+V7m?N2bzOye!x`-poc^SXUv|gbW$&0R)k4 z3;AmIB?*IhE6i0$&oRAvoMnBIDcZdW;<(XkvL$!TIywW(PfIG|Q@e*djqUyaQYNff zRgO|2od@^_DjnE=OW~hQ09Vn4&}QsL+uL0DuA;3?*#bpQup8%nM{K$Yb+}5M%;ZQDUv5Hi zGP=jmd~^p6ZH-N-LJEpmlxcpU?Y{wFDx448J-sOlkXL5gRG9usFPi5GGO^$z;9`-L zRY|bB{bm3JtnV~3Y?5Xg+cuFr*qH$_ik8_lGd8O5{ylEk<2Ap<)@&Aa@KX z@jQLU+f7cE+H}gPTLEI-^r8Yo#u_KlDxfJtrpNOCRnC`Kf4AB;y)`iem#{FA+jlov zq=L&?E|VUMUUu2allA%GXhd|WH#5JaBuS_W`_|-l<0KCyqX7gM`gz8w-LBz|WG>ra z%gPsx(i^EL5L}e#s(dXDd->iuEnCV$jro4K*npIrj5)@;$GmL&usS!)!3L!fN&7Q` z(c*}~KeiJ*I`dXZIK`>N5lvw;B4PQ1*acivGH8&+J!UCdw@uTTB|N%cg3#F)zEk&y1@O)@kJT?8jG^N@<* z6&t#P>gy^e%Y|bb<03<3$(Z-|25|Ty`pH^@h)%xg8~04S*I#9HIS0>W@hRa6o|0O5Nue$A_i(S+3IuA}(2zotx=UG#cl+!WoALtl$nY}e z9cnyur?yNs5e9kB&6ZOC1|gQSUX@pN0Z+qLVgdJ}w2JXy&O zct>m!0d##_FyyjFEMwmdh^o!JD@4=DnX&xL#Cg}zDD~iRI1yHw8@;qxDsFReZvbD0 zW;o*s77j#-%_X1Xkj7f)!zkV%s3k~z8(kriPf5$3RoB)FUxrds2A-PmTj-0nymD|x zX9rOF0U|A=lZvf;kHmkclqJTp1ox6~N0|PDvJ9|IR50&sc0SICH$?T0n>GaSz zGlctxB4Jol`CZ<~&;z(@i|FAXK61_KuObV9>;#${CJ~=%clW3>qQC3~pVeII^o_rE zQ5TuPd+PC{Gzn#DCF`j!3Fc^Eg)rv@X@)V?J^xm!|J5BZXB63aGmld*7qm|Q z5j8x-Z6{uvY}c>n%!(MSa(4L4zyM#yn_`A8=NJD(pA!*>upkJXFoD=ae+7=2qEt4H zwBVhG(|ukta1<`3S{h`C5mkfZ>Dc#cX*tM|8_i8+0SWC%+gQ@shx5AQ*MY~*0e60?Ig8Ta+LMHm1pYnylDR3AssMddB8UlLI z8M;nis>Y9#!tpgF`xYu4ZJ($XKNngQi|YuKUWmh7MF&fU z_>O1sI=n79GTNso|H`FlbPk6Z)w z?C1K`_;Y*Wn!T3rPNqEqWo%bH1y~XX)eF2e`EbAW%jSes8n8@{U=?-|h9CCpi4at_ z5hW;4p~Kou20Y1CESB)oV$IZc`xxr(Ml~0tVU8SSgwi+J;rTD_w*i5O_H82za7z8))3@1M;%3WnBTfhfJ+?#vBE*0SD5X1u&RrP4^J(1a(itJZ zcngE*RTXi!F;MhmuO$p2*rSLUfO`2u6@B1JF(U(oWEPpObQ!QLc!{~K3~Z>?$fH4} zFcT3*{HcbZ2NMKG(_-1qyd%Wq+GLKj^!jBz> zFpU)T!$g=AV-U;S)G?*vz;F84hoVQ_wBx8t$ZXSvv_8bApJFo?k7$PCLaB>x@fMs$ zWt2pZ_meU&ow&qj@*+J^j^2d3R+80maQ2X;{ZPbyEO(hJ+6dhZt{4JaM+&ikGEsxV zup!Mw*T>m#$H`o4s+*bsK`*JDJW7RhU{Uh7m+if#v}Mb(yl=+`*AqQGd+dl@O>t&T zKxqGKCuE)Rko;xoO?uy&c~OUIIztGl>Kg?K1Do1F0cx~ff=C~SQr1WwW!(Z`7F%p!3X3SD*2n3Mk8mT)K3+3f)h{T!qyyc;H5=K3g z%A>0JalLOwnaf3!HBe#`Rpw$m`^&GGgi9KR{!`I&F7$XGaLoV6kmOH>KN$xZ+c1u8<{`9xn=4 zb1A~UGqok+@NDdzHVY<~8o+D?)=dKgT*j1f$BkX(GVeOUTFa30)n^K*lkxzywvVbx z3MMtf@V*L0z%gopED~D*(x9Y&_gzwS>PIP|^zy1Fek^};pqpw!!y8R%cuXxxoapME zJ|@}o2!!y08jd~76$X(VP1_DUh^^~@jB(KlWJ(R*o(7CgM!6x4G!$G2)&ZX(W`HjO zT!Q;DYzR+F&H{O4%KmS#YQi2bNZ{FqS1RBU8=L7;a3w~!P6^cgrlU}X#37nA`V6Nf zN5Xq@tRc>K{KZ55?3LW-=ff~Fz$gPk30JLK;ietE~YR&8|GR`p5*bhH?9dV z;OI^OLL)cad`6q)L|tH2&CI#AI!hX8+vj{5oxIT#gTi#zKO9^mP;lK-@kC+i5=>t} zencpqwO^JNZoJNCqB)4jz>B67S;uaD!AQ60D~T)egZh!=yy}-xH~a@sApo&{#t?9} zs$aG#w~^B9j@#2jTx?`0iXgv{*V68>hF)YYzu1|AaR>}lpSA@{kV0KV-0UCUq?bt; zgehcY5Pl|w*C(}tTNUTVsq|j-yX+U&i*uD7!$_JDvPk^Fm&{usrSx|x;0QsxKtp(R zm!MwdLXwMuS-tFVxO?i&?)hyLHM}0N3-?6qWT6-pVdDJew)7MCuE58FSFO_lY#;)7 z?bw0Rc^2%(J-iyWRAlt{oTM;*mnIf~A(<|pI&<(?VhhnYHA+$mD~j?Egz)2m1{Mj9 z*jZDCM^c1EJYQ(j(G44HRQ1lIKS?2v4SsK|%eRkB0BLjDnD23oT#0v&pYS6ZNcd2jX02 z4~!E6$9aLIbvJ)`^*xt0iMg=Wd!p>ZY_w;#b7jHDY&Hv$Z6s4pg2 z+FuW7j?Ud99~x}k{aDxgo^j$_-`}F-I-_TymA-VEd&?#WUxK76h3>F6JT0VbJOq`* zp;4S+e<61WcvPz3NeSHV_!OBq4e;(J$Q;sQHEWcgSm$NKFwF4;dk?OAo)H{Evqm<< zu7Aw=?)n!?ylQ)@Hm(}O{bcy_lm5ZO)Ex|lVe(I|VnL;3s>ZtS=+9N9G=grG>FrlJ zG847$JF^}SRl%GmUh?n+EBCqcBC}6ji%T1hFgur3+2FEUlK-*bnvv{@rj^j zu&a#)V1!^IN@KZI>MLc~Jf-9F4X>U4g3-S8g+Frvi9^0(vg*DdP_e z6jP#So3H~vhxLta#j3k*ZvXj}m7~c$co;xt4D5hsJ|Z;jR)f@=#$W_>!?s7xu*qe+T&8OaQB}Flb69)>+Tg1 z%8xX1`G1ua^6fvbJfQH{w!MPYS`_s+?9l!0KGo1~UegYd-t74i-YCQj{I1Bnv$D%w zeO$kI?6`~)>_A=%lXz8>w>6l*2S`c<{YKv z;2gzpcn0nlJe|*fXqR{XgndxO^KqTaOnSnMU?!(NA9lnhz_OV*@rG;M^a-rt0sr?9`ibt8XY|* z`h&a0SUaEB5(Z(9xpbU>0YHvWVd=(ANh0qxqJ~CcxrHQb9w7ftdBHxW|I0y@Y+iN0yEkeU{4&s zUZNl{2^7O4N2m&IEnqE!erQQ&t0XWvDWQ0vK(Up%Hbg#I^3R_rRF482iGZwJBtX>&I^`1kCf% z;@r7fE>b}jf6}T-g_1gN@v~-D=%HFsgZbC}P5f&=Q#gClRDP^-ul|9$dX*|dWfjA> zmyE>j_yKlQ8}Vb~xVR%nKZunmZ_UJQXFYih%*oE0`FM&UOL~KMv|HVORabR#z3G5; zbu;c=32pafWSrXZ@?%HCq5USO&>c-YrJQ(wG-?7$)OxVQP;S085?@#{h)Dqv7PP}< zY^4~BfaaQReL-iNOHo?&Txu&sGhM6^ZI2JIEsjkECQBowqQOyBp^hl#VAG$hY9U?u zDs4L<%tXD#A4Ppw#Js?!LyD%9_O;)wX4p?oD2^c;`IsbxpwMF2mxN@Z_#!KhSN8?k za$)lRe@FCPlz|dw1nbU4DgKT*ffE8BZ8T+)eAHLun;(cEBVeYxAdw@a)i-UnI2OFf zg$sCY9#@EuHAUvaraOjRmamBhANGrm>a*qV5=549a>Cm#R-=Z9{~Rm~#&Gi~cyPOwx1`EA8jrV|$mIF@Y{*&I5>2 zTR81*#qlG*%B}~MJ2k+*he_6p_TrK@R1|9_&IpH;t`ypvSOMOKt<@Hd-%mLnhrE_H z3B{0jb`-DlqSY9ifG?oNk1O752RQhx(tq*`=ufbIS#s3gXI*eA!sTT;eDbU9LC52X z%k)kJO}Vfq7_tuV*&Ny}*C2m~dPIGo`2vX!0?C*-NT`2-Y!F7dH{#zGVYLmOqQTz6 z`k!jkaw%;%>+2{2nk#;WgyLAZZ}@FYkxnZDBFw!Q?YMxlu5NK)+qjh=#jP>a zMDNymDc_izR%x(&dO6wle18r}bzUJplb8kmJ*#)>RHA^sJPgRj=Xmlwi@u&H8Gp6r z<-Ye=q54JbmITdp0y4A)V5zxA%{&={65w8r-F)K#3MvkV8XQfKvA{}K)S;Z7JmnyA zW5V%x8Q@A+YU>pA+X*L{Lrg>pZocTOIgNbdV-K#(ngYFdlBh^eZH2+mvWo06j1^f= z3v>eH=-t~caQsSLt+CoI8XbdC%1ImG;r9rBy8!j1T?Tx~k+^m%TVyi>QSCH|Gys9J zg7JIRDZ?tiu1v=q9?a>U+?`{HBFLi9t%A^ zZ|uy;!MipH^#P;$Wd1YiZQ#co5@Aqf}j~S*KbovCMJ9%-+i-0d2TV z4aP{kAHy&q^~IXo7w`|92-pD7`dU9t7o%uJ@=3mUgIogn2fm5^)EsjrPm5^=yLNPYe=HHi{Ft zAJg?yYF(;duyLEm-(?0^fm5mJm@~^ihss zC1P9H($^GgRRr{5=_Z%!z7u2q20}xk?zG^hP zq1Yf&?mafz9;wlBxw}a!Z+FLw@u3w0tjAS^+G%m*3m35L+BkPon9x>OqR3KMJkzqJ zt(wA!Pq--drCn>@Y&mRahX)!ZV$w=5C6{~_<5lu*&r}x1J&MZkV@o`ZnBpjk@rioj z{g-j4@5hE{0&hh(tlJrE5^b{8B&4Px0W}EcLi2GZ0)~UCzx|R+O}0a}4TOAN+pMO5 zXO%i1hp?W%uHkxPmBw5(*f;Vy^sE?0jY-QurYBbeBx>tKHuNTv$?IPNbtpu3QbU-r z&4cGts_gM+IWsRkL2m@q%Nz0wDlv5;$;OWT{y#YU$+-IeLrc*b{w!vp)ouX6h538n zySuM7DTTkjJ^;n%tHWi!^!pwo!G+Sw*icG~WHXAp#dHq{Ztz!lZ`K?j0H*cFMu^PL zg*TJT5%y2t4Fr|G&@)H?BMqtlYAaoC7W2_`(=Sx|_zy~k%ccOUMuInFpr1>;&rmuR z*-d#`Ri$?U!(J=G0;Kj;^~oVXAsEn<-eTe~Qy^Rn6Zlo^E zSMz-~tMQI!4U-u|X`a1K?eRzqLW;{U4fd@W};cDJSzI#GnwS(-P@l-nz zAlCF-SgJBiky@S-14xY+qeA*VDJ#h)^gFg!Ij^hLUj&cl-m0xQfP6k}Gz#}`jV`4L z{>Ju+I=4_}_1;v^&y?@OmI-NDaE)36qE$igt&p3WnZUXNr-%s!POgX5>7VvOVP@iG zW5AjyZoSv!rhd<=8sG-NY@si2k|H1I0XD6A+~Bphl>7Qz`HF9wrlYoUJZs%%YCZa= z?93Av7%%kn2jmeOLr7Rb5etION*vd6!PU3ya?^B~WWN!16IozxgrPe08OEc2adItE zP_BH9(gm%=k3^mI%PY2E@QgsAGDUs6iXRYM9a~i3vrL|R*qPjzWkxd{cbX6OIxZG5g_Ew4yq^EFPKYQ}m9U%#`l7q{?HvPxf$_GPz^ zXtbZ->!#RRUSwF(oa`WeV^DaP0$fOB6&}Zp4HZ^#szxbFHgoeQ={(GirlOgL_6B~v z%R7_xRZ$2>)ONaM=d>(cJTRpJK|)^o5nDtdj+1mA3oQv6^Hag)0CdBSAw8rc6VGdpLMz(FsSK|KN9?>CRPKR1g;68Y*f8UJOJA z&6dd`tc6G(`NPUve$Y2DmfFC|m*$Rl)0`OL4ABmfLBm2|W+5Df5V>r#M*~`dp@TJ4 zfpD?Y6w>K76pIavo)B$8hWB3`O(-(Vnc=@edx(|(hH`Pox@`D}3R^nDCJ%Pho-8d} zD1mYzbCB(l0WNEHpKIOcQ*%{Ckg#_r%JVoD%cXx0p`e|4L(Gps@1IG~3LypBH(?@Ze}hA}&uQ1=5y7QZPV627_^jA=kn$G#z2<;N2IoqJAx2wL3RYT6 zTc%Dh#kDeWD+Az5JB$4KN^qL%jtLcii4vCMm)1v}uf~*@`z6MW_3I4LV~oF~z}lXGjLvfO<<>j%YdW+kAFsl%#w2^7v83L&#P-L^S+*)%1k z@pcKlWUDL|GWEnp5jM*qKx?^4u`!^$uXGn)tHadTUpf@}WLBRC8|j)M4%M^V1iMib zTV{LlVvRo7CkSH-HWhX#H2%qN3eC;EN7W7x`z_GQ8LF@xf69wJKV-=T1;Z0EJIOzf z4V6$yvT>~Mo(p`;(Db4kEI{$sS@6vjoX-g_x+QL4?K%KIE&iWIn!kpl^=GAOt0{yL&Dt@N zK67bj^`P{pga!D=XG^?30Y=_$36cqKmx*r_ifSO3yNd1O`2S z2Z-B+NmumL5ytRx9m7@oosAO*#!Mx<6>^-;EgfzZu}np!%LMqudNK47 zhz-?`tZ3nGf!aD%s7%QaX4>xNw=Z9z;F9KWsC9Pq9F^W^vZXCjLf2A&1s>yO^0xz<`ceFY}o5i`KS=4-(DAvT{SJUgrOeAsF&7EP@AlzlGdqr8ScJQbm236D zm09!|6}xKl{^8A}gfO8P^EAwvLZBjt6p;;6HimvCSsT8?RMu%&z?QIvm)Pr?bo(0P zKYwjMzK|3+&DH}w@AS$?e)hm3mA9#sw^uW*`RoM8tpySC*)j+e$md>Z4S*tZFDD2W zE4*4-vqO2q@f@k#7r!71!)OaUAO;35WuX3LnDlpIDgLW;J9raL;5G2$S^cC9vX`tD z4{kYdEUD1O=7(PAle21D-!4!zF-B7mWYAI)TKU6XytxkMDuX9;*+J#xi~8L+>F0qu zc7z*)Jis_JYC7S=?GkY=ek+u|0R-wUYIr?96Z?OJdqVk2E!UkylPDii+w%)vh%puq zpYbNWqM@Lnb)mJ(WAfBT@Z!!Ya#&-RyBx zIcPyz?RR2@m}+0Aw)Xev!a*|GvOZxPl*^B?D(Z4V@U0E|^NM9-Y9hr7p*cGNc6?O^ zqn(F%-b^ftne@5F6=#k9x5S!&We0-kTe|Vz*DRrBIGZR(3r+5+ioG5-LieWiT9*2pol<@QlMSLlc zKN!OluO?pf0-ytUg=2@!Ppr5g=IjRWkir8Q)!ATiWGypiQ4Zi%}YxN)CvaV0tM=lO8de54lI*r=sLuCCBV@F!#GSgV1zJum#8oHR576G`0mg9`#0eD;oc4|xUVue z1X&HsL=;@KJ6W9Wua6D;qhGX3nE2l0_4%S&Ha(p#hwzAo)16Jyw(ZW`?Pxfk5}53> z*~R#CYRZ3o%_kawyXEm5&lAh;tgYiUQc^`x0h zV!(XJQ8h!l*rjzF~1_PqAb)UbgLJKD>O~|KFA(&jjOFjA^SrYCQ5fJCcm(i<5J`q+fjufy83R(C`Z2Utu907`7N3d2s#qR2zB1B9qZ zy_~m4Ul~9)YAkAwm)^j`rM`99vLecBlXO~iS>jMI_s>8G_-)#CUjSNn!Z-Il`poU` zna`A_!Y&rJLUrvJqyoogNDXJF=*r=92rBI$BH}<*-^`{33tIRU50fD%+RixbOg&F= zHA#iu1^9_6lnbha|%qzJ^#q~aHk7hCffKOkb~fy zzN`loDUcn>SzoJSnKm=fw51)dA~Ga2Y^l!eg80N@C2l~0^>%4A!2WD&l#+C6f(_~Y zHwBK4xc>Wuf&vo!6k3);kh18RicEYB-b^&DEy@!*ua^+Ti&nD(T1Hx*gEDo8_i#I& zkVuhbcX;UMNYi_8V!DuQA*$V6l|$kMJQbYa2fPQ=eCiL{j9zlfP&4Kfku;C?3Qe9zQ8>pN_zd^JGY178r2F@AebT3|VGvno!Qyg)U|7%LO z$g(*@3HFh#AArgtw<1}=axx^k0OWzjYSUpn)7+6M=>73|@d3pMl_t`Kk_FgR+Y;H+ zP5oVNT~XLllVc2+>gbR}O}%tx;poFOLah+3A`yabnZGIhJAha3 zmQ2v-n=rI$d(#1oousj|4=r7H9lF|1o2-Mfvf9-C`a-z>5$;m8VYBg|SgadFJL0ys z?>$}wSTGysze+}VEZ*}^h*8gQ(>?V6%OuqS7kpj84d3h9J8u_3MhMic*e;D16E|p7 zm8DB5m-Snh@P5y;_xK_6T$A@0)44*>dZ*lqu3u}d3;j(em~B#%9pM&2S#H@_k#1Ja zTm&zC0;fB~VRQ4gYBWR);O{`PQ$L?9=ou`tdIqL8OqSxNjmdDwU`*kBSsbEid@j+e zUu>yic3UAT-y&po02yx9E;aco8ad)kGNsk4%2MnoCCcP)_t^b6NcB#COVe1W<~{9n z<@&o0t=Bz^TEN?9Hyai3j;_*v8Ht=l9pO?EMaEsq#nMgwrcfjXn(*goZV#2JF;@aL z%KD&agO#|R(xhiV-^HdwfIP}>c;oYRzkm$tKJI=QzU`eww7M5leey`_Eu5*RW=FVR!eD(#|sNl%xkL%@@#xY zYzzIRLyML25Csi9n1!#GxnX!zzF*=6 zk+;3#P)?Sx2ho=hiZtooeo_wtAW|{)FJGMKI{Yp5_sJT)tfgygUU(+%Js1t`n_hip z?QaVMf!AVz0iOLngSL$}n3arAp(F2;(6hZ_O;LMf~}A zi!=O%_`pW~J|-+Zpg^6=wfC=?1f(>a9KkSwS-Y0M6X~s<$QKfVl@>QmC>k>|`FKa@ z?gy#TGRjOG%IJ4^>;~O=A2R`xZvJIp?k~x%xd;s~4)6{rE=4@t*CEi@3u(|7=^(N3 zTW2QLm<xB+v}1p$J0au!G{Q_$o4nMG4mbfcpcXZD|QD>3Q zj{s8&c!g~nH#yQ&dLlc1b>;RG2ycz5`>=*WP0BBUt#Hz!_6tS%H|HnAOvM60r7l2^ zvG!8A`t=)Q7K|AFb(P3jfsOdp0?o)fWD5f%Y>ZzpZ=FTK25uU0hJQ|K;9v{=p7{dE zf;R_RvKdX_Rq!!F;3q#}cG4=c1dM(@$wRYQxfw{){P1L>rd!^zmA+41Q;(!|z|*zE zV4D>t`-bQ07VVt2*IVMM-k}jUB_CP(g+1R3oI9#bZTdJ~WTD$|?X!A|u-EKIa;UGS zjQ!(U$?^Qf7o)GIDS0*}Mco5N^>i=6#LE}9)O(=!mUjhnN?n-Knf7Tr{AGxq?R@|Y z*IMa>y}K&?aZ>L~$H#22?U*tI@b9teE-I9f90Y}%*iirRk8Dyy1ND|K_>GQ3@@ke; zStCz+M)}qLtH$PWP>6NIb#zaHqG&i}X!+!wfdQ-oO53>nTJDa;N$GEGI5(hGxE&$y zIcif)Gc3MfA5~wx=+5ojpwZ$Zzwf-F^fv`P9d_rJZ=;%T3HI7XE=gR?D-C%A``#;J1uLH8GjZ=5bm?R#3;!G~n_(W1Mq`A?$kiNE)%513+%V1 zF2wb!9q{aehRpEQOgS=J%3Nw$QPV*mS<&}62>W7!`H?;#1HV_ufd=`AChq?&rvbKk>?t zKztBtOkoA~5lo3Ymm36-ZSuPc)wKg{N^UL|xySV_E4-IQLUXRvFw#z>#`w~np)|`l z%A5d+g4KWMI*g|0H`c2A{aZ!(dg`q320DWebAE+>Z#qmize_KSOy>J=T& zGEE7d85soeN+KJv8>4n(8N{FKc?B40&dBB}7a3SSbg!jt>?Qv0RHzP3BCZ`TYWOMQzlgxk&=lT>6(?)m+QY_LpD13*p~ z@SBgAIjJ?URfGx7y~*%`^CbH~Aex~K`$C#pW@OSf4tP9Q!o^J}_qO({ThwoG4WVa7#X=uwifnn(z#{Gl8Sn){1JO|$Vi_?L{mTQj*_X+(7X}vDi^LSAotwpsi3&8_zC-|vj}GK% zviq|30zxc^?xizt+)t!dm;D&J+RnP96y1w;t?;g})=!WLEj!?pbOL-bV>IpI?oIyv z+k*XMMWiV_F7A96_9p!B#80s03JM?58OD&_Ya@DI7_ckuO~VG z`3w1_OQ zW(%##^R0emYrZ0CcePFBC*ECsu~Duhs0 z?A$1+p0Y`xz0&}Zyb6#40unLgmO0!i8N4e4xA*}!0-?(T$?(zMa~P4J2dm*{;*zbh z@GMi9Pm*m7caf<|5$nTpM)3a6! zhgiC%o7_R7Muh*yxN^+Je;mxcAUzWxJyZSf?tA6vbop9;4FWPw#1~O-lTw$&PL6^_ zj>yJ$3PN4)o{PtIY~sjCv}A0+@n9%OY^A4FKsRl^{7_RJ7Gs3M`N5p&fY@}Te!P`* zZXa?qmkikC^H!kiqRIjSR^8Nm4Bhj`juw^DX}A|4j6L%@n^jDZhWKpR68nL1YJ)~oHoAjdq6Whc;u zhWh_ZA1JE!X$ij}UFuH7h-3!N{iU=vUr4*?D8qi4iT`UyvzfrT|H$*zBwMH5dKNRq zN099^@Oa+3=IFB_$LVVrSt0#;fzo1{lGgFtKhB{^_LjCUrnWm8j_|kL9w6UiHR+YqHpWfFmR7!rV*6kLMR4z##tvu zb63P_{Wszy1SOq#$ApO*gWz+0REW}y2jJ4~Y#4@}z(QNH5>f*Lf3(@lp5sTeYGNq093xzG~j-Nb87*ZVM|OZj?=l00+`8aA|t zV5PMuWlaJx@*mrq#VXjIJp?+H9fQ{Vyq2jD^~P%&qfhvHW}bi{duDeH+{-3RUhCZg z^cl%0s3ih~yB*xlcXa%5V$=7ivD;brTj%>!aSH8nz^ zUz$b%hT2gB!)Y~cKkx{6LbL-vpc&Y)5KIh)%}!ZGefiaJ=W+p67m&p9;onbHVCVpwZY4>U09$|pQ3hQl}@3Y;1;~*aHi_HCI>yD3w zoUesLojjAi_pN57zpN`$7DCe|(rFN9Vn{?KtYf#yQU=AO<9nuQs$ZMk>M_-Aj|Xpb z5U}-3YUTnBsByP7lxI51LJ2*#|G{%S-M0b(&|0Sx#@06LET1$Mgv{dsIyWYXi>kDz z6fSAei0HtRt>lz%=@teO24^2REZAdxTiR_>0Px;_({qS|t>S{D&~cp-ahvKi-9po# zQld6LKsI?v@yqUNbGu{k@&VtXvphL+WX42L?E-=I4N`{2n!u!`LpAKHHiRbhl0Y-% zFHwgrEY#gw!*;QL6Nk#zX5>uQQ0RHAJ^m^4^M|hy3{YWxIYf5Ol%s;U-`-dG;@S?i7Q%S_XzbUc{dA)f640wpoLa^FRFwKDnv#BS zb~XNexRL=0k(?}bEh#TJYsbz!)L4{8+yY`S(Q|yCNvOb)S!Wq1tXOIc+(2joEa0tr zuF4_3PQDMqp6+pD=YGYU{vvA*N6fbSxr~{)FD;joYLB?AeCMvmrWo$)kPTyQ_G1Lt zKKEoQn>=+XtlaygYK5oy&zy0u(b2wCYsW6)h2@<=++%#p{&8v9CYQ837y+|viPdF# zi>^4~yS16^of6wl{UmPa84kRE%w6$fPW5=7HILi6@zvwBtMBsrI>1AWM)pT zM^meB=Kpf|-6zbqe~8s#<#P9*!bwAgxAWojxgaV5VF@X5faKRp{KzfqYAm7phd}zviX=SmS9iRcQ}*d~Yr z&_^1Jc0FSxByv!p&KqEUHe-%3Dwx1|04#5Cn7_tq&irKa-%ka>eM1*nl7HEmxdLGN z%n$3eHAtUw+hO&#M=^og6CAO?tD(Y8u@QBiEjr9+8PtvA4YUsP^c9%rYC@ck>FRxi$^0_J}SgaudP758OxNgmdp=r~yOu z%lEDinf%Qtq{gl;y)lKIPiwmMRw16LUN-dN#g=gk{ukn?ipoRI=%b1I`+;P1S$t-; zH<`cxZo(>!J9AN~+T%C|7&{sA!{96N5#9ia=>e2TJ9?UXs4r*Xzcza0YJBLrR!(hG z7e+Pjp}dVvtPrC(%=fWLX;Mt3NI1>5ehs~KIe16L)5H3_FQd#d|9a_z&1mfEG^VA= zp~H&+siPt=JhQfTYn1PE1u2rE?y?0oBJC3T+7|OUA!_T(26fU0XPuni+98$gptl6FJTZf zEkYR{yiqyS^3qqHL0O*218~ctfQ5QQtaWk24F^|ld=1orc(?+f;kH(m(GaKLcHX0D zUR)%8a@;HuUBs{*w>Ro&(8nYT25RsQC!-_RJ;NXGM1Tzv`{wP(@tGTS?$M>~*b#_6 z${jWbrBHH0w+#Od_a(HG^2)(-YRiA*gQCmBX+L7~PRj8N$^r?~nO&B6l#V*l=v#R< zv4cd)SUz&~djT9YvJZ@>ypv^<+arG#4IsJsX>L$tvaM6Xf;m`d0T@=(hN&P+zLg|W zCMUVJ#omjxmMjWX-U4Rtt6QixL6+1WVz^Xhgkc=XlBElS0dK>>TXgyrkquns(1eLg zg)bXc2zC^=*;vAJ5}2bqc|%1ZcKhRdBiH+hjG)I}$T+Q}bAa6J4V0Qa90`nQ-?+sNc{ELCl363{eu(YPSvt*I{) z4Q6B399?Nl@@-kr2aO5>l@K=o4b`7?70?-nm|6TuA0lKf7Yx#8ZmE1il23G7#ppv) zAILS|KW(k(m1nk#sk%G@7uva=LPylWk2`h0%bexzu<$pw($G;$PliWGJ7INsP}?%K z0$NLts-BC_Y!zS$M@g0uq5bScuhY-1;0=m6A5ElE{+^v9BrWDgua<=Vm&>`J`q(YfPn&^?M!3QB)=QCB$=%*eh~$g;ERMrfNFnLe z^)ox1kIX~5bCORryXX$JpU5{8UO60@dcAmd>mYe7z;Yi~wb@5yFT%8x z(i1_d%CMy4pNK5U{|@05Bw>Q59n-no0TotCF`}b6Yf8m(lpB(Wy;HH>w|o{})LUYa zNjHshpAGo9*)u$w?ms_F^8Uj@j&`CliJ{S%K_xSEM#Ltn>(KM_`ZUp@FXG-NY`7e2 zO;XE=7!UIX&#ILc=_VUWgRPxWFKexJM%sGXE$T680NE6@?{|H0Tat8R2ezMbuMI+Y z6_bhEbMjnu$zvthU<{#1=e6p`5xYpmq;$Vfh!2i%#K^CpMNNPfZKVn>XC@IP#{?5e zn1cFS3*mA8`2?h>)kl4`&%zN=1LdiR+yRj`0sBdl;Hp ztm6wb9}=29{ift*%z0r5F`i|z7N79^EJ-dNX`~U@>*fDPhFu1+g>6bb&2<16q3(5R z9zUjqIqkl&Vt%aGja+>3DU69nq(dYK-VYBOu_C$4$y11Dx)pk*l^Nk*k>{FTNh0Vu zIGvZqQbu9a!<2>V7oT!^UvhR1Q&)6=1fEw;GdriSySK*?Pf8_trUkFchkyBA@zlnn zOQ6T^1m5VbUS;~ShbkhZdlhH+M!4OfXRXEkgA2RDbBAbV)Mu*1l48LF1n&J3uA|G3 zTB~%xo6Bmy-4ipxwvE+%ri@-jAQB{BsLe75d@ckm7!q9{lyB2C~sM5;yZxMywn*Jz{sh&ZZ2<9NQLU(K$a6Rq`NO4|x&SHkkch?myh zbu3gIBy}nzgx;;aXo^MQl$_tXk8cQz4z1bUqvnKq!M9^Z)xfu!5Gme^W>RjDls>9b zKR?I!k74^LNpF%pXC!mhmB!iOb^Ygvi((*Yr!>^K?}yYyj7s(;}^q?)#(L&DfkZnD+ct!*2U|PYV(PwE^!&PB@r`WD*=t2v%-a5$xP9|6K4I=KjDud4J5^x%k*GdduQE6>_dilra7%v%h{ zB0+f1kG9^}Y`}!iDO#r$&Tq-i>_zw^kDQCdAxdwMnI89yCW3NMsw=< z`RmlmNP0rVhb0^S2cacX@3=>Q2b+X|8)jRsw^Y##(exhxIWKm#Cg5t179ayKoa(om zeLaDd8KXy341=}jrD|2G0N+1X*6YLwHfOB&w$y5&)6{dPyR`DgrF;QAEP0it zL95ySWYz_a^C4q{g>}BizHnXa-IR_wx_eqCLOw&bo#gtnoH7}tKYhKWdS^pkT}d7B zhD=F6Djb|K7sm41>KZIHCjKW&s)<_td$VnXOkGM~G2~7H*lrt&Z4SMiM94O8y3AxV zGni1=cfbgqVZrK_*40y2iVb?{)~2y6D5L#BeZEaoa+jZlU)*7VwiNx9pB#J!Kh_rg{Qf z$G*n>wHL6<)4z7a((^nxhzI8k9+1h9N9f&!i57LglObTQo9+V#1!2Y6g9I49=?C@$ zHR8>Mf3^dh3dDB7;s$wR-JV|8$TORS)~Y97Aw;rfc4^@L<~weFy?Myn0#4>sVVv`5 z(oY2UiS!wn=ZjEvrdPdn0S|{#kr~KCRAX6og@RB@p!pJ>-V!USi{OEhmnhjMlKg{eDn~)UCReL@*cocUGX@pZ z_1D(D)BMNqMW09LikalUgA#uDJaAjy2Y7anG=w^zg*6}u!Vj&A(0Be;mq6}rI(J~* z4kUV8K9VN&^u=bdYr1>76HIawMK@~uEO?Q9z+;!;VOp+}ELPR!8%jY=DlATI5&66j zl;b~YyD|p8$s^_5Fkr?0RgiH&?^Wkl``EL&ovWYz18;pL zRh>gtliKV`wNR|kP$=tEw@h$ftYXky@=wFB;lK>=BjT?O1hl>C$2?c)Lq$i#=RluLE%bkp}t6lz+UDoj#*}&*7U(&6rD5_*hME7fz&WF@VW}z)|Ps zWDWTT)*RQ*&45w%w3ey>&8<4rO)8pLl1%QA4$MekX8}4^g+8?U~-Xf?@sHGdPy{Ot&*QvP*Uy0#Ee?vJi! z(2Y3d-(=9nLq!_NgKl49r-zZJ5Fwl7g+^4w{ZCmR=VEol1vm2Tx~xe(bnBv#q3Zi!Q?lwEmY*{N;BJSk0eZy_dhHCtZSOB4p%s0=EIcS zqLH@Bq^aD;z*|vsq=ewR^iuoR_+HNG11zN>AjMCWlmxWd+aTDkzcl_FT$%oJq##Y|F>l<3cqpm8i=F+@%A#Owstn(^J@LI_*cWj!xwtjhNZe&=TRn z1ksSC-V`L33u`7Nu!fW1nW7hEfeYdCl|R$?o@mWmTLzD!S$zQNQ^PfqT-mT;v_Vw2 zox8Bg{#Zl&Vi%`)0?u@C63?juZAeZHJXat*L@sp6&e#ief)qCqSGknI%yKw3a0Nw> zs@=O7IKRq|tsKe}_aacMi%Q;l9A(uZbaI)*#|F1DXz+hT>3NZ{!#xrdKCrOtK00!~ zj~l8g_q(Y!9u$uU5Po^=JM3-ef$@^Q!kXEJOHRvA9F9us(PT)R1SUg1-5kFtM*nLg zVLT%=z_ZNW`8EN*6ljqL;3~|k8HU9XH#Mg7Zm*MVQ_^>a7xH>}V>8U(dgHV(D_4N# zuMov(M62ku@D8g_Obq$$?wSeTgM!_h;-jjk zEb_yzyx(Weo&d_i$`k;%op>esas_|%IaEG$=^f^ziCiJBeNuwQ>&K;hJm!=2B4FVF zf7HWZNI^%{BIeJ{milZrx zq2ub50AFM7Flf`&W8@bM|A4NbuKOGBi78o8|2E-8H|N0-h!KJ$Uq^^h>q46~R(PP3 z6uLPOmFpPlsVM;ZaZl$}r!@V)^xt?M)OeF?lEYj^RQlnxF82Q44Jl)^g#E2P23GtP zy~lq58(wHVI`LVWjI|edj&87{Lq>t?fpYvnD-@0f?9!^u9Chd|;A$j&3QkzSa4fe> zmQpOFGff-DVgVl%j60gg;)pkUaPV*)xenvq@d(S<+eZ`$Pu&v{LYDrTNmYe{pK}=^ z;?uS#dS*Y9K}d1M$x>YND8sh>1b`Um%gn3VV6PLl$F%|f}fyxNz4Mp)(1{X!5{~00h^jKSdu$_@5SsEz^|wCs}9SmgI*p{ zNs$eq2!noL0OrB)G~Pb_IDv`G!NPbLsKkY#_XO|UI%igAS-24Eg`4H80+y&md#VL< zUgUs-o@|>DCuDs~`RFZ{&Io&tea2_L8Q@MJn!VKqw*{{2(XVQLUu?031S+*pL{@(QFUrDzgDXVuUgrNJB57OvLv&_F#%fZJK#NjCgXe~ahhBNVO)E$9 zB3`>bNMuVK3ER7u*2@_X(fw!p?vovcU&G4*+9d5%^nuds-%0Kc*%*Pr%OKjXrkMxi0V~ zK|y`|9Ffw!)l?Ru4GE=IU)Wh+EEzVQoBGM3y&6U-ev51=YMS=DpPBI%(4b*@BIDbW zRvK|C_?=<9Sw{6e&&71S)WF-gF-X8Raqr$HNY^nZIAfCco~INkzjA}?P&*5(IlSk8 z35-o9;X`|W;}+j<0~I4O#DZl0Zy;?6 zb%+^Wq1u4_P8rQ6I3EEP`Hd{7dPS*>;N;_4E|2r3zP;>B1tmvn7bbHxaz|fuN&?B~f%PQ<=4WVTwYI6yZc z7#?Ec!hfB)q21I^2yZX&RI_^V&iT<`!~;o8VSKQyVOS^csYKT8o4{Emb%x!35a2ju zu{6W-fB<;sSlhk9$yM-f;1JTj2|ztP54YIDz_!23?1Ta*EyUzVUnXP5?Q7vVegBti zrWzbG6#Z_ZP|M8IH)bcdX?}f0_NWcpkOwv%kjvI%?O3h>oOSUhoUOIM?Pr&05N^;; zm74^O@@LVXt}cfq zbw^7E#$5!`eMS)}R6&R<)*h%^xgng*-v;q*yr708XJK4M2aJsFiDiEzrfYm=2Ji3Z zsc*p4YAN|#ysqN|B?wwH@Zs3iE|dWjpVzrLE2AL+nFps7rFMLBf2HJ!t@u|~{;^k8 zzS8@FniTzXRo^@9{^N&uDO-(?lO*2ywoSehK&a?#lYa85eI&WP`;GNpDSE>$Va#pc z$L_vgVtSt|+oy{)xQUpzOx!~}rx)BiPb>|jemyvKyuGU69(=i@!%qxARYLY3UN65t zJ4$Q}Q76qVWK1ap`rV~@arZ$_6_h{JJa{a#&UOcdg`@J2F2QOo^|Ps#olA~Cu8q0Izd*?C!O<2-((hP4YC

sKB>Pa&hnRgpwSqU2HjQ_91U9>e%HoskjGh<(hE+p+o5iuF#Admaxq$45us@BUnyQt&t% z_y}SWzFH~26Z{K30oQqmZuYQV!S|KS{B;Iq9^vVv=MEHwKb^4Uh_M zxWjf11L2XT0JeXfo*`opKt%}smzlW&L22*AWMGtcecPXbNN5Z<_*iMe7WE0$C(kcveTCSHr@i87{-H_>uS>DJjmJMO(vNH*y=Gmx@wAxD> z>+N&zH;A=$M@TT%%c{?#(B@zxQ6vqthw|4TI<$1L{(sK;rT+VoaGLvw;uC@naq~GO zAKg3m-Z$D@>*qOF*55}9XFWOBbhm}Xy&SXgVk_ztB9(*$aReN@Tugb8*gJai5;KcM z-z~9iBXPv^VU`a+(-o%WfM}T?Dru{KvL%HSJAJ!!#sVLxf#7VD0wonzZK%cUUt)P7lbp>2^a4u|{= z$APj$X_JXX6fWC1_!NRydP;b-O9e`@3(3v%#7%wMVH!bJb5;@1;?R*5Gg|prN@&Q# z2x7wa-CP#2hX7MC-mV5MRl#sU1#6AvUXL?Sl9e700nE0vWgzUZ%}5mFtRillJNk0V zOn;N&<+ht}*9=x?)W?qrm${keho(({$pc2SzwUMRgQKi}%TbNcZIy8WZO8twI5f^= z0VHTugX?IALSm&^j610mbIeWbuY3h?2H6@0@U&q>GLkdXy7QLx_A0X|&}PX+Ge8Jc zNX&B|u;-+Bj&-DKTLb% zGaBRO*}>a>cEK7@C04E{BqMqcgO_85@A!KBE#{(Zv{~GR*M<$)tKo1Q@w_Bm44kDt zAaH?2#hR+Yv$ueZ7 zb2Owr?`}#NWr>PN$d1;CTD=13M61tIoe1^9@BlB+f@~lV3Ve?eaErAu9@iDLW&f6Q zO}LIrWR8g=l(uzA)=c092{XbScuArL3z`n_2_kIfRDKuFn-le5xr_ebrUZl4jXDpx z)@0Fjc%LmZe08fxRY)EmgI6q_wegcKTa_=t4cFua{mRpPE<~P%s+jt#aHX_(SEI2K z(GPqK_vErg+Uw+Mki#O+gunM-B?0K0=Y~RU5ouDW)no+=YAmuRtc?9~Jc7ggH0GLG zEYa+rFQGc|pkPtEMF*&0hxw!CQo$iL;9SEoNW^15*KIIYj;FiP(|N1?{RuA(+o zq`g3jT{RqC1uUo}GE*Zil><6KBX%D`)B^so^|!lW2!Z5)@;ufNFK_p2&1#lSevUnw zhw1Q}KW{Fs%Mq6gjXUK#xZ|PNXs(An+b}sRr|1=ym8uJ|BrN-l$rxnj;^stzZYVSmJ zDvutg=9PZ+uoExlx0`L~g)_9_eJ}S4opwvU1Uhu_fYe&7W;{g{jLK^wX_{svTw2-R@+9K18~q#qQ}`->RZWt3w-#AON+J@9HvA zZ=wL6Xs{LO0W(|&tJl2g5Iad50(exvo%Lsn91sy?z&Ij*$6hn*rKAo59+F7jZky#q znw&0)$eEG_8DVX@)3CD~`Q6=iz|ZwEvG>>N9q%=!-GA`n{lr z@3+s>w{u&V0O-PuUNkAM6{YFegPQ`GCt04qOyR7%1@&t~vXL733X5jdKd#~9Y`Rs5 zu>^_s99qoaD0;<8=HU>0IH#^zwowNr{_}gel)vmrPXWXh=YAGPh#LtnKhNWRfA5Hi ziQG2(R1M8{`IX{cpMuKMEN!{SpOJk?fb^ylR3zi{1~z>t-F%HYaq)u`ej>TPr3SAe<}d``8p1AnNqU<_@G_e>T!UYdC;tbzY1Y_KfKAhOu%zB<+>WKem6H zxKR!><%S$qV271S6S%7tggaI~{O8-bwzUipO9w9*>`FR!d@n!7^iLNMs3mu)qSA@2 zanfgtzE&A5xOb12RA|hwt4T#}c*gi|vkz2pQVxd>(Ne^nWW3LIm5!3&M>`S|+k(`} ztLSUE;xm?pLP{4pEQf#@g6cuqi7tkQH;k%GGCo%AET&4AlIYsu4K3~MacC<)kcLmY zO)&H~(_&lQ4$m=`twO~E6?Lq@QXo75M}I~beBF4Jh|amNd`5>sGBkt#P8SuQZE-ZG z&COMy(HG3!CgnJ$&xGsFAkvxxZt8g0 zc>l6Hgj}Ocuy*GW{0v+I9{#AxQvKej=v6=DBIACDH$ciO2Ujvm2!5p*;CId1ZXNFw zZi|K_!^dy(1^}J&o8J?R`<&&>Y`W6BSXSAB@fwzX`eQAr3?5G-0AR z?|@$h{t(T*&mCeX2Y}MlJU5uT)*bY~Z&+D;@&C^2Vy4z)Y=b;>PRgj)flKx^f}y4M z$gE62tIY+XCxdAFHGkgq0n00j;jfXYinwyOq9RsVtkb(%5o9=tT+XWfeZN#H8HB02 z!OC7EK(v)k!0|A@%ui&?8BJ(RGH>$IPTxIfjOMuiSX145mM2~2>;P55< zJA{|sD00jcApZnwAY#4hGj6 zZMQVea`X3zCZm$4v{;$u(8`a+6&qXNn4U(Zw(onG>}w<4+FOj^2Z2lIZC^9HDzj>) zdtq?=XrckTAH!@tQw34lY%+@75~H~#RqpC!2`HNdegs6K!9Hkp93hb-L+2Sz+6oeD z1dl0O(k`(MdOhpnGZm;hN%}kOJxJIDI@WXkoij~eP(oZ%lAo`4PWLSj_5}*W`Y8t) zJTE<O)?ghkO76%fAcD+#wFddX5>8ncJ||kqMf+B{iF}#Y2gpDI7XV?I7E}f zo!pzC9LZ|#vUzCf0zMtSLX+=j)G%ak6>+F=tTfNU;%WZ(UrKZ~B}QFL53PDTzSt&k zVa9(!hV#EsLCM)gxnx@?HT7 zD}orahBuKI*vw+QRKdy9B#W6p&3sN>Y$bnCm@dw0vq6#+)v7T~B@Ox{*zW0>T6B@;90u-i8qzWQi z_;j2}7HOACxsM9H97^PtxiKviEKZ2tiU?@J>09>HOe@{Y=#}PwVa3&z)jJo~iyT^_*fL3xq3ToHKGPdN#P?vRUk;CgqN5JX--QFJ;DG=`uu*_8-M zV3#F0*_$hlgR`DqkpCl25$X#D@Gq>(RX5^pX!IuM&Sd<*t6Jcp1rxKBtz@<{N-!V~ zJ5nQ2LYS#G9*72kSauW4{wLzD08*C8Xcq~VZ8V9xK6$_hmLO{x`<{W_eSiaaFMFZO zR`h;h5S<-Kl#|1KeCK*M7!(Rb@AOax+pIdRwL+sdOpx&Jyz`IS+GY`0LL|=p)j`NW zDz`m4@;;N_^?lAmLqkj!^=L^TP7 z(t1J#2#*TKRRN=@Is@XYP2w(I?{?+R7BBDyK%DpSA}wAVKj4nI57s$?;J91}QUghgp6qnjX>XnP+mg0TGk0G|-qk&H3c&?~ zK_&KB@J%fL0?!)~4qgAra>(t8C4ZKb67{)^-uSwrLd_+yyjaj;a<|a3`WIidnEuxO z_i|Fv6DSht>b+yMq6KTOOI(k7AGZmQ=;|JcnOP+dY;ST!R9y^vCerP%1T^v*c7t*r zcn}I#C-&`)?8(A+VgCY@TrtAcye{ZycI<0es!!|!Xni3Escc9<)7vv*%KD(%)h}Y6 zBp`cMgf540w6dNV+;ix|(#q(l-)kba+`4l4vd<~DzmXoLsy3GYp3eKyC}rkrjwh@q z7vn=4W7`9R`&grrvoR!l&Kl7vc(v#>Ayc3uK$Ad)krN+hXz+LV0j=MZ)p+@XiNq)5 zGm4?&*Fz?%7ciH!s&j~$2PE$j92$&rJa?t<|9e`&=<*1;YjxLOd1p@`&Z+uJZ{_S0 z)Eo*)A-_DwAP(i5d9MdH;UMPcZd0uRWl@TA7~6Az zi%)fK?d*DVHy(X~|Gdnf>9h0zm#Qs>lS*?KSw6)zii&D2mhdhI&$@DXyPM?){DpfR z_Wuy=qN{-^^kK?)BN!?vMuiPr0SP9NA1sjHsx@#$CwB?W#bW+3GSj*7aOqokvfW`~ zy@xjV^!qqshg0EQvHyp|36p|;{l zqNt;Q11`7N98?wZ1xOnAe1;I>v{47OmAXCWSL)Vieo+ z6uh}>uOgIkYktu_00gkIiN06)#=Feg2&O0P#*4eMF>kpNGQFgF{i`P z0@DJLh`)WaZV12j_nb{*r#h$NdPf}=V=gZ&pu}B$>=%Ds4Ela!kK}FqyBfHyGui02ZMbX)QCWho>PL!Iyl#54Qzg(h{2tiv^5!BQn#o?bJ|SQ>(v>ZaFxc6+Go$aK~IRNyQ}^DDQHnNHuc{!yO8~ zYW#;Ua7DB+S$g9=u0c1Np`R473Q3b`NT{qx6xb+Th)K!CTpfUg|4?eJM?t2JA;DK7 zZ?nt$8I(>TIkmFrNQcOE!$UWC-spuD9-j)j%}pyc8e!xv{L+XJv5pGOuL1Rk)HvLf zo;;jn>_f>BR6e-WFu!>NS0Fg#Sa3}B%>Fwq^9mr_i*g2XJyP9W&@AtBgDX-pT!sbYHuRkjt%Qsg}bXSQ2`-YFKoI^kVXBp4*-w>z@^I^^UPS)>8lIsvX|yh6JD` zOv7!GXIo->DYY^_xP5)U8vQK!;A=DbFGlPlO#dr2izuB`t`OU&>&M_2v>S*oYg?nx zW_APwR(U!x<>G;MAPJ+K-drk}u7@=_T+HPjSpo>qH+>5WGXwrAa_~6jwq+Tllbnw_ zCN)pKx)~z4T1I^4hH3s6UB;lMS9@e`XSINo%;Zt0lUF18!=^(h`--r(V?WkRT>t2e%uAcP=~c=S zaM>gp8O}us%*aJe1UjA<{^0pZBJ`@icQdrD>9tTA=Y`VPb3t+b_Rh5jb^XkWA-obyGr!f7`RGTs) zI=RItSl$Fg00GR<(OsmD%vp=B6wx9jQVlxXZHn&f)G%)ssG}9zMN9mF{$pY5dGxy) zZO>!hJWXm3A*!){qbm~p?B*|@7j;7}o+4{`d*7SUP5}~?c|KBav!u%YI$C*MQi?MSykIxXIfS_bBD#Dp)6hlCEh6{79rli3(b~H?+A5=yNS!-OWNxJeLR1ZC-e* zO=P}|aw-lmsA>8!QzPDZA6iH7gu+0c;>lqHXxBB{BDvd%R=$Xf(Iwx57qp7*%0s(; z`imtGNfUcx4gfz`A>w5ZCt~$9)fG#e0v>i;sy;J}xK-RxlZF5tqP-n%CBWhk$ z5>t-m!07A^89)7iAXKFfMv-p$rd-Xw+9I7iGR!+4bjOrs6)FfOQP=QJ?pjQmF zh4dV1LIwf<-gIEY4QoTbHhP+geC7&tso)6}l?;g4WL2e)b$>&s96RI+DT`;!ECCY! zuT$AiufrFf3#b+CIPbx2!9{k88dmL{ROYu~c`NM&iV#Na4mZON9ds)9EdKho$MMNT zYT2&F3S8?dvD*b1_PsJnTWGXrjS?D6AW<&-Aj+T=7ROQD;FLM+@2>?vZ^Eg^s;rSd z!onsQcBa(X#81eZ(DnT3CbE(MH!-J+K+>F>SKJyF#gH&j<0uT6FE*;j3Yz|cxSEyK zS@8PAc3cL7WA?oRhj_68rR-NzgNHjHJ3ZhHX=&S&%8X z7mFU_(&EfD7IWRSxX*q#<~}v$MZ8=|Ly`FLuJmQf%L2m}k^%D?M_U^#7F8Li23@l* z4>e>H+1T3v0sh;^BDcq#FCP$Q^hLITNf6~;iEP@=SwC4pi=ByJd^}l}F~d@%X_GrwvdGx8l(~s(f7y6Q zZ%Bp^MU9C^(iK2edq-Isl4ZWH+mM%Ka(HTwD?IsjFqjE5ox#gHs}`xyPj211Pa*3G z*kC9mkX6{RLA2@5doqkcHfB|2U21TmzyjpYXzp>%WM2X*=xlKuTBns0sN`{v_DQb- zNSk}euZLCt`p4w?u3CNRFu~=T$YYv#kZ!1{UOV^N%Uc z5m^sjA>#WcLN`%TL-#J)Z>bxEd&S=IA}&W|#xAEl&L@SqjOWR6he-lTU5mPtIgrfR zEDy56BLVmj4nYT8?SJIjrtm4QxY2nV*Wj>)6n$=RcPMF)2F@0G(n%{VV!q6!EGt}Y z&qH?1*u}wT`cN1Thw6|rbK4DM4uEwcf_0h5l2A+dPi)QJ)M6}5$QW@MSb5;iu3A2# z;~T|Jg9SoBOgQu}Xw_fj&-}4dakYBQ_2>M!>1`^7?aZ&h830ij*Bs-4QS__JT9axM zBVnr&kD!+gt)W5mI)tCVJc()*2?Ink^0qo15)=+CLJ&Q5#)zFJl5o@Qd!2h<6G-~iYH}eo zceQA^v70EroTf3m8(+0}HD}zw#=bSVq)|}VN{_#wotwlK=I@{agF79Jx@@(^)@sZ& zRMDH-atbSet{~VE(pou}+>VK>i}a<**r1w?4RMcv&*I=Ci8J3${TQq8pUb%n_^~XG zn>hI?Nu3u+v9lUcyNOMeET4es;!CdGB?@k{!k%TBuwM@s!1zqu>HBjFmTwFjeQm$i zf2yUY$c9CA^aqEVxUH zKYoEaFxwZibvFq$=4p=O5&B)}!}wz4)A`dBW~FPNa`Utj3+eWeRS6D>ddzDp zGJtu8vl|~vZtbD|6K-i|FVNr_iwz2U#`P)s#RpBd!pT!C-Lr*&8@|U@4H;HKKB3H6 zyhUK*EUNJFV_@dX&+q8~deVD@4NE74U&SN!y!OT1asRNB+G>Xl_NRnU4J$O70cZZF z=z}#<*{_GxI#k`E-{Jj(bN}JTq50+VY6`7Km+WT1FAL~Q$DwPd3ds3S906bv%b{lY z{fUgFKg)5;qL0tox>kT4=5^|;!`ki;)?t2=z@r5oMXtZAzU6Sfw+z{s!peJ!WL0ff z0vN!tm#t#;4RnZIc*-x4>U9B?ygS|j95}Q=)4=dcCq#(t8rUtOhfb~$1Br*VK^>2` zJ}cBKsYn&#`E^lBn+bW*fSG#p%P9pj9!u*EjV<$&6YT`z5s-LToKZYaQvCy$IiPYJ z_G~y<^A7u%v{3*DH?>RbAoe}v-)N}m?QI1Ej^2P&ot<{3NfY@TR}xp=fiTf@8Y|d>F~QVa3S+}JqgsNE z70baa#=}MieFZ)FRFscp^0jV1&7=mAR7K<*B5cIp;Ems?nkE>`Ix;nxWU3hcxM#HCB(_lhs>4QghyNaHyY~jS3IftF#5Ft^lfn@aB5-`&$Z85p?`|e zvAuQiRiws$>=f`9?FL*iWbszMFb>EgJXEk_P7G^cjQJo+DULyKW?DK6IOV9r)Ict3 z-YCstjt9k$dRag&qf8n8GW%PxiMQ+(i!I$S=VuvV2p_kXKVKE1$_EHF{r$$4N332K zM-bG>+}1zH5rsb&;I7cYvB3YXFGo7<4rg-T>|<+}eVMhl8Qwy=}qsHP|x^u_n z$)cSvMlg|3k=ZrFA14uR>k9zzDxIIzxEI>X4)#Oki8C$}<-J0XRz6Ip;Hz;MtG3Yl z&!)2ZB3UBvyF3K`(mJj$HskV3K%!P5ByUs|=-dV0rlm8!IoR}9+W2^Uu1NxuYYS0{ zhd84JLvD*n|ME{o6}(`u05CJwF<)}J7M5_51Xt8v917o|)oLO{u|yy92%qGj#SLku zLQ5EmmtfB!@(f^&vI%9ZU6r$j@w33cuNhS5Czun8)bl29u?&Pf|A1P^Sd2!(t@kKG zg5eqUG%({^AmdSOXc9maGZ6Ls-XD?iHef`zfmo${9Z#8;9$oglW7?dY?g(;4447ye zhZ>vHjuf%kN_fxHpIMM4eezC{(W25D0BA+{fqss9);#Ntx22}T*uYRG-47))1U+1o z1y?6s;EV$*!L_9jk2Gx2VF0F6V83!BmjU}iqwEOcd+uuLe3S2DUR$4?EXa}%o-v{s zckSg?rC8#SYGRnwURgCr9$S!+;O)qw`PkWfhc)ykm`e-hDmnMxh!PA~L@6E3XJIMk zFdaMesm6kmla2)%qG*|!1b{c4T&@(a-ZT+}sG@vaVvR|v&FB`pZ!o)C+|>XRLQ{C= zzh1r-8P*UBvE}`Lr_y|_E{a(XV*;CtSx#knHA#v#Bnod(^w;52Af>P8t1iz@%<>YD zYLsK89%>JJJ~Pj_me1`1Af%76FX=b2Zdf^3C8)d@iHweIOawtBi{&35@9BUxbL}KkPj#6;6<81*sJP)oT%KCNgr%I8ZO@>^{%j6rBz9(3InYH*fxG&F z8G!BEnHV(WFUQVt>P%B6RtwZ@$!As>I1Q(yt$b9G9Zmqr3q%X^)*_ z(O08Izps@->5Tdz=B4Bz`~wfigl#i|kUAV;b67iKd&JXa6htB0LY=B{tDt4S4sU(% z{H1@4GCr@<4P$TwT)-C`y%Lp^jubj{9kKc;ePu#&zGwwcn4*8$>>J`S31>ZhOBUc>@fQw=h_yY+9`9xAJw;rjMp?-c6R|&Maw_kW z(%to{$<;nLjlbz(Bx4PVV|uM!B@VP}x(V>D4OWpSzgl^wG|w}|_7ytXz%hs5z<0<@ z9>E-`U6ocU*2Tg zWI?5b)*fhI?Ir==Hc8+caF;i|ec9yT%AwxF{o_L>wxKmfA_Ej@48lc^5r0LZb$+> zs?fw0)wH&fllQ7>5%A{lwMGpA9nGV+c)1t6)`h$utK2ACb5DUM`59AQ`a1zvf*=B)AJCuqLWV$-mX38)(6U#^N6bP=D!292Xf zl&!{Aaub?NOV`yo6XB{5dOb#vv?}JoW58+#JuNBp@qjH8j%h1zpSl5tjN>%zfP`19M*2G3PbQjvQ^slFPgd9`kQ-aY4= z&f$mHBgcjeA6&&RUj|s1Pob{hWOD%o+(cUuAg0!vBFOXO^G4xYjXKF=C4KRBDKH~i zXN%-Brnqaj%y`pK`73(xS=JypQsqc!oHr(|-6=a{*pGjiV~(Q67P?IZaKS(f;Bi$f zt32_c59xDea1XFrmCSH8FG@a4vW6h6qC?vepg3G!#CD7KAJ8^}0?WTQwxB}_T8ZJ` zu2G#(HmkFj*@<=vK&~YX1!s}{IMsy$ zLeS_Zeo!9rDr>U=bW}3M&@6ipD$Zew!b8kg6Jz{O;klbg-kqp7k)3Sp-NDz`)*d?YozQ=<*g zeTms1v<%=~xP42C?sNk-w`f=~<6?zi)={Euqdw8^Gv3kCfQQVwm8erJ5ZQ1E;=^pGY_bvp}^l#x`&m4nHojH*}_1*o!3vNPT0fbA`e<74!%_o&M!_ppu< zlf5VTg}_FGG**?TZRV|y##m1>n(3rM_q7UXG)V#12S44xHYaR~nA=cg^Cw9wdBc0& zAmFgXvC43_Sx?k>a02yQy*hep-=BbM@%ne+EEM6tk5t3i#nEcjjW1!oj z^3`J7Tya4|sTl67iD%3zAm+g{=$1clHB>@jeZJlwo7EVfj4?7WK?takhBk2khdokV z95PBl42!+g(aA1BvJpg%Do#YGhY)#=SOZ+ysCNuKdosvujMa9d{^5NG84ZG1(~lQ$ z|ArrzQSTu97d|)n5V}lq12XQE$UmYBE9wsUW@il}dU}tJ^BnOfaw2kP8ykou529nP zwE!n|-4QV4rGZW0yN6hWMEedc=i3ZsR8H6WWK{V3DvIZl={w~_qkxX!Zj?0a2kjt7 zxxoJLPyu`syubRvoeK>2)O?K(tzba7GsD07n=*Jj+&$Hm7ZN1Yn!P{tj6%mveVAhpTt^zyK#;x3?KVx_2YSt?0h)?@q8kItlL$Hm& zEGk5@a!!&0x(BkA_$K9ww0Z-kWl2;G0O|Vi!G)U35dIi(Pbk*0aVIoqRI5G7JgD}H zhe-T29hNaMHSs4+OwMb_vy z75wge7nUYO5A$N|MiWo~qxR+zK*j(^HMnbJRlP0g49o+(xX|Fn|8-CK3BWfK_l4cN zhie%i@bpWzT_`4_-Bt4LkbyHyA!ERG9P8q=-XoMSF9-3<0u&NSj_wrC;%6?;UVepU z)gTI-CF!vOzHbLO(htzpFAMPT{{)k{Rg#YV!6|~jouRRWCy@kt=*AE~ zpLhKDmr0$>T{3;EfEVjAncf&7%?3PRM>RY2)eQZTjt?9n%@u9#$8+ABu?r8r@Ph`; znlXblde=eFIS3eBP(3?V_m~{k;fYCm?vi(2_ezx2s)#LzhyUXbqE|U50~3sFvPtNw zx=KH^1rBPeG$~0G;&MI*Jl;N#v#AOcRM3On{q5ru+a+A&$aVr!vcK@J%e`sT#B{+> z_7)WnjX(VYz8*XQ4m<{Ye&5d%uFinalgvB2mTaGb%6*I+Q{#r87PAs09xB5Z zs%H`6t2>NW7CO!#iqIK&>`p4D2SaLnmt#KNKxAF$3*x6hH*Y`O!_45cZjA$2Yir}j z>>S)zen2XA%GT|;<)jmQ&C0#1jDq&d@tYDa=&}u3M=(1+tiZoNS~Q3mChiBjk3W67 zha!DxK)VN%#b$`<3n2zx;Uh&rv5=BPu-Z4fCP%x8;BY0?=yWN(O&lrFwfMvQnq$rj zx{oBp;!LjS>#jQR#tSdbrQam*VqS{!X*(ns)pkx-$HP?G%DzNrf+*|*ldkPf2-AMLwvgw_tcYL3s{S3i^}a_q0VcPlL_;WB#- zos~$&x20dvs1mF7uScm#d%E{qiZIy1R$(@Ffvrb|S+em@_ou%8{QXP%L14Vk(a=VS zKn4(`@FXN3z8utvyTIq}%KsosmyZkXhl}}jSQx{JQHAfs-D(@l6 z?w8@l?@^asD?lLUei7G1z|v~IAH_Km(r4zK@dROr&=D|>#A<|{MredJHY3(%J<%Bk z#O?&f9r`>aVT_HaQ}y$D|JvRL1~tOLzP`yn=?qvKaeN5xwjvv28#O`cXw|Av^t0}0 zS!qo(MX#6L=`v}4VCA@H_8*M<4+DxC90mthmhCPdk8$T;z+`?_S1L5sll{+lk`~9v z`w0R1Otp?Ds~`uv{#4CLbndr%Whtm5j%`8$L|bq7W6#0u`yB|dl&?c*RT~iEe6Cif znt&cq_TUeauq@_(rF}lTiWND-1}Vp=rc1GSk1iC1*Wox0e!$}}dzBdWyPy8WtJXeE z{3r5SyDxGadC~4f79Cy6KnD+tx*C%)5B>i8rmeSiuuX!m{2phaZ;@*e3jCpOM{!{kp&+pa-olIa9?z7yS1TQ@~7LXMI~unAS69v8%424e+xwE?oM@Z z(VLc;b7vjlHE$bLxFvbBQGf&jx(=?Lvg~ngv;Y7LRh4AlZ{Xb!;cYkPH|@^X7j^Gt zfbC5h)EkS!W+U`-$^d z;L2Y{Am^4Il8%Vb*%Tn4=(1!Lb3C zr#ltgZT=~2TM@#68(N_#60!0c&Tq4aK?$7?uKaK>Ujl<~Tv}FsxQXi!u*m4f-~%Y{ zMTgoozCp`jQJ&$IqCeISYuL1*TloPC2Uhu-LD=wB+_`{=%}!MITzEs1q#1LW6I*c| z^mMel%5F%HTcjVCO905x6$GK-YeW*LNobeNN1K!ZI*Eq)S-}6p?+Eur;3$r`yq$m? zx`a9TcJehPj}uRxE^0G`Ak0zIA)vrOq@q-X{qqbd5`Trz{&_zHfccx$mw+nf`2DqF zErZQ1X{>#`kpPC4Qt|ep?qy|gn|=<6n_0gL4+D@23*^tJQfA~5O^NRc;z$$CQh+@+ zu7MeBAN8EMGmRn*F@A!ZJk$wvo^MzRD`h{<3!lIMErJ&MoLkp`D~W;tNCDXka8;I# zq=n#%m38Sg4UGv-KBbC@SfqTeKBKcXiPJsITV$)(o2WwYDts$9h zHV(cmTz&xXnMGggzg)&eA8501sn2GLEvGu?OLdIJ=D$h)y z4JYc~xBokhs01$};0c#b$zvt{T5tj`AfDb`|Fz&Ixf4i05&iDj5I!hGM3po?Uw>=L z&K3WA7TMDf6 zf=T7W895V6uNgq{$Y#QGoN)ipS0cdoUT8mkysr}9qCqqz32}K$K|iiA;#Cr(=d+`% zkg^t#Qz(8T-LX2rVu-2cikdi;%FN2~;()y9SR3JHv6RFOSb=WoK^P?#(8)+5za!c9 zVeb%=;bGzAWi+HU2~E+dS{tctkU7!+zgh|uIAGbyhv(qhGO0wE4WfTC-nihLMNU4)Pg=nJKW4&2oN=C!x?;=iSnr%n-moXCjaNa!RDKc&A{omUGrN7 z5`t#IE6qQl2T=Z5sWc;KYv7^c`|b>&pO|_Jcl6_HX!MP$!!#FT)k$*m=BST8%Ql{v zQd#TOlm~GF7ci?sr-Zzp05kwR^P@o2Qe)DbBKFOZs9%Ud4U4c6Qg z3G2IN;x*NAeB5x;m%*lf+TRTRm3fv>cmp1|^`(HRq@FqtYc7SxRD*keN|}W4)xog} zrjz186$%qR*tSkbf$DYmx>zYeYk9UqP4)7dBAYxiE?!pg$*wsE*1l=UBlIA}j%$OM zmWk#w4%B)Jb>l%u?qZG#eLl#F_-x53jK*MMXR*nVUNj%n9ZhAATIuRTAhtXE=0@_{rBu)=E#6Gs=fyqVyLho<#@c* zs@%gXuS=Qluct-V9b!vaY8wZmr{KGD!@>I`rB7zxeEyW~79HN>nkMmAGl>^ARB*~! zKU|)@omOszty~mirFy{#8m-OhnHca>{)l)jTQogst3`K%W`IIme3rBUztxg&ncc6!M2zZFW2i7X)}wogPPqR`XuwL zvj+D9Mgf=IH?&`i-l@jlEDtz~X=`U+h2t6ccjgBebjQ8Rx%bD?9B`Pw92?>2l(s|0 z1Cj(g=u4&EVABb8O$>qO&<=LRQ7hm)-csHEgWQmZbr%ccLNqQZAbx#^yLjnB*bRek@oV*X%5NPOpF9s;6j_(Je69suiZ$j?Z_d#87kWY2u3 z8dX9t zWDa!k{)9u@p0PwEpPu<*Bf+h9tXFKql^~Z0+RFID>$|$Rzc_ z%U#~Y%6PF_>dRJhiY-KFtyz8xrAA+tRuQxjg3W=n1S_7ly??QXOLuNy_M8-A#Sll} zG>t@8Rhj-Gu=(D;R{vUqvxejUtHQ>qOwE5A>J^ckSkQQ*+3(J{C(+iNsk>{s40dNtRo%G{3_j;7A(|131IA02x>apwt9?RD`mN(hj1dY$JWsAIedgRNWw!iDn`A`(p(g$f!zk-L7mA|7SJV= zBLq=Sd#lnd;xpwVEEiIg!CAom*GTS6SVvw2YfodA6&_>kZ`E9?r=0a19p$$Ziq-kK zc(h_Ko6h2hT49y0KLMTdix5n~#e%Z7sX<2U_J~WD-fqaLgcf_1@81dOuS>WHKevbE zmp~eM#e-f@ldL?bVppWNHRU9dP_h5)!*^98O%yW7rAnP_%Iy|c!SR{*HFMH)*XzIi ziEVQSiZ#*7f=mw&E$kPglpo0^n>}fjn5uv?5FWMyO?XUQ7l>gfU0YXe3)wXa!jZ2V zUEgc|Vk|JbIiJKd$yHQavX1p3tB{gUiwcLS!zhDEagqX%=xnu=MlG<9oW0oCBaV_u zCG{2IS-p`t2bx;YL17HJyd32C4*Bx!l0l(xI&*_ir*d>>vM|89$*6x&o39R)FCtGf zgA+SdO+LR^4N_V4*s^=5Ro0@wYr*#FxS^@GkNSpPv6|lJ(iPjJJ5{t+`ww0JA}izi zeoSDeo;bBQXE!Gt+@&i#!Xioadr{wa#qv}|lhFlhn{%+u&2zViVaMH=_z<+=Q+4t% z+5+&<3YQLY)^8&8NZA3vlY5rc29g69iBh^Pi#*Rgvsawc5H59tf+s2tFQoI2UF&FW z`*fA~cXwD*j1!~<3z;in%*CcDmd13Q*YrUcKHMkN>k3C-@++;9>e{ccP4B!VyY*UV zyc7LHVfmR21wHK|%FF_tLz)uJ+CtTcAf{1IeT^UwQ87J09f>k@0^8?BA>%P1+Xt>O zgctU^ib%rNPMaJG|JbST^Lq=hnwya-?e|JN=EF3{Feu~)Z$-dTS4ZAD0R#0wdY@Lw z@Q+7m2`F9@??G~r!r`=#lH}`^;XXAEz-C>nRmPuh0H?_# z#F*C2nUpadh{=2 ziI3ES7*w}lR9psRmoMd6pwl9B=(0nhI%%G6uaV4GhYnf5RU>U19Faerc!Kk?Xba4$ zAq877V8C`tFKp^}pa-Y~#0o&PTa_jxA+^^2^+wIR&n-_{<=JQSUyNff2FRM#FQUa` zg4psfuA@o7t+zm&WeNP~?(Knv*B|i0&^|6diU{C1o_-v|Q$D>kW%0l)4?F;Cvtv27 z`rY>}Q}{*om=K|Gz;J?0i^W>av(`jW4jwU;M$>bGlYLUzr&jC^TVkJ>mjT4>lugXWU|PV#we_Sun*|9SQjYrxS0TuwpInbs^5A70uE3c4)%sdL* z3^HRS#2XL*Ayybz0>=(3d3((*UqzlX%7NPwAx^$v?z99bIAT&d1&%WnP~0a@z->_ z=587)?D3mr6KHwAb>yQZFE}YaYA+sbMe@<5E0(RnV(;#mr>_Vp*7NX5d#NfX2V_B~ z0#C=l5cpq*9jo_wU1wNQJC}M*p*aB4niR~DSRP6yof?M*I`bW?6|^u-kL9K6=B%M% znbps-@LrHeCkh_wrjVUh#V721QD=9s&Rs^hDU}}0_^L3_XwMcO<>K8r4`qvfyiUcf zVAxqIFV~@&pR3=$H2$Sv*vosXCE@2>h;$4#j5xOuk8$C`{*~9ICb0E zW1ycS4xhP9V{ZR4{Wb!q3NY_a8<()zfhL=x`63D_!rJLztb=WN&lm(EQ5PEpx9nf2 z2ZQeOvQhSUvF6(;30QBGoah{|5o@|lJ zH89;lNj5e^Q~fBRyOyX5mm2gAex3|HL=h*T@mGr@4@r4U8&GwUOfCy+!IQ=1oK@ZD zwz-+YN=M#Ic17#4-x2xWmRzq~f{4!r#VZti?eG0)SdqBe*0PLCQ^58kS&g^hLr8d3 zLzKkCV$W$d&Zmg6u<14aFpq)wx^kRTaY1a_X!+l}5yG;INSugmUtk66K9B7xU!%v2 z?dY9a!V`EqN~+J&jR%We2C$mJd-LZg?@WAq5e0OGb;z=C*=ELdKCMOf%}kpFlu&rD zOv<;U$q>v5WwHBw{buo1&X@OJ1I`*a*q^DZU8*DwdDQ2&oZ~h?`xU`6;GS{cl!x%RCyQ)Ml7so*eM(CH!I>gO#~!-S+me?!4PzGLV@P=7|!baqznv zb;uoRZX zXf7q0!Tv6LMg}>(wrP_5DkX%}wcc&`po`fHU8v+`6i0Py`WL?pwW~GX)p%5qt~(amv6X^kz*TAR$Sp0+Ae&10Z1tMQ!0E z!%+DUk4Q7HtYjHHMr#5){4oOnwCQ>cKiNTWW+kE z4#Nb3isKZ>yqWavPngp3;o4Uy?fKm!Kz%r639WjfidqYWlI8-oVZ(L&T#A}fKL1U^aW?LKpl6iD2yp*@h%yo6n zu3jP$-ukZpppA*B)LhoVetytW*n(UB5|mKD$^8V7=l;4lpo*^Xawo2 zb6GeEg!QPPT-*CL9R!33{|$R~CB!nQ0m)1cmT4v8T;yBptt7nV%sL#5ABnI<8oSa5X)7ov!CeC>yllI}6_dvQO`_kNMHJ)QWGmfz?3 z=+XzG#l0eZ3vS;LE5mMks09JSSAh0*gMmCYdv>%ci^ox0tVAHmUPIlNer_QXfsQNhZGg%@Sq*dN_04{!vfqmR*h$vGEP-9 z;fYvF=qT1o0BJDYh9a@Ym%o;lPbZvEu9nFq11Ff~9H0^4YQT2&{h>*2WpAj*TwF=4 z_6`vW2QwU17g5fHVF7^QkZ7vf!zRRg0l$J2!L>u@UQ|*~Q;`XfR3JML4%A$ZZl_VD z3z+I(jVhb2t;HdFU`Ka=>gde@TZaB9#5aG&8rVV!EQ9!_JP78{pN); zX;^3F;(kO1#S^FIe^E=@^__6_I#LL*O6h>(#9JLTE2vBf~D_d*Isp6#hX4NHn4nO?Znobyw%n8d$tK|ezX@oG!Mp=4^%@y zkLEEP{dd;;eTf>v3yf*>w^7}`A@ujQPfdw-_OXz^03fw0tdeYoHL79 z-}lu#;fH$|dTK<*B&Cmmg29>^2$BKgFq#tm5aZ7`E{?jobDyPhcHD~cKCee>SYhmQ zR!S*}kAr#?fx7pw`bGf=E?^PWM(Oo!(ee?W;Jy_gXw2tW zWuHLYeZV1kaXDzcwd=V@d%gc{JtfuzgQc-&4&$|QO!tB>OnE-T*o)Je&lN zGGDhPHPnY-F0D$W&xu zbn=bJIx{^v&I;v4u`~r7Kuy(muDB}{>ca|fMRhnT;C5t^lgHGf<}>B6W?fwSy{Rl# z9&PL=0jL(+VhTX$Yd|=<7R#`WjtqNQ+*UeNCcV#?r(>Z51eWt96p0Fg{HiOtE+ikbm4#6Zc9|OV;u~&dJfFOw zd*<|e{jdu$VO2X*vHT5!!&j_9&TGoq8|nCt@+Z+eZ}60dt`nKKw5lung~HPBG9pMc z(b1sPj3-_I*Mv+Dn9Nwe932!;A$v*HeHR;;2oZOkzVIc5Thpt1{tQKZG=C z1qJ=J1YzJ-?!#fL?kvf)4ngDX@8YK$j|+oHe@EtcG$su7SJh!6dOWP_;mavFLFCJS z@}C)4Eqb_`pm;+b7#kpI?6>8G*BAKZllPU9^hsW%2j;IKpK>A51iJaoE}HP9Y$t!2 zw)HEFL5r%G*tJ+RFSNXAeDZ-aYQ@yWebVYz6?L6>ZP4wf!l0njmgb7RmCchYf^ zSylrqvF?&e7hc$7#o=@9;AH~6t(m8+45Mn)ST=TP+d_OLI~|UK?1sgsV#)a&0#>NP zgnlLwA&m4R&C;s4uh!-T`ZHAE<1X>E(G{Zbdt02FPg5;mz@O1eh4ELSdO#oTJt3k~ z01MPKhXxk-YZS5DgJUSNHpyStz3)z^16WjVWVW3%D<5Yd3p8`k$H9XtTsPc33~G8_ zD&lLk{M9G2KqgSNr6m1=lP`3qmSK1XZTUNayJtk^BBd_h1Lt9RjfVb_VEwYE48+XO zdLd}N2`-pEIf7{MzffGc*fTEcv-3DMFJ31w+69J+1^)xG8o&}za}1R_B`_009t zU1$JZtH7f7(a1|>MA=#_dUC0hxC6n#^{@u%p;C7DAhlBy9CqVz?-kL`XI)V>^xe*d#bPNby!#3`tC}(#J|1TDPIw zGk1x+JG?xCFr{sjb4i;l-RerSHdqR?^_l9bgoy8)sX;bw>B$$Lj!a7pQ~P}CTT*$l zs*9tDL;*NQ-P6}**6pV*aH@oUHNl6UvWPk$x?hh6Ls$2}IGur$tp*-OLV*kA$IFf34TDhQ> zl&%Q?5M6}3F73Syb?4t3=^qoRc#aH<5>bR{`ZhG-zh|b3$1q@0IDF=sO67y|dVB}e z>(eI;a>uKXjXy~Blf0TZWq*+O?=Qfcr{(8XG8_1Qc=%8LSvU*SF_xW~Ta{>s&Znrw zKh)o+XmFg|v1>?Sg3^y)-f)@4nT7KDR1v4AB4Merc^~}z(UrfLa+i#A)gUydKaoit%JkCL&v8VP!n4cIpKf5O8SB@wZQ!Foak%ihIfKwW8f4T9c0L!jh$Y{_p z5A{2=5!8}tD9QxtR|wmvWauvoXbH#%o48IQinQ()14QXgaby?5Jusf$S&UOROTe_n zE%3qzc6YVB0#@0q(W9c~t*h`{yhGyT^De&c(WN$A$up8tle_REWaw<4Q zH0Ai=^5AGsnt`PZEm>4~XgswqY|BdgmPDv1b4oN1jjVR?k5==#F^VoE90tgfVZle` z>!|DqNwZAZER>msaV~Zsi^InBY@v6{K}&6q&Qe9k3EG=hfYmp-=kO zQZ8(k>g9t~_I(+(je+Ae9O>rbfY%CGH-nxoN0Qzv#)i_eB+HvF!O{)Ts@T`GVeOXx z44k0QgAIa6HvqwsDM{18g*tNgfjvJx=eM+ws}>qJ0Y)$xF1iA2fh+Mh3F#LoDf<63 zBz!tRzG@(je_?_H1jEYO7d#a;36RZs!AP^17XWS_LQtKqQhW>JtyRjyJ^QAF&_Dmx zxb9va-2(T<0agJAnWQOLK6+o=J?ALq8a^c4w}r_!hpB8?71bE;V=mYU>pxQXQs7$4 zjIt{pA=pQK?P`_i*r{Uq`U8v;)E>BIGERMNu|Z!YRraG1l61-Hr5zqRVBkO`zH#5` zjy@HtdR?NiM48Q~8+}7TDqO=JKiW*RDs30B8dWUO>Fq?U`HADHMo`X@?9?~`*}!5< zL}VgJq%_x|Q7sv30M2M|!`8rA#6G2Bn}F!3Fyr`h(TNDE%;{2D7BzX=2-B<6d^iRA z4{e9Sg442$C?&%nsin-w(?3jx6e(o3Y7-@&f#ejMb>B~VMtKb%_>k2x18lhIZEUE& zbJ~c%FPF{lt~V`k2`MoVM6NB>scSPP2n#7rITKMpeq)y0>`SyYf;guuJ8H~dOp7qP zfrU2}*!1llU3#SxeL&h1);xKhV(tt~qzw#bwQMyAxmR{Xy%)I5#L_=i*2uD;*C8IA z46f6x;HLBGpg$a|TYSd?Kx?;#@qxSYr~Weak(7PugUN3Q&(hk%JRAK?f8(Gcm{w#e zF`iRJW{L|?P31Y8AUTdHri{yxz>E(dSW0Z-IrFDIj?ANP6D1-z*Zl0VfbQFvRbZum z&~9IoL@^g5p!Ok@UG_MNCY;sI6$0{bZwI61me%~BTy7ekB8u0z%6|9QgDFNaH66qK zy^DhcO6xD@E60u25)od@?7FwnnIaEWq7TBxmAwO-R3QsNEJdP|jQaRze&+(OlKL;I zACt%+%Q<|}K@?Ahe3B=Y=2^x|_acFF%YczSE!injiUA;jZnu<;$ErlmQb+bS>G1U9 z+rdbBeIFVr2;c_HpU+b7j}#h9G|PJu zI!tS>12?4}kk$0Fam^^2?*irZ`gRwW6saW{HL?9ckQCI*nq)NX+B7pkoA|yupx6e2 z!RsL_6OsrCOYIoZje-~hw*C!_rcMR-^w*rjhY6|TOp2zJcdz(TE&ESN)m|^QFig!{ zIGnuI?)rSad50GL|1 ziv;WJ#8P34>MHCPGmrvWgj^WqM$6#?+L0hk4o=iD1%lw?-_ebk&Y(|i2xsQFl+4wi z<%Fy>t_SMDK`uV7PdNCrwm!!VTlVi&O9kd}f|xV9<1U$WpV(`e zQcN@5u(!s03jr5^{YxK}0f^k+kG`QR6bW<<@!8xFiXz>syclO`hcg*sYh@18{o~d} zzY>!bD9P#hsrJrfg~NT8M|Q)RMj7_!5}b)mO;oq*cojo-{7bqgvLW(mCH8b24UtJE zLOKCZ#0vTXn8@5ek6`YXw{y571ZnxdIc|rSGqaWE8n~_P9`IjJ>Ig+*YJ2%#R#rEVe?k zLI>rvZfNv{pJW*)OQJ9L_)b2!v@+FMgYiA(mD8XzETYNJLR7mzb}goF4Q5P2IVhve ziTTOOX`(;^3{PeW_+8i(1FgPjF3VjIp6KQb?#N|(o@XJSJ7nl(MLKzC({G&O06##$zv^KaJ>liDB^Yh%Qw^oZ z9(8Vyjw%E$->Z4BtWU>t1?Pga=i^d+YR2{3iLsVYkeRs{u$w41ZY-D-b>-erwJn}! zt~deX?`S!6ed3HV^zO2?;8EH_FRu&+&hLG}x$TTJm=M|P`94#%d?fZ`>*1nd>zuvy ztm=mmkM=7CLR_`Ry3h1cSiP2(uZ)u{6sZi;SO?tW4~Mq`oZloSGLLwa#&DL-4!Glz zFt5PSo`C&sTbew!LYgX2^CxDPNo`6+?VR5P)N__BkI)B_xONd9f#HZON>Qrp$DcQa z8M-q%##~ZXMn(AhI2X|KKz>YcI{P+Z8kO-;Efxy$DL^K`F+fpuDGACP{<{2T2X9kO$L5iy}x}~bGh;ASY|27gbqzv*+ z%dCJ{>u|maC%kbV#@*+j4kW~zYJ&>epn$-}c~q|Lc?S@44ItIksG7+KdyBk-&z)Mr z`pLR?fS^rzqT$u2*D2>o)`VPl!iB?yHrkmYqF!CeFWsLKk`7es7% zT_ziN-=ABBn0BY-YVcYq@F#^be25T-C1<6oOd(iEvHo(au~~ z2T)jJhA@6cy+bYjP+!(R6N*=2vebGkuOfbeT6J{h@Q={iB!9&OCTu+JJ&+jxc&Mtz8UXO<_67Lg|E0ILGc@-4 zq*|ezx+cj6v9&hl298tenBD|NA?N(2vT0K~VX}@NoY4T`aIDmD1Z~EBoE(nCuUDMQ zwsCE>@bi{(d_ZN36s!HfzYHR%$B<`AP|Z5?a0^qw7?Xq)X@h{f2oCT2sen(=hBgy6QSYU_!>Z_Tj4WU30o@T| z#!`I?@$yPN>K`Yn`=yC==|j;P?(YaqRtedR(oycPri=#BTW zbyGS*iM*X_f$|Pi;%#4_ogmpY9R~jl%#ikSydqvVC^xM&L$ZdBO+p{5Vhbb%9`u3X z^BNWT6eVmTZQyIl8Vs;dT0q-{9kJqr<;fZ8F+aWdH+wn9!$Z%zqV3<@32TRB@1A8f z7uA^(zeWc7UjnE_s*>708tGjVg-~$z>?;efSq2AZtc!XJmp-^(BF?KOT^!bbsSK)QC^o?mf!6{6A9QG8-z9qh+y$n}eUHAo zQlG-){p{u>1L~e$ECc8bJSIX75U)_ZyhEr-)26H^w4da#xnD>R1T#w^*N8tNhrk0o zd6sC-^>Ux69h{>twp{=-`OQr1EI>${8If-&M!!c%j}F+*k?(?txj`dfC(wVuOn=Wu zSMR3Y`YMSaI=a4pUqulkZ+X8RP)$MX;v8crV{Hh44d!TSw}2CXcZuCO%P$%c2UMi+ z3;=|M1kDQFyu4i-PXSy~V6_2KSWDISl}H%6&Zd%7V^U{I;vSbF+Is826u^oatnKh@7*K7tBk_g=*l zauj``4^W>q3bw*q+J|HcLFRUnZ*)qd!5*_cX%Z1xJD@KAc8Wbyse~_DYU1`$3tw{} zl>#o@1IM4Ixpy3tr5uO>Yz*T75lZeKo=ohFD^Aip?ETuJe<(v^_iZ)UAb=_&GzRy_ z)+?xz4_TRN@0d|tITwHqkr-^-4e`0Fs~B510dsbx3i>@=5o%9Vfwa_5?CGtAQ%724 zlFMAFG$jGDe6a!dXub^x2vmiOAtwwpz2tJ~l8ow9(=#G~8x*x9kpL4$Lx%g{5tjQ zng;pG8Tam0i>St8>srg(o?&cSA1ZZ4B;HRaGRm&C4VJzy3SEQ+D6Qak(kx)bfVf#H zDQ-rigssh{;sGqi!c5qVMv&BvIT=F;xAI*f!>3Yjo@tp=zrJ{=2_)vUsr@!yl76Vx zZ`P12F4zM_PkF{-6+ANoB)$qihn02^SxU?Qxn?=1+22SoX6Zf(T&xQ$y0VFF*@mBAph!~#&FIqtKg$sEdk84YkhRfmS2+86r-j_Qk*8+T<1d$RIEZt(9e}5;n;Cr&uI7 zc@l!7Oc-e(j317M5bR)ZL}jE(9Pn40dQ)j$<&fF)f^9&k+j^i&(9Nxxtv0g7-n3qmmARPM_mf-2KTt>~_iN#nQ-(3u0}anK_OOKvX&e-x6xzxE9tPs~NlRc7|F9{hoy zsR}{QJMFyOSC0~ZLQvQ&>WhWc?`=b~rtwk@u3>#YQdNF$ySq@X91oi{!U!Ej(3v!m zN^wIEpsPMv`1gh$>+NOdV*C+ubQaL6L{Z$v=L^cLkm3DnV91zLy9;W|NuvBTu0Kvn zmGAdQ^W}b<*$2e4CP5YbZYfS&QKFCO?JlXSU;StNwKF86(Q66!t4xP4;Fk=^)k08j zP?^Kq_xt7StXGif2i7!rc;`Av8Q`$~U9s>rIm1hVj3;%g&isG_5 zz$xFZPN5H`NDs$W#LEItPJP7Qt6AxQt9ZGl0Rh=8B;UV1;i|2{I1uI22^^N(5QXfp zJ3n(po@yf;Yo3}$6Q>%1e@d5O#pbuDQVn^-wJPXquY7?uf??oZ<`B4avwGNTV&6SL zA2xG;dK$LQsuKGUw`w;YcnGL6M(@F(@~~}E^^qeKbvM>2$pP(KZ8gdh3RwK;_eOH% z$&(IisAl$hxFzUhv96*ln&Ytzdmw!u3V5C(iixD4bd{BEU;XV17FSBHO@B;c<^T>; z=Xvs27}W*%ytfj$Mg|=mW+H#oHhaS+Y25$=E*MH`0M^zyAo=QpA8Si8m)z^)ZG5L= zHf#f-uyfF>EY0nPQw=7Ci`iy;jEVi}O^oPBdz`2DJOw3S4 zfDcX@k(p0`{#eG*(Ic!6cu$fdU==4(tIJU2lW$pn$2Ol0$lFr6H#nhlZ& z`r`*sNhXns5@$C)Qo;7I3MwK=O2c3;FO{EViTv4U&FcpK_Wn{2%UKe7apbPi-zbn6 zU2FCYJ+!>BnwSwX2Co@4`wY=>sJJtdXHo0dUWCLGkE2hTs?I=KDu+F)LTxbypvzd>#zt8GbeINmI# z_%Fh>cvs{;Lf)q;bK4!W@Syr%I>q#aK=HbVB@mylb#YGMR8(HXtNya;YGOyd-I)B0 ztRf;U^@ZyWIaIKY!V?+vw6vKHMk7n`a5Fr`u?)a4ZP;KLR^gXcs6&2_UW z6o>*gV_O5=rKa#V!7O5 z%s2Rb1BeE_qa#D1hv2wdMab#vn)-xFB1uk41#^ByJm|$Z4o(NydsR;WteTkKfM*h% zkws@$$%-?(OV`#)eq;uME^SMQ#EQ^zR-+G4v_d4-qUb~V~R z94wpLC0kc=n!(1IG7;=Ap0RWr!_p^|K)Gr16Av;jFE{C%z?Li4!oa?jb;p5D^p6o; zH)!|KyT2%~f~^+H&=sv>_rvgqbXgZ4s{UfLf#_Dt#af!wk@}|dlzZ(-Vr$`5qM9*M z4DJ~WEk4LnGEDT%>MWXEe4~e*1Ne7S^gTc^oA9-dFaAwugzop{E}jNzp&T3*#MP=y zm-i^`xr^gEU3*)M#XQYP^*i{7rmj%Dfnb%(p9 zU3T0z#^Zl9%cI$j9f6`9@D$yos=NOZDK_&-CpBR0eTMy!R=#IX8P=g!_Pw>`g9`eY zrcXNbYjw)#Ywz%4a$BjeJY%N5jO-q(z?pK~^3>wSaJx?%))E78^W$nessHen2TTp& z|Ipr<^GL4?V2O+&VmYeu@~-Ri0(ga&L3LOO1ZTi>D9e}@OY?*@aff^4>M31;s-SNa z17=}po39tv0+fnwftT-xpREU+lYsSy+p_yPIA#8>)w8AV9#n2cw42SZ_pxE>HvE}y z5{o=j6ND3`Lf^Acq@`+89VPPT^xmJ}rC^aK)p0`^D1-PABBbU{AvcD1CL!p4M9sJ9 zCRDA)MwJQBF8fwxMMoW$G?Wh8z0N$QRVNe(rO}b^9EqN}m@@p1*@|-sk&aH`4+6IA zkjMRgU@~owzUH*<^)2-%=Q{>mrCjpb5~>ncy29MLWr>(~`bwkP&4=LeawP7E6NCfl zQiaQ7%-9_l$u@%&*96k*2C9O*-=%|RHDECDXJ3uIk`R2NjM23;YDIyufZA7UWV zqhGR%$J*@VMFvX4JSngcR_oqZE(-FLSMomvZoeGpL<7eKltp}fc&x5O^ zE`T(KBo}ORCAceNqr@)@Nek&?g&0<#0RKf$iA{+jsqzsJa}7~Gujep7i79I3xs0Q3mh}atn~Ht`Ac^J zU7G>qHIk87A^a!pPiI-jV0JLETbJCzkpH!zxXtO-k6RUW@L}tZ||;7 zlE%>4*)6va<@Arr+L8J<#g3T%106y(7+!NbiS7{HzzU99+x73rNk46LR#29BDbMM2 zu2MMbzr^R@!sB(`IO{5h_|yCUE}c`hwViM3#owTewNg}=cm4eRa72K-f>Uj>nZu!H zptaW16l3#Wu5IJq}?#c&FpKYo1m=q=*2i=g>J^> zqc~>ruwjh8VC3uhCbnFR7Nts??ra0<;6uVadpl@Pr4|QBwVYAa3s0>5PP5N-WA?`0=O&dE{Bk0`|pjsCY2X4_#pA+WOKShc8ebXd@29z`UFs9d}` z+srj&z#%lbMBCfU15+e)_MT|2780S#)?KC~w|S964C{7tCk|9;e~*-TOq4g64C$Ho zTYa4`11I996HpJO+agB{=rgTN;sk{Vg;z=MM}q%0sYor8yu|2geUNFoyU&I#(BV&? zcN9i1Rd|SzDh~ITuxJdp~{^_U#-# z7~B)j?EeleN?2*Xo9@4jwO0tq)xWGqCNI3jd7E zECxa{dvxmRzZCzDGUsTZ61fb-MAue^iXuL)(Q=1Hi7b8$0(FVwi-mc>BMhJHRs75H zWFoLvriOPnj8~``dF9g|IqE`D0hA+zSeS&WsCer!Rr;}6{Ajjc{aM(L^xsUh(F34% zO6=7-(qWsH0+3IJL5dv0nIxH}?a_r_9T$(iO5L})* zx>wIt`3vj4`GP=`c?t&8uNz}KMXT74$o;aOm6EY<0|mgi#CZuSLzGXHrY)tJ^tc;lyQm%~w^DXWJW3t5n(Y*c{H`*h;sgI0gQa=ij@B zbUj{jd*nq@$*uaOtR+qYKZAifwDKhFM&u|~Zy%_(gL>RbwiDQ+wF0D!|k@y^8*80j36(7%SQ)%N0y-D0z+Q+mSPLQ zaX(e=kN5m!|4HAc7HkZJw1B}Z1#*H4;V+ptW*Q+KG~;u+Wb)AAKDya$p#Md}{&t;< z$q%8h+G1VhccgsgfVm#mghTnx_9Yp_D@%>G1FmsCF7KbRIXs2z(8AwfXy>9TIV8>^ zeLN5y2`@ z!!T~8ZH9TD?LjU=z+MHcBXU7GFLo8BQ}iA$=5Ki%E6gmG{0k`yxa(Fmld%jJXqsc zW%YBw%(x;8{W<-d8bI<5!C2mYRB6GUgpagGD@aSH$cH!~ighuutj93y42#oAkZac0SY?jR^;tB`F zPnRFBci{4in&ZR5uqL+$G*-b-TPQups?ntM^njKX>_GcYTwHtE$gPU2F;|u+WtaxW zd#lc#eNYPNEnaT@--EWPNkK2rI*945LVu@`n%i0Gpw$ur6X>zD2@n0Gci3hOew0!^ z32xC9mcn<2R)sw6c9h8~WUZQV6;K0cdhD>oIURtii%x7{SJIPyG>;U zJXFwdZiG#Ws;n3aMNr=d@WZIx+#L*kCY?j{NFgOQD>1y{MW#r%gdKstS1m0>IDJi3 z_4MTF-%m0e23!7AKYFYKjpyE)`n`@lBH1sZAnxRPYmOVn?RG`MesL5MO1}D!h$k6Q z-mjn9hC(8DJET5o{DY5iL6MySITfBR->`pg#DEI^r5F^34pDKpr+LniBjTpy=$=A? zd4i=Mtq%=s&dc4ri1nu%)&Q|NiLQB8{BhIIp>SuSqGBu+%R@z?jp`7V6+(N=KULLd z{-?RM=bIPhXb${h@480did=1#Obtjdvdz?-^m zrbiAYgwCXAw}WzBg1wA6%MstuH{z@AV3$9csGLD7d2oRr?ndD3M7Yu$Yv>FmM#PE( zpj^PKH|z`7ovGc-9AtbaB2QD23^w^ht-enhrwN#S5(k?rk`t$Ag6ab!kH%2&tB5Y;<)C=3piq2>)!%5<@I&l@6piF-TC zUR8CZL-&`)s;l9sdfo?wj#84P@BG!S3TkpZvQ4oO?CZxaeOf7r#`nEq%x8~}IhqFE z7t-|B0AiVIdbZp~N(i$uLid#h!v@xLGX`Tnf|2__;urwmH~6!8J_L8@l@q3l$+8|? z?XV|{2kWP}tOyFte@0#6;fam4kRB_{RYyqsC(r=@kBj;E^s568qQ#cd>4_f`KK+=C z#xmHt{>TbhC}}FY>^#}OZppNbS^pXSL)S- z`P0TP^$Z?0^9zd`ws&(h(DINO_7aP@P7f=z0KAOHlteiU;#F;X4pE+5L=~$44+MBs z7F5bkxIUnK0p)xRi44BVw1vDuV;5l08K#X|YWqkH(WVETNQO-#_XlcIXP}^+-pE3@ zoq;1u(m>&Wk_D12+$Dt?)?WZc#R;?d(RV!#{hC7MUvNQJh&I8SI~6a+{^#gwAW`?B z8n&T+#Kt%~Zq9TGvNHZXLh(CqGlZPG{aVTe(ZgI{N}HURwUxM3z=g1;vq-M8?51Ocrm2Fmk_X;Xxt z;ND7xw&cFkByZQpJLSt|i8+y=1|%`puMEMs(@SOZ(WCU7KxROnR9 z=G2}^SM2j- zSVqji)_5xi7&yq~9X8M!02e(Zy(@iHh@}eYgdX z>XC_~(31D|vm81%$~A`?CsUVk7^TKaIMfNf`QH|D!T)?WRjp>aMci%u%D5XqGTyf{ zabP)1Cbaw-3_LL`#Ha^YlAQBBw@I0!*AyR*tP%Kn_p*71f3P5{e%Lyz;aGI>*6|oQ zj1}vn@uU1Hy20@DR=n05QC5df#*h&h_KpnmA?3Q?=ZWtK1)FHY`_$gqjO*&rn2 zpv8Plww?lDIcKhANKNx;9<%ekj)s9+aCYf@3rasVN=xl2)gGrHuMOST{_M0 z4d|7T>X>nlYvDi;#tHgjHKK*>=IoWOz$^LFp5N*F2rB9!z`H8O-W{Tz{9%qrM2Gj6 zxfn*f1dzcOhrw{L$Z5ijbYgYAkR^s&R34e@o|R|N+Wmx(;NGt!(o}i7pxJBwABA&m zRtbi?4I~@HEF;G$Wtx^gKXyIPpWZs{yC6G}wRGF|>z)G8fEx*(^1kjB6wHVznJiZB zbPm|0`3iHzolq(()VwyYI)f}8N{TFr+qi=WzPZT8UiH~hpc(>569nK#N-OnjhdlJ< zC2EK6_P-u;ZOUcjqTw%P1R_MzALLaEQ)HT=pSX|3M0^?{EpajUcBgPb_-MO>Yo{%6 zyfZR4M5RD4y;pHux$4n5E^_$CF9zfnyk9dZ*AcmqUAl;EfBXX~<*;>DihK!+eZbE#*OS~?Gq?ik~BQQ7-UE% zSUH$RhiE++uL}kU#~Zzr&S#kMf5jw#6>&!H z8eI8?N5)bV#sga(2{@igI?sk`wJyK#225;4VB+INFN|*MrCJzZ4#`q*SmQKN4-ag? zB;pW=LCntE$(seoz!oyKe0=|@N0Mtn81q)8|K$^~o#y(?MVMl;Y^QhQ$f5o5h0V13 z4qZ0u+!_VHSS1bD?sR0!L$;E&H5ppCZbCOHC?6ZPC zxSk+}aeV2a349!zm)a^EfnjuvhHJqrf!u}==m#)Blk}ni0|NUH^Ql~zBs_}xG|6}~~>rRQ=8N~W*-DR!+?k*$L zjE_W{dsB%85LOz$n<hM6zO45p^%8Cxo$#DZMlQ$mm3HNa)qb=HcwE8? z#qTVdZ{ew?tb?kn+`&GLL*!n1{(&-4G;9ieB`B15L+^Y%vnM>mOt&1yt3npJlvXtn zCGYrYXV*hI{?+Nqms}`l@)L)&OVmPo#pEbXNeSBh+y_m20exb5q6at47zG{*Bk3+J zo>6CEB2Ojn_(&}US{<**O?Zfc!W;0`@`30OR3uP2SLhPzJJPHg3`fYiBnYi#X%2n{*U2??K6#P(D zqG1dm@h(Dxjz>{G)>NIgFu0~7N#lCP0(XZfyRTR`L)G$j6xs5g*M`XZ=zZUwU%Ne7e|$33b6(Mr~=iHn=i4A@jnW-qN$-Tuw!Z3N8u{r_uB z7W;Hl>k-*BWAcd7yp^x_JcW z8(X_`GT+1cQ_#oUgX>Wz@Enny`p!O&@+j8?qOtc>fq?^ocqFh1pWqv1EYRU~kRf-$ z&Sj4iDj9GP0)K_CW!Y;pb2vnUSs_1ZLywPXY0$6G1Tlm`KoF@ofIE&MY;5vp9@1e zx;q(wPN(l5#%q&N-YK%X_=oMx`X8Gvaf**7lWm=GjfPT6JmV|33=~%uouqPQR8STY zCxApevBpEQvqoAtc$;p^a?Y=a;C)cxx=Y^5E@jX(Ubhgad%6~1WAcSL!`T|4HEaQ9 ztyV3nbDkP>{Hq?kcL9mz8}$xHh5*ablsd3@1{BMWJugdbE93-wC6ykzA8D)?pFkbu zAk%78RYHecOo)Fu;~Q;JD9r!z6>+n`keEm_=Kg}KO>NSp9GMuwTg zG~$Z!X&6s;~cf=*kE^hTPY(#pB zX8=E-oT6Gj17I8sI=zBm?xF4sVNQm>2J>>iE zdi}o`AHeg4@K42%3-`P1T4If&l2%EnjP3OPko_xD9oKkXaakCWgMLa?$@<6M{_o~O zvJIfQKTYm!g6^?Nw3ziTq<}Cwe8>L4n#MjIBJ-hd=v}vWxb}y~q6h#I}Ey#H4c<)Fc z!H6sjK7s|m`zxmxKn1@%!sn4)j0$kFn8`+1j|QhE5qn7KU|WSN@SNS9$ReZPv;Iyx z+_Y;D^=KXa?>WG3R7OF&o)zX_?tT%#s`N|<#Am8E0<%$f-%sl$iADSwE6GaNv<{@! zhRO5-vlf*ssoJ$@3PygF+JI>zGW{0;)|du=az!&0aTeK7Ak1p9R`W*R0NGU=Srm!i z)?I}Zk7gLyK98H~B~XRWbTsV|_1YBwABhyDQ{s`ru6o7@ zRZYp&Oet+?!l_Y}E2WqgKHIf;MY#tyO$aeIv_&|voRvIl#je(l*BNn~+Y{*gvSvES z15hrP`ApOebv36h@n1+F{DIiUk#$=*y*x=dNr-!dagzA9p^#maa3%p+eU-wzUT971 zz%Y{%qYf`Tp><16j5_s5>cy$;4bJi+(>X2awc+@J&}Y03SU>#81BH_>?YTN&ExQcQ zU9J@vJx0-gkkh#;{^O8PN^UBjs&jU^lYH=nZo*YXUb@XQlV0v*+^7IN7|@-gW`10* zjPmtO4(kn7!%>2z;%&5E$>Dd?M? zkS3HVGWU&97jk3qr=19P@$gy^on08CyUa@lK6CY-=Dv7j@1VU?`&;J$hL%U#vzpbG)FhE;v};&cy#=?babzIc;Ou*Cf9iG2!93mqU?t)x-C z-7bx+#I*0nphnZWj+;xn{$R)l&{{E){q=Aj&&!{(AU=m|2}a_?`#mw~QudW4bqmtN zAz5#F=~5C*aRpk7_pfJynpd+?_4tcJ6)Fr@Q9(aF{*bVogV%*W+c8kZO}I4zY#n*4Fyi zP8Afxg5DqIxJAV_aFdh64wnFzekVqq&jsf%s3kSzg?9Cp5EzsCw-5FMn93XczD}u+ z=r0En?lFv7p*W8S{%1_Zvux=wbC)CFzLCbMG(+rUvfNl{^6#Z?7%U%rxdooLc1sm)vR|#IoE@l$!bS3yR|4|?g+&@!wlW0zy`F>8^rm)laScqJ z3D`dxtMi#J*K|$=`7n*cKzos$Wh%#NP6tZZ&eLpi2w1h%?drQQb5@3*fjyL9jK&ZZ-#0>ROiPk(a5LO zZ2LT+XkP00Lvy>Pud-u8BGj3cvM&wjL^0%a`+*?(QLEPbR{x1~%$qPG;W8!z&^L^J zLiF+wgVNc|H}7b2hW`+P4zP@^%0e)WMbeu+oo#FuDWoR8Js8xT%bHSo4WmWU=QU)P zn}F@;)q(b`(K_2p@;f&jN~#9-TRqGA$PI=z=L3Kb*JUB6$BMJnPhm5&p z+c7<80%eQ{JCk5FpK>dDX=0M~LfGsCW!*vCik!(Ga(*DCzHW9A*23=fj?}H!c&YTJ z)h)*k9klEFr7vg0~Zd3|Fu*z?fG~(*cQi_IEO8d2Fa4 zog4x1@l-@WJmZOU!F$Q%jsG0T%Bcf0ZW0H?mnQ()mceYF- zyUY!PLuQ7OYv>y0!c#uG z=yqFG=Zcf!ZEf>hE3U@dvWoj)WQoTOLc?!*p zIMQSJJs&?xm;J_p<;h}H2wol9>I30ZNEa%+TAk(*)OHQdr;C-=Iawg{XgM|IQq^CS z*^ww(CM)sqJgslsSEGrB`b>=4$~wB>1lr-7G;mc+ocfVGE*FBA6YMKrO%Q{swz#1%o7?qMZdX{DA)5h1|5Y1hU!vxm~=-6_=((Dp2ezH7-&;^fvp_C!L0S} zU?0g`$LJN|=MobbZ%9%Y$>g`oij=~;+UHYAwLE)*V=w^?QT@|sj1LR zcPOYqzDnpgpl_weZGDY?;8xMWMDf|E=eaJVE=!*~j$WBz^{XhIH3Zx*$52fRqxnf{ z<|ZIia(fvp4MPXyv`2^cI_wboO&3PGUy)3E=H)Ane2{;i;VAd-8e5s>kYL`1F=VRY z#?#x(q3$}%7r{gX3Jl~MkCDf6Q|Gr8242*)%dRzpO(zQWhX({bd_>|P@UQXME72Tp zv37G&N`hnt>n(?0BBp{^+_e_An2%ZiLxZAg4BOG`X8xlm39<>v;$KWzi3C3%eH3;EZY;yE{fStGj4b7)Im)ZF1{=K~(NKYdH%hJvWA55N(vaegL|!^h_?M5r*m z*Je_U;I{MRN*&0)Cm_0=#Iwwy9@4?vugAxk`6UllgYE)Gff(cpa!3bY0bcs$#432a zRjO*2Mef2NsKNI~*LyGxFvP!O3K`c+G3cfj!Okid!S8ptoWt}+%DMOBp7>KJ6cwlI zG=bG=#T(*s{W!&K28YU`8G9-n)93p>wTT+H43RGUFp%i4TKYcGXjxH zmhHoxPHE^6R-}fJj9M=9GmRo6Yfefp6YjeUmrg4K!mUN^cy@U~l7P+B2amjSk0qKl ze9GT*n2{QB51O4M)-Dr~l;$;l5Q&yy)zjj`dawS>Vy|LP``^loeI?1%))wsy$Nt`0!FB8&1^y>zhG=n5N#CJG!huqEp-l!I% zcqGp&7Sq31T}RoyyzjO$M%Fo({PT05VTYfYb6PhJbeewbvoBy9@mnr98&eX3DNgH) zp06exv(qq8KjyIfCwu0QqC{o8n=(7*(W6-rMOUopU zg%b3<$U;1C0~7N;APSe~el^^zgYLTh*lgQOY!+(^V~J`Z13!PB(^B(utmUbfAs|5EhB97T4{SFx*v~p0G^)em|%!70=@wRo0|dOj>MI((bmg-tq0ABf~9A42p%Ut<93> zu9Op+W`w@zzxQ!Cpy79xm7$_Bb)KS?lIQ$Mg6(#?r)FFB;d@wU?W zR;IjQpc4;#fC(&!h=U|^Kys2w1POJewK)<5#HC0^?RtHa%>D^LP~DD=!Xt*2dcJZ$ zG*v27qA-szCH`HfiC$@5dv68xJ?HcTCob|SBSIlpZ+0wg2d!SPgNl;buPD7XopU7- zreyQyz`747%R#7#*#B5i<+2#jl#XcBe*D&4F@D+Er>dyNR;mOpn)s|Ra|QYLT0jmn*IaP%q1ioP zna6orCsb|^N-;V_PQ*+kSF1yBHmUMOiOa2Jkpo( z*2K!)9~}dEKEKS8@QmLAe@;U>-e%z$%=948c9F}FIV)5<|Kb}IskdC`eqAWk=IYm0 zBN>-iwCigmG#xa^o)I=*sZ_jYka&XP+!%{c zIcwtL0rFma<#C9FAsecX@`=iMO9{!Tx!@|4FE@6_dsyFL3d#Aj030W-KA4yDO8lH# z^xWNl@>z~#~9Gn`YZDwKS+e0p*HukcySIlk^sf z@NSUp!OG1w7)cIZfb1_8&Dn;hww+q6hEu z(26eWC;WDC0hL16X<+>JSp)~ZXBDh~iz}B_=RszyMz4)7M6>snCauIyEks!hl2@}^ zOb^8X#jy_V8N9c}8eWG_{)JO{%>!#PW$JGfCy~pni9$-;o@}0~E@JJ(YgVjDZtj>X zY0+b=okk^8ILg|vg-ck;8MM|lpEyqU6+%KR{lp-p_*}sc0lqfkjR!aP&f~4XP*8uW zfmg%ZR@9R(wjl|CXF(Cse-H|~IhZ)2XE`yeDse{~wT3uyF(=5Q$HaewBA_p|Uc$Iz zsc+Xgx*R&9^!>KCAg}*Y;~L>T=RAA6uhf}~j8^6yRSr?uDePTQXsY4WqB|mZVCs+j zLLb=n8atMc)b_Tnv!&z4e^J&nr)oVT%IA%Qh@=R2k`B=w2(jslaX+eSoW{TsZv|f_ z7GwB|Sb$3r+mj^bWAK)nWQrU)dL~-$CnJ=!WYtMwx3v&Q_h#UQ) zCqcK&YM2Sm(vvdCO3fPzp~yR3{CU)*8}9N)rmwMM+p4hF@{ltwoo!}PUvMe zq)cM^oV-bjgqF*z{^l!vkNQ8CDT`+>Qd`=JO&R=OQ$S4NMMr0d)yvVCTVa^W#|P0c z77zO66E3WWCka8_`ddDA%&+b$%kvP_C9djN)1U4`FX#YOpnJoDq8d$mrBed)`gd0?Jak z^X~$@#ALR;>HNVN<(hiU(~Cm4~Yfh3N$`grLOjC`P5s6)c$N*2e>Xk}n*6p25c;Tgv$Yz*PZD&wqhu-xC#m33xgnEx= z;=l6pKJJgh&CQ__Q#>`B^&{By^GfipoXoRQuNAEIE3Xcw(C_+0Cg>p}wkjbCyu6P0I98?yK`f;@sb7O?P4p?!HJB4DeR3R`6kNuj zvUt528grsbv%6uHx)*)``%`mwi>2)~8L1EYgUeXSaTcmu=}0I&M;cb&xE)f7ZVptG z8f0EOiMPaxYo^lpOpXR8Z4S|N zHX3RQ-$=JQsfDv2g)xF_8?^|Mf|Rstn+ripX1?jE{-;e*lZfXZ_mIB!B|mM{E2 zDKw=y%%G$uBa$fXn)Cfg(`2owU<%SNoa`?jdV8wCN62)EzvVfP zbA|iInLBA3B!uy*s5FX`dpf*QIL_II|NQ6=V>zN9Q=3*s`j~OzsQDP`-TMnJ+5ylj zD+OetT6_!2aCqyiPV0F_oTebEeiZvxqR=ECkmWI;=}l^M+!cd(pB{ydG%3_%xuqM8 z;;LkyNO}rcHP}gO@VYaAy0)$AzfD&gggJrqutn_|q>SOQCx$T?wnvR61E#f*_N+0G zAe#!Hm+$P5)5@pPq~MzS70j5*!me2SNqE&y;9FgG@t<=h0J~g41Z*=I=Z6B7%!r=xe6A zv-bQ`G+c|6blfo<=-az`koY+Cmwv=-#lS1fVhKZF>D>v55|~V!nuLCe5FE_p;tb>f z7%b%QB;})sxGgA^%rrOBVzJFkwwldP{OE;_jI(;4V0jT@0-+eD*ZGYcQn6FD#BfdX z53&DN!ws1f4H7D2I_TYl{+9-|6(jYbq9cX)TbVkYQ?wN$;F7Z1rQcRI)s*aF;L7m@ zN!W;+(QOa1*=nkf=rHD55Dz7_@QdG+`RE4L$7g5yKUQ0FjleCiN4^OdH{m`20z0bd zzLHf`h~WDR-nI#Yrzf3fm#}u~lCe~6)iWA&Xt#}`W7$2TunL3-57=%u4L2c&dtw0` z1zR+0@TDX=anuYec*E+w9KnRMw>K{Jbxq7d*zBL-(D5O- zq&G{r@p?xVtp5j)HgUVbIQ1dyb85KLZ7&XU*{6|o`%ODlCZoEfZzg;_FaO0`y#20 z;7ck0(z}}aSh&qpOGo8EQk4XQcvLRk)K!LuVgJ2IZ@YHVO{tAlHdRlla0KGoxB*bY z%;`3&`}dW6T&(;hs{68VBkGQ%(7m%&s>+2i-iFyzNevxnuxis zz73YEC!9~*4EU+%%I?*M|1*#`D$SIfZThO=SwhNo`KxmHm5uA!y$DW`6;7HV9KfKj z*|0}y?UovOMU1hz9xo!z=jtXm7(Yb$wtTHA5OpquZAASNiFU^970~!l+UA16mB1wv z=D*f8S8NtYAj@S4*?9Od{CA;!a|N)dutllI<90Tq>VDdmKyih8#js@Rh`ffb3T-C8 z3`2(hH5oJLu6JN~!-B-?!9^gS)g0?~PmSNaPfP4iX%V>93Yy~HFi1a$s`+7fHQg=n zwf~5iV|NN@QG1!r7Y9Bzf|Ij9|F6mHvnoKAz;Pje26NawVItpv4}bDQ1ZL25i_ z%H~S1v>lq~Wb;=*|4cN;8$VegonO6eDv<&0RNwy+Azc94tzlUP8rUjP58 z9i0-^0_lYFA6wKtZV2G{idmViPA0LHWI~6;QNTRd`uZ1O8Qs>vg)O2k>PT{;lh3(K zKcilkJQO^4f|GK{J9>4n^yj-t0^7Kl@a|zjy3LnJJfHv9ijHuN#*uA#F)AhTXlItH zLCQ}nu=#(?Ipo_eSW-sZ15;2F`*b;`XYC>I7xYmPpcvb6yW;u{)A z{C>QYa$tbn%4Yg%VG?2q!?2w&rsQG&^4U}<4-AV&KENs}>{&R|8jVT{FjLDkH;;oT zfaC9|!8n{yvfUJbdtd@dg`($s4}Ph5qGDbihKgEu1WUJy)b4*a)r*8y3yKBeG zW|?QL=vjID5SK~VXhr5F5q*He9=-n*KT&ff%=RMlY*4pv!2yo)Je#WP@@_sBk@Q>W z6wyArOhzXF_cL2XY%{Z88Bv@Uek+JR%Fw-$$pd4{$gmKn4r`l5Stq+^x;UHvOn??L zn?Kr`c8RhY5%4R-etD-+Hpa>pI`k@pi!psq6%7FbKd+6FOlT5fONSI zc*8L$9duL})sPDpbFs+7bBr7V;LdIAPK?ZZs-Zzvtbb3#?!Jsb$5i+Nw1`Ia!$P$3 zHp9-)dyRJCD!!3)eySB+ZNh!#(Q`Y}3g#sOGY{4%cYEK|3^UN6+NC#l0+zpZ^7cL+=HK=MfQ(wJp=v)=hGOJ2ifpSVdbN9P~P_H^_OJBjBNsEEt-JW(-Fe z7KlG}fFl$yDkc^rY9(3Il(<&+xG<|-Mh zIELwsd8LmsY#(9Q1#AHLtLyZsY!J;%Xjm7!MmaJ`^2a0<(a*HDpV$KD|u?we{#sJS-e3@DFgVe=5O0G`9|zSJFkDZVQ1mm^8w($M7|| z!4qCGG>XVpcOFsZF(h%Dp3LLXJpm^@HfTJ!C183HbztMftwa8<%7z;tK@p;_2Nec3 z^&zj*EE=y%XUS*U6iZSb7*f|^FfSZo|BwTPI<;RPL05@q;UwFxk4FaAHvcm|gFF^b z8eceBhGG2es&;k`L~9i50>ND;UPL2_xTypg-6(i?l?Mp0yV;lo}I~ zw<6;ZLv2|8lS42NAy$e?|8@P=8z15|jn|z~WRl3<3ECeelQu3YfK>)lEi{SDnHd&E zvn?kn+n_r|1l!&%{C~IuGC_Xr1eS2xQi)Cz`49l=B@`E_sdE<@sp6WlL4u1N`Jz5Le1 zg%)XsuFBoaor_jx#{L$eChD`vKLmy^}JbZ|gws2nQgvd1n1ruZ+ zd>`qR9=+*Oy_0!)xV3jF?Dvk5J!}B|y zcAOnC@Y7J-eU>6%*5-#}Gh%bBnYsQ+(Uo-p|!CK9?O=eJjG z{`urkW#*#6B)8fLUlO`E68D#RChRV7Lv8Y1pNKs3Vr7fP^B!*t7_Qxv;RIilCl!mc zg=Lq6Lsf>*d>z@V`p-1y?gOpigsw-NPl7dCxY4B*@ZcTvhsu-V8kq9XAhmNjFPjrM~g1Y z#EW1I`D@dQjb*~izWv_EQp9G>yYg^C6xU+EDS`U8O&w^LJQP*NQ=pq86kvF!uH7mM z5m(0=3mgg4{(Zej;RG)Oy)Bjc21knRNF%V$oauCKE>WpQl(L$A{iKXL=1!oWt@)j3 zAssP3x08`ziIU6+^mMecdJSN=wCOKoJs_&0{>QBzUbR?{HqS7%nPzpG2!6J>5a^LN zAk<{pAzzoJM}Ghq@$Kqzpt%&$d~#cdhI*0OsavTcB-48oy>~$uhSam>)1VHs(o12^ zzDi0(uTq$HeK%M_PbY|p@*FTJGs47EC5BU}7hp10WVa75PPapJ-Y0$!wrwT5=Ha3o z!Q~Zg{46r^+#ntC$ne~WVj!TMc6StsmPCOGnCZR`%d+%Z)X!#14nGbTX4+t(7rL{` z$1M)Ni_b;u?pVn3?z8^Nx`7`Ms!efnMa66SU+bp(t*fC8YuO(l8uY)0K|TQXSy>m# zy%o3aaM>9+^n592%+I+<&6(ew< zV56jzUX0W}!m?ygZz;lX;`A>fu4zCm0^3b$5Fab+PI>K7kr?v$qEup2VDChtk;%dg zbUr%d2%~H`RILCp2lFaIVMkqWMx{uk`_y$WR;z01hUyQu6KKR|Xa&-RdXX1sEhT&9 zwNpR39%xz*pkC28IV7?e>gV<}d@HZGhPFQbcycSw1{_AtbziQSu1i#}|N{ z3IuHz8&X^>tc+?Kt$})9<#hGaTe4snDo%eSK+4y0IB^qSv1%*7_?6s}$(jSV5InRb z;tPnwvZ#!Y38nJIZ1-3Xj-;@LUw!}Wk#*$kbYEh4O@oJye!P4)T7+$c+U=RRvdjj_ zNYrk-;OGT^NNfQ?jz@*eFB0&?2jmh=bAsA6Z-wSC4NB8yC$3uWlTZLpBGV(4S9(w# zsfjh_d#9Il>Jq)E0*MCA_a{?kD{@#&V$Nc!@M^4dB3_%0Do{UodX9$~GJbo7eu%nB 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

6wo+THrXFuI{H$S?Gs>4I({a6=Z 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 F002zB^b`O9 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/banners.js b/internal/api/adminui/banners.js new file mode 100644 index 0000000..ee8b1e2 --- /dev/null +++ b/internal/api/adminui/banners.js @@ -0,0 +1,189 @@ +/* «Слайды на главной»: шапка-витрина, превью слайдов, фильтр по статусу, порядок перетаскиванием. + Форма слайда — прежние bannerFields / normalizeBanner / editBanner. */ + +const BN_THEME_BG = { navy: '#2f4a68', lilac: '#8f86c9', orange: '#f08a3c' }; +const BN_ICONS = { + grip: '', + pencil: '', + dots: '', + plus: '', +}; +const bnIcon = (n) => ``; + +let bannersCache = []; + +const Banners = { + state(b) { + const now = Date.now(); + if (!b.published) return ['hidden', 'Скрыт', 'grey']; + if (b.ends_at && new Date(b.ends_at).getTime() <= now) return ['expired', 'Показ окончен', 'off']; + if (b.starts_at && new Date(b.starts_at).getTime() > now) return ['waiting', 'Скоро', 'wait']; + return ['live', 'На сайте', '']; + }, + + render() { + const f = $('#bn-filter').value; + const list = bannersCache.filter((b) => !f || Banners.state(b)[0] === f); + const live = bannersCache.filter((b) => Banners.state(b)[0] === 'live').length; + $('#bn-count').textContent = `${list.length} ${plural(list.length, 'слайд', 'слайда', 'слайдов')}`; + $('#bn-live').textContent = `на сайте сейчас: ${live}`; + const tb = $('#bn-body'); tb.textContent = ''; + if (!list.length) { + const tr = el('tr'); tr.append(el('td', { colSpan: 9, className: 'muted dr-empty', textContent: 'Слайдов с таким статусом нет.' })); tb.append(tr); + } + // перетаскивать можно только полный список: иначе порядок скрытых строк непонятен + const draggable = !f; + for (const b of list) tb.append(Banners.row(b, draggable)); + }, + + thumb(b) { + const box = el('div', { className: 'bn-thumb' }); + if (b.layout === 'image' && b.image) { + box.append(el('img', { src: b.image, alt: '', loading: 'lazy' })); + return box; + } + box.classList.add('bn-thumb--made'); + box.style.background = BN_THEME_BG[b.theme] || BN_THEME_BG.navy; + box.append(el('b', { textContent: b.title || 'Без заголовка' })); + if (b.subtitle) box.append(el('small', { textContent: b.subtitle })); + if (b.layout === 'doctor' && b.image) box.append(el('img', { className: 'bn-thumb__doc', src: b.image, alt: '', loading: 'lazy' })); + return box; + }, + + row(b, draggable) { + const tr = el('tr'); + tr.dataset.id = b.id; + const grip = el('td', { className: 'bn-grip' }); + if (draggable) { + const h = el('button', { type: 'button', className: 'grip', title: 'Перетащите, чтобы поменять порядок' }); + h.setAttribute('aria-label', 'Переместить слайд «' + (b.title || 'картинка') + '». Стрелки вверх и вниз меняют порядок'); + h.innerHTML = bnIcon('grip'); + h.onkeydown = (e) => { + if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return; + e.preventDefault(); + Banners.move(b.id, e.key === 'ArrowUp' ? -1 : 1); + }; + grip.append(h); + tr.draggable = true; + tr.addEventListener('dragstart', (e) => { Banners.dragId = b.id; tr.classList.add('dragging'); e.dataTransfer.effectAllowed = 'move'; }); + tr.addEventListener('dragend', () => { tr.classList.remove('dragging'); Banners.dragId = null; }); + tr.addEventListener('dragover', (e) => { if (Banners.dragId && Banners.dragId !== b.id) { e.preventDefault(); tr.classList.add('drop'); } }); + tr.addEventListener('dragleave', () => tr.classList.remove('drop')); + tr.addEventListener('drop', (e) => { e.preventDefault(); tr.classList.remove('drop'); Banners.dropOn(b.id); }); + } + const pic = el('td'); pic.append(Banners.thumb(b)); + const title = el('td', { className: 'bn-title' }); + title.append(el('b', { textContent: b.title || '(картинка без заголовка)' })); + const from = ruDate(localDate(b.starts_at)), to = ruDate(localDate(b.ends_at)); + const show = from && to ? `${from} —\n${to}` : to ? 'до ' + to : from ? 'с ' + from : 'всегда'; + const sort = el('td'); sort.append(el('span', { className: 'bn-sort', textContent: b.sort })); + const [, label, cls] = Banners.state(b); + const st = el('td'); st.append(el('span', { className: 'pill bn-pill ' + cls, textContent: label })); + const act = el('td', { className: 'dr-actions' }); + const edit = el('button', { type: 'button', className: 'bn-edit' }); + edit.innerHTML = bnIcon('pencil') + 'Изменить'; + edit.onclick = () => editBanner(b); + const more = el('button', { type: 'button', className: 'icon-btn', title: 'Ещё' }); + more.setAttribute('aria-label', 'Ещё действия со слайдом'); + more.setAttribute('aria-haspopup', 'menu'); + more.innerHTML = bnIcon('dots'); + more.onclick = (e) => { e.stopPropagation(); Banners.menu(b, more); }; + act.append(edit, more); + tr.append(grip, pic, title, + el('td', { className: 'muted bn-small', textContent: LAYOUTS[b.layout] || b.layout }), + el('td', { className: 'muted bn-small bn-link', textContent: b.link_url || '—' }), + el('td', { className: 'muted bn-small bn-dates', textContent: show }), + sort, st, act); + return tr; + }, + + menu(b, anchor) { + Doctors.closeMenu(); + const m = el('div', { className: 'menu', id: 'dr-menu', role: 'menu' }); + const item = (text, fn, danger) => { + const x = el('button', { type: 'button', role: 'menuitem', className: danger ? 'danger' : '', textContent: text }); + x.onclick = () => { Doctors.closeMenu(); fn(); }; + m.append(x); + }; + if (b.link_url) item('Открыть ссылку слайда', () => window.open(b.link_url, '_blank', 'noopener')); + item(b.published ? 'Скрыть с сайта' : 'Показать на сайте', () => Banners.save(b, { published: !b.published }, + b.published ? 'Слайд скрыт' : 'Слайд на сайте')); + item('Удалить слайд', () => delBanner(b), true); + document.body.append(m); + const r = anchor.getBoundingClientRect(); + m.style.top = (r.bottom + window.scrollY + 6) + 'px'; + m.style.left = Math.max(12, r.right + window.scrollX - m.offsetWidth) + 'px'; + m.querySelector('button').focus(); + anchor.setAttribute('aria-expanded', 'true'); + Doctors.menuAnchor = anchor; + }, + + body(b, change = {}) { + return { + position: b.position || 'home_hero', layout: b.layout, theme: b.theme, title: b.title, subtitle: b.subtitle, + badge: b.badge, chips: b.chips || [], image_id: b.image_id ?? null, mobile_image_id: b.mobile_image_id ?? null, + link_url: b.link_url, button_text: b.button_text, disclaimer: b.disclaimer, + starts_at: b.starts_at || null, ends_at: b.ends_at || null, sort: b.sort, published: b.published, ...change, + }; + }, + + async save(b, change, message) { + try { + await api('/admin/banners/' + b.id, { method: 'PUT', body: JSON.stringify(Banners.body(b, change)) }); + toast(message); + await loadBanners(); + } catch (e) { toast(e.message, true); } + }, + + // Новый порядок: 10, 20, 30… — сохраняем только слайды, у которых номер изменился. + async reorder(ids) { + const byId = new Map(bannersCache.map((b) => [b.id, b])); + const changed = ids.map((id, i) => [byId.get(id), (i + 1) * 10]).filter(([b, sort]) => b.sort !== sort); + if (!changed.length) return; + try { + for (const [b, sort] of changed) { + await api('/admin/banners/' + b.id, { method: 'PUT', body: JSON.stringify(Banners.body(b, { sort })) }); + } + toast('Порядок слайдов сохранён'); + } catch (e) { toast(e.message, true); } + await loadBanners(); + }, + dropOn(targetId) { + const ids = bannersCache.map((b) => b.id); + const from = ids.indexOf(Banners.dragId), to = ids.indexOf(targetId); + if (from < 0 || to < 0) return; + ids.splice(to, 0, ids.splice(from, 1)[0]); + Banners.reorder(ids); + }, + async move(id, dir) { + const ids = bannersCache.map((b) => b.id); + const i = ids.indexOf(id), j = i + dir; + if (j < 0 || j >= ids.length) return; + [ids[i], ids[j]] = [ids[j], ids[i]]; + await Banners.reorder(ids); + $(`#bn-body tr[data-id="${id}"] .grip`)?.focus(); + }, + + // Фото холла в шапке — assets/hall.webp; без файла остаётся мягкий градиент. + async hero() { + const ok = await fetch('assets/hall.webp', { method: 'HEAD', credentials: 'same-origin' }).then((r) => r.ok).catch(() => false); + $('#bn-hero').classList.toggle('has-photo', ok); + }, +}; + +async function loadBanners() { + const r = await api('/admin/banners?position=home_hero'); + bannersCache = (r.data || []).slice().sort((a, b) => a.sort - b.sort || a.id - b.id); + Banners.render(); +} + +$('#bn-filter').onchange = () => Banners.render(); +$('#bn-add').insertAdjacentHTML('afterbegin', bnIcon('plus')); +$('#bn-add').onclick = () => { + const next = (bannersCache.reduce((m, b) => Math.max(m, b.sort), 0) || 0) + 10; + openDialog('Новый слайд', bannerFields({ published: true, sort: next, layout: 'image', theme: 'navy' }), async (d) => { + await api('/admin/banners', { method: 'POST', body: JSON.stringify(normalizeBanner(d)) }); + toast('Слайд добавлен'); loadBanners(); + }); +}; +Banners.hero(); diff --git a/internal/api/adminui/clinic.js b/internal/api/adminui/clinic.js new file mode 100644 index 0000000..f32f9a1 --- /dev/null +++ b/internal/api/adminui/clinic.js @@ -0,0 +1,32 @@ +/* Клиника раздела «Отчёты»: DANA или «Очевидно». У каждой свои звонки АТС, отчёты по каналам + и дашборд; сервер получает её в ?clinic= (без параметра — DANA). Выбор запоминается в браузере. */ +const ReportClinic = { + titles: { dana: 'DANA', ochevidno: 'Очевидно' }, + brands: { dana: 'DANA Medical', ochevidno: 'Очевидно' }, + key: 'dana', + + init() { + try { + const saved = localStorage.getItem('rp-clinic'); + if (saved && ReportClinic.titles[saved]) ReportClinic.key = saved; + } catch { /* без хранилища — DANA */ } + for (const b of document.querySelectorAll('#rp-clinic button[data-clinic]')) b.onclick = () => ReportClinic.set(b.dataset.clinic); + const share = document.getElementById('rp-share'); + if (share) share.onclick = () => Reports.shareDialog(); + ReportClinic.sync(); + }, + get title() { return ReportClinic.titles[ReportClinic.key]; }, + get brand() { return ReportClinic.brands[ReportClinic.key]; }, + q(path) { return path + (path.includes('?') ? '&' : '?') + 'clinic=' + encodeURIComponent(ReportClinic.key); }, + sync() { + for (const b of document.querySelectorAll('#rp-clinic button[data-clinic]')) b.setAttribute('aria-pressed', String(b.dataset.clinic === ReportClinic.key)); + }, + set(key) { + if (!ReportClinic.titles[key] || key === ReportClinic.key) return; + ReportClinic.key = key; + try { localStorage.setItem('rp-clinic', key); } catch { /* выбор действует до перезагрузки */ } + ReportClinic.sync(); + Reports.onClinicChange(); + }, +}; +ReportClinic.init(); diff --git a/internal/api/adminui/dashboard.js b/internal/api/adminui/dashboard.js new file mode 100644 index 0000000..71db2ad --- /dev/null +++ b/internal/api/adminui/dashboard.js @@ -0,0 +1,289 @@ +/* Главный экран админки по композиции референса: слева крупная визуализация сердца + с плавающими показателями, справа заголовок, сводка клиники, графики заявок и последние заявки. + Компоненты: ClinicSummary, HeartVisualization, VitalBadge, TrendsCard, LeadsTable. */ + +const LEAD_STATUS = { + new: ['Новая', 'off'], + in_progress: ['В работе', 'sky'], + done: ['Обработана', ''], + spam: ['Спам', 'grey'], +}; +const plural = (n, one, few, many) => { + const a = Math.abs(n) % 100, b = a % 10; + return a > 10 && a < 20 ? many : b === 1 ? one : b >= 2 && b <= 4 ? few : many; +}; +const svgEl = (tag, attrs = {}) => { + const n = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (const [k, v] of Object.entries(attrs)) n.setAttribute(k, v); + return n; +}; + +/* ---------- ClinicSummary: заголовок и три показателя без карточек ---------- */ +function ClinicSummary(d) { + const box = el('div', { className: 'summary reveal' }); + const h = el('h1', { className: 'summary__title' }); + h.append('Панель', el('br'), 'клиники'); + const stats = el('div', { className: 'summary__stats' }); + for (const [value, label, tab] of [ + [d.doctors, plural(d.doctors, 'врач', 'врача', 'врачей'), 'doctors'], + [d.services, plural(d.services, 'услуга', 'услуги', 'услуг'), 'services'], + [d.branches, plural(d.branches, 'филиал', 'филиала', 'филиалов'), 'settings'], + ]) { + const b = el('button', { type: 'button', className: 'stat' }); + b.append(el('b', { textContent: value }), el('span', { textContent: label })); + b.onclick = () => go(tab); + stats.append(b); + } + box.append(h, stats); + return box; +} + +/* ---------- VitalBadge: маленькая белая карточка рядом с маркером ---------- */ +function VitalBadge({ label, value, unit, tab, x, y }) { + const b = el('button', { type: 'button', className: 'vital reveal' }); + b.style.left = x; b.style.top = y; + b.append(el('span', { className: 'vital__label', textContent: label }), el('span', { className: 'vital__value', textContent: value })); + if (unit) b.append(el('span', { className: 'vital__unit', textContent: unit })); + b.onclick = () => go(tab); + return b; +} + +/* ---------- HeartVisualization ---------- */ +// Реалистичный рендер сердца — файл assets/heart.webp (прозрачный фон). Если его нет, +// показываем честную пометку, а не рисованную заглушку. +const HEART_SRC = 'assets/heart.webp'; +let heartAvailable = null; +async function heartExists() { + if (heartAvailable === null) { + heartAvailable = await fetch(HEART_SRC, { method: 'HEAD', credentials: 'same-origin' }).then((r) => r.ok).catch(() => false); + } + return heartAvailable; +} + +async function HeartVisualization(d) { + const box = el('div', { className: 'heart' }); + const hasHeart = await heartExists(); + if (hasHeart) { + box.append(el('img', { className: 'heart__img reveal', src: HEART_SRC, alt: '', decoding: 'async' })); + } else { + const miss = el('div', { className: 'heart__missing reveal' }); + miss.innerHTML = '
Нужен 3D-рендер сердцаФайл assets/heart.webp: прозрачный фон, около 1400 px по высоте.
'; + box.append(miss); + } + // маркеры на поверхности и карточки рядом — расположены асимметрично, как в референсе + const spots = [ + { mx: 36, my: 27, badge: { label: 'Новые заявки', value: d.new_leads, unit: d.leads_today ? `сегодня ${d.leads_today}` : 'ждут ответа', tab: 'leads', x: '52%', y: '6%' } }, + { mx: 50, my: 49, badge: { label: 'Отзывы на проверке', value: d.pending_reviews, unit: 'ждут публикации', tab: 'reviews', x: '64%', y: '42%' } }, + { mx: 41, my: 74, badge: { label: 'Акции на сайте', value: d.active_promos, unit: d.active_promos ? 'действуют сейчас' : 'нет активных', tab: 'promos', x: '2%', y: '80%' } }, + ]; + spots.forEach((s, i) => { + const b = VitalBadge(s.badge); + b.style.animationDelay = (0.5 + i * 0.15) + 's'; + // маркеры отмечают точки на поверхности сердца — без рендера им не на чем стоять + if (!hasHeart) { box.append(b); return; } + const m = el('span', { className: 'marker reveal' }); + m.style.left = s.mx + '%'; m.style.top = s.my + '%'; + m.style.animationDelay = (0.4 + i * 0.15) + 's'; + m.setAttribute('aria-hidden', 'true'); + box.append(m, b); + }); + return box; +} + +/* ---------- TrendsCard: два лёгких графика с полосой обычного уровня ---------- */ +// «Обычный уровень» — середина значений за период (от 25-го до 75-го перцентиля). +// Это ориентир для глаза, а не норма. +function quantile(sorted, q) { + const i = (sorted.length - 1) * q, lo = Math.floor(i), hi = Math.ceil(i); + return sorted[lo] + (sorted[hi] - sorted[lo]) * (i - lo); +} +function smoothPath(pts) { + let d = `M ${pts[0][0]} ${pts[0][1]}`; + for (let i = 0; i < pts.length - 1; i++) { + const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2; + const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6]; + const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6]; + d += ` C ${c1[0]} ${c1[1]}, ${c2[0]} ${c2[1]}, ${p2[0]} ${p2[1]}`; + } + return d; +} + +let chartSeq = 0; +function LineChart({ values, dates, band, framed, showX, tipLabel, title }) { + const id = 'c' + (++chartSeq); + const W = 1000, H = framed ? 178 : 150, L = 62, R = 16, T = framed ? 26 : 16, B = showX ? 38 : 14; + const wrap = el('div', { className: 'chart' }); + const s = svgEl('svg', { viewBox: `0 0 ${W} ${H}`, role: 'img', 'aria-label': `${title}: ${values.join(', ')}` }); + // шкала по фактическому размаху с запасом, а не от нуля: так видно, как день отличается от обычного + const vmax = Math.max(...values), vmin = Math.min(...values); + const pad = Math.max((vmax - vmin) * 0.18, 1); + const max = vmax + pad, min = Math.max(0, vmin - pad); + const span = (max - min) || 1; + const y = (v) => T + (H - T - B) * (1 - (v - min) / span); + const x = (i) => L + (W - L - R) * (values.length === 1 ? 0.5 : i / (values.length - 1)); + const sorted = [...values].sort((a, b) => a - b); + const lo = quantile(sorted, 0.25), hi = quantile(sorted, 0.75); + const yTop = y(hi), yBot = Math.max(y(lo), yTop + 10); + + const defs = svgEl('defs'); + const bandGrad = svgEl('linearGradient', { id: id + 'b', x1: '0', x2: '1' }); + band.stops.forEach(([o, c, a]) => bandGrad.append(svgEl('stop', { offset: o, 'stop-color': c, 'stop-opacity': a }))); + const peakGrad = svgEl('linearGradient', { id: id + 'p', x1: '0', x2: '0', y1: String(y(vmax)), y2: String(yTop), gradientUnits: 'userSpaceOnUse' }); + peakGrad.append(svgEl('stop', { offset: '0', 'stop-color': '#f2566a', 'stop-opacity': '0.95' }), svgEl('stop', { offset: '1', 'stop-color': '#f2566a', 'stop-opacity': '0' })); + const lineGrad = svgEl('linearGradient', { id: id + 'l', x1: '0', x2: '1' }); + [['0', 0.25], ['0.1', 1], ['0.9', 1], ['1', 0.25]].forEach(([o, a]) => lineGrad.append(svgEl('stop', { offset: o, 'stop-color': '#8b8f8d', 'stop-opacity': a }))); + const clip = svgEl('clipPath', { id: id + 'k' }); + clip.append(svgEl('rect', { x: 0, y: 0, width: W, height: yTop })); + defs.append(bandGrad, peakGrad, lineGrad, clip); + s.append(defs); + + if (framed) s.append(svgEl('rect', { class: 'chart__frame', x: L - 18, y: 6, width: W - L - R + 30, height: H - B - 6 + 4, rx: 18 })); + s.append(svgEl('rect', { x: L, y: yTop, width: W - L - R, height: yBot - yTop, fill: `url(#${id}b)`, rx: 2 })); + // подписи — границы полосы, как в референсе (280 / 52) + const fmt = (v) => (Math.round(v * 10) / 10).toString().replace('.', ','); + s.append(Object.assign(svgEl('text', { class: 'chart__axis', x: L - 26, y: yTop + 4, 'text-anchor': 'end' }), { textContent: fmt(hi) })); + s.append(Object.assign(svgEl('text', { class: 'chart__axis', x: L - 26, y: yBot + 4, 'text-anchor': 'end' }), { textContent: fmt(lo) })); + + const pts = values.map((v, i) => [x(i), y(v)]); + const d = smoothPath(pts); + // розово-красная заливка пиков: всё, что выше полосы, закрашено до её верхней границы + s.append(svgEl('path', { d: `${d} L ${x(values.length - 1)} ${yTop} L ${x(0)} ${yTop} Z`, fill: `url(#${id}p)`, 'clip-path': `url(#${id}k)` })); + const line = svgEl('path', { class: 'chart__line draw', d, stroke: `url(#${id}l)` }); + s.append(line); + + if (showX) { + const df = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }); + dates.forEach((dt, i) => { + if (i % 2) return; + s.append(Object.assign(svgEl('text', { class: 'chart__axis', x: x(i), y: H - 10, 'text-anchor': 'middle' }), { textContent: df.format(new Date(dt + 'T12:00:00')).replace('.', '') })); + }); + } + + // подсказка с измерением при наведении + const guide = svgEl('line', { class: 'chart__guide', y1: T - 6, y2: H - B, visibility: 'hidden' }); + const dot = svgEl('circle', { class: 'chart__dot', r: 4.5, visibility: 'hidden' }); + s.append(guide, dot); + const tip = el('div', { className: 'tooltip', hidden: true }); + const step = (W - L - R) / Math.max(values.length - 1, 1); + values.forEach((v, i) => { + const hit = svgEl('rect', { class: 'chart__hit', x: x(i) - step / 2, y: 0, width: step, height: H, tabindex: '0' }); + const show = () => { + guide.setAttribute('x1', x(i)); guide.setAttribute('x2', x(i)); guide.setAttribute('visibility', 'visible'); + dot.setAttribute('cx', x(i)); dot.setAttribute('cy', y(v)); dot.setAttribute('visibility', 'visible'); + const date = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', weekday: 'short' }).format(new Date(dates[i] + 'T12:00:00')); + tip.innerHTML = ''; + tip.append(el('b', { textContent: `${v} ${plural(v, tipLabel[0], tipLabel[1], tipLabel[2])}` }), date); + tip.style.left = (x(i) / W * 100) + '%'; tip.style.top = (y(v) / H * 100) + '%'; + tip.hidden = false; + }; + const hide = () => { tip.hidden = true; guide.setAttribute('visibility', 'hidden'); dot.setAttribute('visibility', 'hidden'); }; + hit.addEventListener('mouseenter', show); hit.addEventListener('focus', show); + hit.addEventListener('mouseleave', hide); hit.addEventListener('blur', hide); + s.append(hit); + }); + wrap.append(s, tip); + // длина линии для анимации отрисовки + requestAnimationFrame(() => { try { line.style.setProperty('--len', Math.ceil(line.getTotalLength())); } catch { /* старый браузер */ } }); + return wrap; +} + +// Пример для пустой базы: до запуска сайта заявок почти нет, а пустой график ничего не показывает. +function demoDays(days) { + const leads = [5, 12, 7, 16, 19, 9, 5, 11, 17, 22, 12, 6, 13, 20]; + const appts = [2, 7, 4, 10, 12, 5, 3, 6, 11, 14, 7, 3, 8, 12]; + return days.map((d, i) => ({ date: d.date, leads: leads[i % leads.length], appointments: appts[i % appts.length] })); +} + +function TrendsCard(d) { + const card = el('section', { className: 'card trends reveal', ariaLabel: 'Заявки за 14 дней' }); + const real = d.days.reduce((a, x) => a + x.leads, 0); + const demo = real < 5; + const days = demo ? demoDays(d.days) : d.days; + const head = el('div', { className: 'trends__head' }); + head.append(el('h2', { textContent: 'Заявки за 14 дней' })); + if (demo) head.append(el('span', { className: 'trends__demo', textContent: `Пример данных: заявок пока ${real}` })); + const legend = el('div', { className: 'trends__legend' }); + legend.innerHTML = 'все заявки' + + 'записи на приёмполоса — обычный уровень'; + head.append(legend); + card.append(head); + const dates = days.map((x) => x.date); + card.append(LineChart({ + values: days.map((x) => x.leads), dates, framed: true, showX: true, tipLabel: ['заявка', 'заявки', 'заявок'], title: 'Все заявки по дням', + band: { stops: [['0', '#f4e9b2', 0], ['0.1', '#f2e2a0', 1], ['0.4', '#f5c29f', 1], ['0.55', '#f0a8ab', 0.95], ['0.85', '#f2e2a0', 1], ['1', '#f4e9b2', 0]] }, + })); + card.append(LineChart({ + values: days.map((x) => x.appointments), dates, framed: false, showX: false, tipLabel: ['запись', 'записи', 'записей'], title: 'Записи на приём по дням', + band: { stops: [['0', '#bfe8d6', 0], ['0.12', '#b3e6cc', 1], ['0.45', '#93c9ad', 1], ['0.7', '#b3e6cc', 1], ['1', '#bfe8d6', 0]] }, + })); + return card; +} + +/* ---------- LeadsTable: последние заявки ---------- */ +async function LeadsTable() { + const card = el('section', { className: 'card leads-card reveal' }); + const head = el('div', { className: 'trends__head', style: 'margin-bottom:6px' }); + head.append(el('h2', { textContent: 'Последние заявки' })); + const all = el('button', { type: 'button', className: 'card-link', textContent: 'Все заявки →' }); + all.onclick = () => go('leads'); + head.append(all); + card.append(head); + const r = await api('/admin/leads?limit=4'); + const list = r.data || []; + const table = el('table', { className: 'leads-table' }); + table.innerHTML = 'Дата / времяПациентТелефонОткудаВрач или услугаСтатус'; + const tb = el('tbody'); + if (!list.length) { + const tr = el('tr'); tr.append(el('td', { colSpan: 6, className: 'soft', textContent: 'Заявок пока нет — они появятся здесь сразу после отправки формы на сайте.' })); tb.append(tr); + } + const df = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' }); + const tf = new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }); + for (const l of list) { + const tr = el('tr'); + const when = el('td'); when.append(df.format(new Date(l.created_at)), el('small', { textContent: tf.format(new Date(l.created_at)) })); + const [label, cls] = LEAD_STATUS[l.status] || [l.status, 'grey']; + const st = el('td'); st.append(el('span', { className: 'pill ' + cls, textContent: label })); + tr.append(when, el('td', { className: 'strong', textContent: l.name || '—' }), el('td', { textContent: l.phone }), + el('td', { className: 'soft', textContent: l.form_title || '—' }), el('td', { className: 'soft', textContent: l.doctor_name || l.service_name || '—' }), st); + tr.tabIndex = 0; + const open = () => { go('leads'); Leads.open(l); }; + tr.onclick = open; tr.onkeydown = (e) => { if (e.key === 'Enter') open(); }; + tb.append(tr); + } + table.append(tb); + card.append(table); + return card; +} + +/* ---------- сборка экрана ---------- */ +const Dashboard = { + // Сводка нужна и шапке (календарь, колокольчик, бейдж заявок) — на любой странице админки. + async summary() { + const r = await api('/admin/dashboard'); + const d = r.data; + d.days = d.days || []; d.upcoming = d.upcoming || []; d.todo = d.todo || []; + Topbar.calendar(d.upcoming); + Topbar.todo(d.todo); + const badge = $('#ld-badge'); + if (badge) { badge.textContent = d.new_leads; badge.hidden = !d.new_leads; } + return d; + }, + + async load() { + const root = $('#tab-home'); + try { + const d = await Dashboard.summary(); + const grid = el('div', { className: 'dash' }); + const left = el('div', { className: 'dash__heart' }); + const right = el('div', { className: 'dash__right' }); + left.append(await HeartVisualization(d)); + right.append(ClinicSummary(d), TrendsCard(d), await LeadsTable()); + grid.append(left, right); + root.textContent = ''; + root.append(grid); + } catch (e) { + root.textContent = ''; + root.append(el('div', { className: 'card', textContent: 'Не удалось загрузить сводку: ' + e.message })); + } + }, +}; diff --git a/internal/api/adminui/doctors-dash.js b/internal/api/adminui/doctors-dash.js new file mode 100644 index 0000000..44dface --- /dev/null +++ b/internal/api/adminui/doctors-dash.js @@ -0,0 +1,236 @@ +/* «Дашборд врачей»: полнота карточек, онлайн-запись, отзывы, заявки. + Всё считается по текущим данным базы при каждом открытии. */ + +const DD_ICONS = { + id: '', + photo: '', + clock: '', + award: '', + alert: '', + star: '', + link: '', + users: '', + check: '', + chat: '', + calendar: '', + download: '', +}; +const ddIcon = (n) => ``; +const DoctorDash = { + q: null, + + async open() { + try { + const [r] = await Promise.all([api('/admin/doctors-quality'), adminDoctors.length ? null : loadAdminDoctors()]); + DoctorDash.q = r.data; + DoctorDash.fillFilter(); + DoctorDash.render(); + } catch (e) { toast(e.message, true); } + }, + + fillFilter() { + const sel = $('#dd-spec'); const keep = sel.value; + sel.textContent = ''; + sel.append(el('option', { value: '', textContent: 'Все специальности' })); + for (const s of specialtiesCache.filter((x) => x.doctors)) sel.append(el('option', { value: s.id, textContent: `${s.name_plural} (${s.doctors})` })); + sel.value = keep; if (sel.value !== keep) sel.value = ''; + }, + + // врачи с данными из обоих источников + rows() { + const byId = new Map(DoctorDash.q.doctors.map((x) => [x.id, x])); + const spec = $('#dd-spec').value; + return adminDoctors.filter((d) => !spec || String(d.specialty_id) === spec) + .map((d) => ({ d, q: byId.get(d.id) || {} })); + }, + + render() { + const rows = DoctorDash.rows(), n = rows.length, Q = DoctorDash.q; + const cnt = (f) => rows.filter(f).length; + const basic = cnt(DoctorDash.metrics[0][1]); + $('#dd-count').textContent = `${n} ${plural(n, 'врач', 'врача', 'врачей')}`; + + // четыре круглых показателя + const stats = [ + ['id', '#e3eefb', '#4a7fc4', basic, 'ФИО, должность и специальность', ''], + ['photo', '#fbe7ec', '#c7627a', cnt(({ d }) => !!d.photo), 'с фото', 'no-photo'], + ['clock', '#fdeedd', '#c9803f', cnt(({ d }) => d.practice_start_year != null), 'указан стаж', 'no-experience'], + ['award', '#e2f4ec', '#3c9670', cnt(({ d }) => !!d.category), 'с категорией', 'no-category'], + ]; + const box = $('#dd-stats'); box.textContent = ''; + for (const [icon, bg, ink, v, label, filter] of stats) { + const b = el('button', { type: 'button', className: 'dd-stat' }); + b.innerHTML = `${ddIcon(icon)} + ${v}из ${n}${label}`; + b.onclick = () => DoctorDash.toList(filter); + box.append(b); + } + + DoctorDash.chart(rows); + + // требуют внимания + const noPhoto = n - stats[1][3]; + const withServices = cnt(({ d }) => d.service_ids.length > 0); + const att = [ + ['photo', noPhoto, plural(noPhoto, 'карточка без фото', 'карточки без фото', 'карточек без фото'), () => DoctorDash.toList('no-photo')], + ['chat', Q.reviews_pending_total, plural(Q.reviews_pending_total, 'отзыв ждёт публикации', 'отзыва ждут публикации', 'отзывов ждут публикации'), () => go('reviews')], + ['link', withServices, `${plural(withServices, 'врач', 'врача', 'врачей')} с указанными услугами`, () => DoctorDash.toList('no-services')], + ]; + const ab = $('#dd-attention'); ab.textContent = ''; + for (const [icon, v, label, fn] of att) { + const b = el('button', { type: 'button', className: 'dd-att' }); + b.innerHTML = `${ddIcon(icon)}${v}${label}`; + b.onclick = fn; + ab.append(b); + } + + DoctorDash.details(rows); + }, + + // Инфографика: по каждому разделу — мягкий «холм», высота = сколько врачей его заполнили. + // Холмы соседних разделов перекрываются полупрозрачными слоями; шкала — от 0 до числа врачей. + metrics: [ + ['Основные сведения', ({ d }) => !!(d.last_name && d.first_name && d.position && d.specialties)], + ['Фото', ({ d }) => !!d.photo], + ['Стаж', ({ d }) => d.practice_start_year != null], + ['Онлайн-запись', ({ d }) => d.booking_provider === 'medflex'], + ['Категория', ({ d }) => !!d.category], + ['«О враче»', ({ d }) => !!d.bio], + ], + // холодный голубой, персиковый, пудровый, светло-лавандовый + hills: ['#9cc0ec', '#f4c09a', '#eeb0bd', '#c3b8ee'], + + chart(rows) { + const box = $('#dd-chart'); box.textContent = ''; + const n = rows.length; + $('#dd-total').textContent = `${n} ${plural(n, 'врач', 'врача', 'врачей')}`; + const data = DoctorDash.metrics.map(([name, f]) => ({ name, v: rows.filter(f).length })); + const W = Math.max(box.clientWidth, 340), H = 290, L = 8, R = 40, T = 18, B = 46; + const max = Math.max(n, 1); + // поля по краям, чтобы крайние холмы плавно уходили в ноль, а не обрывались + const step = (W - L - R) / (data.length + 1.9); + const cx = (i) => L + step * (i + 1.45); + const y = (v) => T + (H - T - B) * (1 - v / max); + const sigma = step * 0.55; + const hill = (i, v) => { + const pts = []; + for (let k = -2.6; k <= 2.601; k += 0.1) { + const xx = cx(i) + k * sigma; + if (xx < L || xx > W - R) continue; + pts.push(`${xx.toFixed(1)} ${y(v * Math.exp(-(k * k) / 2)).toFixed(1)}`); + } + const x0 = Math.max(L, cx(i) - 2.6 * sigma), x1 = Math.min(W - R, cx(i) + 2.6 * sigma); + return { area: `M${x0.toFixed(1)} ${y(0)} L${pts.join(' L')} L${x1.toFixed(1)} ${y(0)} Z`, line: `M${pts.join(' L')}` }; + }; + const ticks = [0, 0.25, 0.5, 0.75, 1].map((t) => Math.round(max * t)); + let grid = ''; + for (const t of ticks) grid += `${t}`; + // высокие холмы — дальше, низкие — ближе, чтобы было видно все + const order = data.map((d, i) => i).sort((a, b) => data[b].v - data[a].v); + const defs = DoctorDash.hills.map((c, i) => ` + `).join(''); + const hills = order.map((i) => { + const h = hill(i, data[i].v), c = i % DoctorDash.hills.length; + return ``; + }).join(''); + const labels = data.map((d, i) => `${d.name} + ${d.v}`).join(''); + box.innerHTML = ` + ${defs}${grid}${hills}${labels}`; + const svg = box.querySelector('svg'), tip = box.querySelector('.dd-tip'), guide = svg.querySelector('.dd-guide'); + const show = (i) => { + svg.querySelectorAll('.dd-hill').forEach((g) => g.classList.toggle('on', Number(g.dataset.i) === i)); + guide.setAttribute('x1', cx(i)); guide.setAttribute('x2', cx(i)); guide.hidden = false; + const r = svg.getBoundingClientRect(); + tip.hidden = false; + tip.innerHTML = `${data[i].v} из ${n}${data[i].name} · ${n ? Math.round((data[i].v / n) * 100) : 0}%`; + tip.style.left = (cx(i) / W) * r.width + 'px'; + tip.style.top = (y(data[i].v) / H) * r.height + 'px'; + }; + svg.addEventListener('mousemove', (e) => { + const r = svg.getBoundingClientRect(); + const px = ((e.clientX - r.left) / r.width) * W; + show(Math.max(0, Math.min(data.length - 1, Math.round((px - L) / step - 1.45)))); + }); + svg.addEventListener('mouseleave', () => { + tip.hidden = true; guide.hidden = true; + svg.querySelectorAll('.dd-hill').forEach((g) => g.classList.remove('on')); + }); + }, + + details(rows) { + const n = rows.length, cnt = (f) => rows.filter(f).length; + const sum = (k) => rows.reduce((a, r) => a + (r.q[k] || 0), 0); + const pending = sum('reviews_pending'), live = sum('reviews_published'); + const withReviews = cnt(({ q }) => q.reviews_pending + q.reviews_published > 0); + const rs = sum('rating_sum'), rc = sum('rating_count'); + const leads = sum('leads'), Q = DoctorDash.q; + const extra = cnt(({ d, q }) => d.quote || q.kids_from || q.languages || d.show_on_home || d.absent); + const list = [ + ['«О враче»', cnt(({ d }) => !!d.bio), (v) => `заполнено у ${v}`, 'no-bio'], + ['Образование', cnt(({ q }) => q.education > 0), (v) => `заполнено у ${v}`], + ['Награды', cnt(({ q }) => q.awards > 0), (v) => `заполнено у ${v}`], + ['Конференции', cnt(({ q }) => q.conferences > 0), (v) => `заполнено у ${v}`], + ['Онлайн-запись Medflex', cnt(({ d }) => d.booking_provider === 'medflex'), (v) => `подключена у ${v}`, 'no-medflex'], + ['Услуги врача', cnt(({ d }) => d.service_ids.length > 0), (v) => v ? `указаны у ${v}` : 'не указаны', 'no-services'], + ['Филиалы приёма', cnt(({ d }) => d.branch_ids.length > 0), (v) => v ? `указаны у ${v}` : 'не заполнены', 'no-branches'], + ['Отзывы', withReviews, () => (pending + live + ? `${pending} на проверке у ${cnt(({ q }) => q.reviews_pending > 0)} ${plural(cnt(({ q }) => q.reviews_pending > 0), 'врача', 'врачей', 'врачей')}` + + `, ${live ? 'опубликовано ' + live : 'опубликованных нет'}` + (rc ? `, средняя оценка ${(rs / rc).toFixed(2).replace('.', ',')}` : '') + : 'отзывов пока нет'), 'reviews'], + ['Заявки к врачу', cnt(({ q }) => q.leads > 0), () => `пока ${leads} из ${Q.leads_total} ${plural(Q.leads_total, 'заявки', 'заявок', 'заявок')}`], + ['Дополнительно: цитата, возраст детей, языки, «на главной», «не принимает»', extra, (v) => v ? `заполнено у ${v}` : 'пока не заполнены'], + ]; + const box = $('#dd-details'); box.textContent = ''; + for (const [label, v, text, target] of list) { + const p = n ? Math.round((v / n) * 100) : 0; + const [st, cls] = !v ? ['пусто', 'empty'] : p >= 90 ? ['заполнено', 'ok'] : ['требует внимания', 'warn']; + const row = el(target ? 'button' : 'div', { className: 'dd-row ' + cls }); + if (target) { row.type = 'button'; row.onclick = () => (target === 'reviews' ? go('reviews') : DoctorDash.toList(target)); } + row.innerHTML = ` + ${p}%${st}`; + row.querySelector('.dd-row__label').textContent = label; + row.querySelector('.dd-row__text').textContent = text(v); + box.append(row); + } + }, + + toList(filter) { + go('doctors'); + Doctors.setFilter(filter || ''); + }, + + // отчёт по каждому врачу: что заполнено, что нет + exportCSV() { + const yes = (b) => (b ? 'да' : 'нет'); + const rows = [['ФИО', 'Специальность', 'Фото', 'Стаж', 'Категория', '«О враче»', 'Образование', 'Награды', 'Конференции', + 'Онлайн-запись', 'Услуг', 'Филиалов', 'Отзывов на проверке', 'Отзывов опубликовано', 'Заявок']]; + for (const { d, q } of DoctorDash.rows()) { + rows.push([fullName(d), d.specialties, yes(d.photo), d.practice_start_year ? new Date().getFullYear() - d.practice_start_year + ' лет' : 'нет', + d.category || 'нет', yes(d.bio), q.education || 0, q.awards || 0, q.conferences || 0, d.booking_provider === 'medflex' ? 'Medflex' : 'заявка', + d.service_ids.length, d.branch_ids.length, q.reviews_pending || 0, q.reviews_published || 0, q.leads || 0]); + } + const esc = (v) => { const x = String(v ?? ''); return /[";\n]/.test(x) ? '"' + x.replace(/"/g, '""') + '"' : x; }; + const a = el('a', { href: URL.createObjectURL(new Blob(['' + rows.map((r) => r.map(esc).join(';')).join('\r\n')], { type: 'text/csv;charset=utf-8' })), + download: `kartochki-vrachey-${new Date().toISOString().slice(0, 10)}.csv` }); + document.body.append(a); a.click(); a.remove(); + setTimeout(() => URL.revokeObjectURL(a.href), 1000); + toast('Отчёт выгружен: ' + (rows.length - 1) + ' ' + plural(rows.length - 1, 'врач', 'врача', 'врачей')); + }, +}; + +$('#dd-spec').onchange = () => DoctorDash.render(); +window.addEventListener('resize', debounce(() => { if (DoctorDash.q && !$('#tab-doctors-dash').hidden) DoctorDash.chart(DoctorDash.rows()); }, 150)); +{ + const acts = [['users', 'Все врачи', () => DoctorDash.toList('')], ['check', 'Проверить карточки', () => DoctorDash.toList('incomplete')], + ['chat', 'Отзывы', () => go('reviews')], ['calendar', 'Онлайн-запись', () => DoctorDash.toList('no-medflex')], + ['download', 'Экспорт отчёта', () => DoctorDash.exportCSV()]]; + const bar = $('#dd-actions'); + for (const [icon, label, fn] of acts) { + const b = el('button', { type: 'button', className: 'dd-act' }); + b.innerHTML = ddIcon(icon) + `${label}`; + b.onclick = fn; + bar.append(b); + } +} diff --git a/internal/api/adminui/doctors.js b/internal/api/adminui/doctors.js new file mode 100644 index 0000000..d4575b9 --- /dev/null +++ b/internal/api/adminui/doctors.js @@ -0,0 +1,366 @@ +/* Раздел «Врачи»: таблица с сортировкой и фильтрами, быстрые действия и сводка справа. + Карточка врача и «Услуги и филиалы» — прежние диалоги editDoctor / editDoctorLinks. */ + +const DR_ICONS = { + building: '', + pencil: '', + dots: '', + plus: '', + download: '', + list: '', + people: '', + info: '', + close: '', + sort: '', + search: '', +}; +const drIcon = (n) => ``; + +let adminDoctors = []; +let specialtiesCache = []; + +const TRANSLIT = { а: 'a', б: 'b', в: 'v', г: 'g', д: 'd', е: 'e', ё: 'e', ж: 'zh', з: 'z', и: 'i', й: 'y', к: 'k', л: 'l', м: 'm', + н: 'n', о: 'o', п: 'p', р: 'r', с: 's', т: 't', у: 'u', ф: 'f', х: 'kh', ц: 'ts', ч: 'ch', ш: 'sh', щ: 'shch', ъ: '', ы: 'y', + ь: '', э: 'e', ю: 'yu', я: 'ya' }; +const toSlug = (s) => s.toLowerCase().split('').map((c) => TRANSLIT[c] ?? c).join('') + .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 100); + +const Doctors = { + sort: { key: '', dir: 1 }, + noteKey: 'dana-admin:dr-note-hidden', + + status(d) { + if (!d.published) return ['скрыт', 'off']; + if (d.absent) return ['не принимает', 'wait']; + return ['на сайте', '']; + }, + + filtered() { + const q = $('#dr-q').value.trim().toLowerCase(); + const f = $('#dr-filter').value; + let list = adminDoctors.filter((d) => + (!q || (fullName(d) + ' ' + d.position + ' ' + (d.specialties || '')).toLowerCase().includes(q)) && + (f !== 'published' || d.published) && (f !== 'hidden' || !d.published) && (f !== 'absent' || d.absent) && + (f !== 'no-services' || !d.service_ids.length) && (f !== 'no-branches' || !d.branch_ids.length) && + (f !== 'no-photo' || !d.photo) && + (f !== 'no-experience' || d.practice_start_year == null) && (f !== 'no-category' || !d.category) && + (f !== 'no-bio' || !d.bio) && (f !== 'no-medflex' || d.booking_provider !== 'medflex') && + (f !== 'incomplete' || !d.photo || d.practice_start_year == null || (!d.bio && !d.bio_count)) && + (!f.startsWith('sp:') || String(d.specialty_id) === f.slice(3))); + const { key, dir } = Doctors.sort; + if (key === 'name') list = list.slice().sort((a, b) => dir * fullName(a).localeCompare(fullName(b), 'ru')); + if (key === 'services') list = list.slice().sort((a, b) => dir * (a.service_ids.length - b.service_ids.length)); + return list; + }, + + render() { + const list = Doctors.filtered(); + $('#dr-count').textContent = list.length + ' ' + plural(list.length, 'врач', 'врача', 'врачей'); + const tb = $('#dr-body'); tb.textContent = ''; + if (!list.length) { + const tr = el('tr'); tr.append(el('td', { colSpan: 6, className: 'muted dr-empty', textContent: 'Никого не нашлось — измените поиск или фильтр.' })); + tb.append(tr); + } + for (const d of list) tb.append(Doctors.row(d)); + for (const th of document.querySelectorAll('#tab-doctors th[data-sort]')) { + const on = th.dataset.sort === Doctors.sort.key; + th.setAttribute('aria-sort', on ? (Doctors.sort.dir > 0 ? 'ascending' : 'descending') : 'none'); + } + Doctors.stats(); + }, + + row(d) { + const tr = el('tr'); + const who = el('td'); + const box = el('div', { className: 'dr-who' }); + box.append(d.photo ? el('img', { className: 'dr-photo', src: d.photo, alt: '', loading: 'lazy' }) + : el('span', { className: 'dr-photo dr-photo--empty', textContent: (d.last_name[0] || '') + (d.first_name[0] || '') })); + const txt = el('div'); + const name = el('button', { type: 'button', className: 'dr-name', textContent: fullName(d) }); + name.onclick = () => editDoctor(d); + txt.append(name, el('div', { className: 'dr-pos', textContent: d.position || 'должность не указана' })); + box.append(txt); who.append(box); + + const spec = el('td'); + spec.append(el('span', { className: 'chip', textContent: d.specialties || '—' })); + + const svc = el('td'); + svc.append(d.service_ids.length ? el('span', { className: 'dr-num', textContent: d.service_ids.length }) + : el('span', { className: 'pill warn-pill', textContent: 'нет' })); + + const br = el('td'); + const branches = d.branch_ids.map((id) => branchList.find((b) => b.id === id)).filter(Boolean).map((b) => shortAddress(b.address)); + if (branches.length) br.append(el('span', { className: 'dr-branches', textContent: branches.join('; ') })); + else br.append(el('span', { className: 'pill warn-pill', textContent: 'не указан' })); + + const st = el('td'); + const [label, cls] = Doctors.status(d); + st.append(el('span', { className: 'pill dot ' + cls, textContent: label })); + + const act = el('td', { className: 'dr-actions' }); + const links = el('button', { type: 'button', className: 'soft-btn' }); + links.innerHTML = drIcon('building') + 'Услуги и филиалы'; + links.onclick = () => editDoctorLinks(d); + const edit = el('button', { type: 'button', className: 'icon-btn', title: 'Изменить карточку' }); + edit.setAttribute('aria-label', 'Изменить: ' + fullName(d)); + edit.innerHTML = drIcon('pencil'); + edit.onclick = () => editDoctor(d); + const more = el('button', { type: 'button', className: 'icon-btn', title: 'Ещё' }); + more.setAttribute('aria-label', 'Ещё действия: ' + fullName(d)); + more.setAttribute('aria-haspopup', 'menu'); + more.innerHTML = drIcon('dots'); + more.onclick = (e) => { e.stopPropagation(); Doctors.menu(d, more); }; + act.append(links, edit, more); + + tr.append(who, spec, svc, br, st, act); + return tr; + }, + + // Меню «…»: открыть на сайте, скрыть/показать, «не принимает». + menu(d, anchor) { + Doctors.closeMenu(); + const m = el('div', { className: 'menu', id: 'dr-menu', role: 'menu' }); + const item = (text, fn, danger) => { + const b = el('button', { type: 'button', role: 'menuitem', className: danger ? 'danger' : '', textContent: text }); + b.onclick = () => { Doctors.closeMenu(); fn(); }; + m.append(b); + }; + if (d.published && d.specialty_slug) { + item('Открыть страницу на сайте', () => window.open(`/company/staff/${d.specialty_slug}/${d.slug}/`, '_blank', 'noopener')); + } + item(d.absent ? 'Снова принимает' : 'Временно не принимает', () => Doctors.patch(d, { absent: !d.absent }, + d.absent ? 'Кнопка записи снова видна' : 'Кнопка записи скрыта')); + item(d.published ? 'Скрыть с сайта' : 'Показать на сайте', () => Doctors.patch(d, { published: !d.published }, + d.published ? 'Врач скрыт с сайта' : 'Врач на сайте'), d.published); + document.body.append(m); + const r = anchor.getBoundingClientRect(); + m.style.top = (r.bottom + window.scrollY + 6) + 'px'; + m.style.left = Math.max(12, r.right + window.scrollX - m.offsetWidth) + 'px'; + m.querySelector('button').focus(); + anchor.setAttribute('aria-expanded', 'true'); + Doctors.menuAnchor = anchor; + }, + closeMenu() { + $('#dr-menu')?.remove(); + Doctors.menuAnchor?.setAttribute('aria-expanded', 'false'); + Doctors.menuAnchor = null; + }, + + async patch(d, change, message) { + const body = { + slug: d.slug, last_name: d.last_name, first_name: d.first_name, middle_name: d.middle_name, + position: d.position, category: d.category, degree: d.degree, practice_start_year: d.practice_start_year, rating: d.rating, + photo_id: d.photo_id, quote: d.quote, bio: d.bio, booking_provider: d.booking_provider, medflex_user: d.medflex_user, + sort: d.sort, published: d.published, absent: d.absent, show_on_home: d.show_on_home, + seo_title: d.seo_title, seo_description: d.seo_description, seo_h1: d.seo_h1, ...change, + }; + try { + await api('/admin/doctors/' + d.id, { method: 'PUT', body: JSON.stringify(body) }); + toast(message); + await loadAdminDoctors(); + } catch (e) { toast(e.message, true); } + }, + + stats() { + const all = adminDoctors; + const n = (f) => all.filter(f).length; + $('#dr-total').textContent = all.length; + const cells = [ + ['published', 'На сайте', n((d) => d.published), 'ok'], + ['no-services', 'Нет услуг', n((d) => !d.service_ids.length), 'warn'], + ['no-branches', 'Нет филиалов', n((d) => !d.branch_ids.length), 'rose'], + ]; + const box = $('#dr-stats'); box.textContent = ''; + for (const [key, label, value, tone] of cells) { + const b = el('button', { type: 'button', className: 'mini-stat ' + tone }); + b.setAttribute('aria-pressed', String($('#dr-filter').value === key)); + b.innerHTML = `${label}${value}`; + b.onclick = () => Doctors.setFilter($('#dr-filter').value === key ? '' : key); + box.append(b); + } + }, + + setFilter(value) { + $('#dr-filter').value = value; + Doctors.render(); + }, + + fillFilter() { + const sel = $('#dr-filter'); + const keep = sel.value; + sel.textContent = ''; + const base = [['', 'Все врачи'], ['published', 'На сайте'], ['hidden', 'Скрытые'], ['absent', 'Не принимают'], + ['no-services', 'Без услуг'], ['no-branches', 'Без филиала'], ['no-photo', 'Без фото'], + ['incomplete', 'Неполные карточки'], ['no-experience', 'Без стажа'], ['no-category', 'Без категории'], + ['no-bio', 'Без «О враче»'], ['no-medflex', 'Без онлайн-записи']]; + for (const [v, t] of base) sel.append(el('option', { value: v, textContent: t })); + const grp = el('optgroup', { label: 'Специальность' }); + for (const s of specialtiesCache.filter((s) => s.doctors)) grp.append(el('option', { value: 'sp:' + s.id, textContent: `${s.name_plural} (${s.doctors})` })); + sel.append(grp); + sel.value = keep; + if (sel.value !== keep) sel.value = ''; + }, + + add() { + if (!specialtiesCache.length) { toast('Сначала добавьте специальность', true); return; } + openDialog('Новый врач', [ + { key: 'last_name', label: 'Фамилия', value: '' }, + { key: 'first_name', label: 'Имя', value: '' }, + { key: 'middle_name', label: 'Отчество', value: '' }, + { key: 'position', label: 'Должность, как на сайте (например, «Акушер-гинеколог, врач УЗИ»)', value: '' }, + { key: 'specialty_id', label: 'Специальность — раздел в адресе страницы, потом не меняется', type: 'select', value: '', + options: [{ value: '', label: '— выберите —' }].concat(specialtiesCache.map((s) => ({ value: s.id, label: s.name_plural }))) }, + { key: 'slug', label: 'Адрес латиницей — заполнится сам из ФИО', value: '' }, + ], async (data) => { + if (!data.slug.trim()) data.slug = toSlug([data.last_name, data.first_name, data.middle_name].join(' ')); + data.specialty_id = Number(data.specialty_id) || 0; + const r = await api('/admin/doctors', { method: 'POST', body: JSON.stringify(data) }); + toast('Врач добавлен скрытым — заполните карточку и включите «Показывать на сайте»'); + await loadAdminDoctors(); + const d = adminDoctors.find((x) => x.id === r.data.id); + if (d) setTimeout(() => editDoctor(d), 50); + }); + // адрес подставляется из ФИО, пока его не правили руками + const inputs = [...$('#dlg-body').querySelectorAll('input')]; + const slug = inputs[inputs.length - 1]; + let touched = false; + slug.addEventListener('input', () => { touched = true; }); + for (const i of inputs.slice(0, 3)) i.addEventListener('input', () => { + if (!touched) slug.value = toSlug(inputs.slice(0, 3).map((x) => x.value).join(' ')); + }); + }, + + // Список врачей в CSV для Excel: открытые данные с сайта, без персональных данных пациентов. + exportCSV() { + const list = Doctors.filtered(); + const rows = [['ФИО', 'Должность', 'Специальность', 'Услуг', 'Филиалы', 'Статус', 'Адрес на сайте']]; + for (const d of list) { + const branches = d.branch_ids.map((id) => branchList.find((b) => b.id === id)).filter(Boolean).map((b) => shortAddress(b.address)); + rows.push([fullName(d), d.position, d.specialties, d.service_ids.length, branches.join('; '), Doctors.status(d)[0], + d.specialty_slug ? `${location.origin}/company/staff/${d.specialty_slug}/${d.slug}/` : '']); + } + const esc = (v) => { const s = String(v ?? ''); return /[";\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; }; + const csv = '' + rows.map((r) => r.map(esc).join(';')).join('\r\n'); + const a = el('a', { href: URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8' })), + download: `vrachi-${new Date().toISOString().slice(0, 10)}.csv` }); + document.body.append(a); a.click(); a.remove(); + setTimeout(() => URL.revokeObjectURL(a.href), 1000); + toast('Выгружено: ' + list.length + ' ' + plural(list.length, 'врач', 'врача', 'врачей')); + }, +}; + +async function loadSpecialties() { + const r = await api('/admin/specialties'); + specialtiesCache = r.data || []; + Doctors.fillFilter(); +} +async function loadAdminDoctors() { + const [r] = await Promise.all([api('/admin/doctors'), loadSpecialties()]); + adminDoctors = r.data || []; + Doctors.render(); +} +// старое имя: на него ссылаются подсказки колокольчика +const renderDoctors = () => Doctors.render(); + +/* ---------- справочник специальностей ---------- */ +const Specialties = { + open() { + const dlg = $('#sp-dlg'); + Specialties.render(); + dlg.showModal(); + }, + render() { + const box = $('#sp-list'); box.textContent = ''; + for (const s of specialtiesCache) { + const row = el('div', { className: 'sp-row' }); + const txt = el('div'); + txt.append(el('b', { textContent: s.name_plural }), + el('small', { textContent: `/company/staff/${s.slug}/ · ${s.doctors} ${plural(s.doctors, 'врач', 'врача', 'врачей')}` })); + const edit = el('button', { type: 'button', className: 'icon-btn', title: 'Переименовать' }); + edit.setAttribute('aria-label', 'Переименовать: ' + s.name_plural); + edit.innerHTML = drIcon('pencil'); + edit.onclick = () => Specialties.edit(s); + const del = el('button', { type: 'button', className: 'btn danger', textContent: 'Удалить', disabled: s.doctors > 0, + title: s.doctors ? 'Сначала перенесите врачей в другую специальность' : '' }); + del.onclick = () => Specialties.remove(s); + row.append(txt, edit, del); + box.append(row); + } + }, + form(s, onSave) { + $('#sp-dlg').close(); + const fields = [ + { key: 'name_plural', label: 'Название раздела на сайте (например, «Кардиологи»)', value: s?.name_plural || '' }, + { key: 'name', label: 'В единственном числе или так же (необязательно)', value: s?.name || '' }, + { key: 'sort', label: 'Порядок в списке разделов (меньше — выше)', type: 'number', value: s?.sort ?? 500 }, + ]; + if (!s) fields.push({ key: 'slug', label: 'Адрес латиницей — заполнится сам, потом не меняется', value: '' }); + openDialog(s ? 'Специальность: ' + s.name_plural : 'Новая специальность', fields, async (d) => { + d.sort = Number(d.sort) || 500; + if (!d.name.trim()) d.name = d.name_plural; + if (!s && !d.slug.trim()) d.slug = toSlug(d.name_plural); + await onSave(d); + await loadSpecialties(); + setTimeout(Specialties.open, 50); + }); + $('#dlg').addEventListener('close', function back() { + $('#dlg').removeEventListener('close', back); + if ($('#dlg').returnValue === 'cancel') setTimeout(Specialties.open, 50); + }); + }, + add() { + Specialties.form(null, async (d) => { + await api('/admin/specialties', { method: 'POST', body: JSON.stringify(d) }); + toast('Специальность добавлена'); + }); + }, + edit(s) { + Specialties.form(s, async (d) => { + await api('/admin/specialties/' + s.id, { method: 'PUT', body: JSON.stringify(d) }); + toast('Сохранено'); + loadAdminDoctors(); + }); + }, + async remove(s) { + if (!confirm(`Удалить специальность «${s.name_plural}»?`)) return; + try { + await api('/admin/specialties/' + s.id, { method: 'DELETE' }); + toast('Специальность удалена'); + await loadSpecialties(); + Specialties.render(); + } catch (e) { toast(e.message, true); } + }, +}; + +/* ---------- обработчики ---------- */ +$('#dr-q').addEventListener('input', debounce(() => Doctors.render(), 200)); +$('#dr-filter').onchange = () => Doctors.render(); +for (const th of document.querySelectorAll('#tab-doctors th[data-sort] button')) { + th.insertAdjacentHTML('beforeend', drIcon('sort')); + th.onclick = () => { + const key = th.parentElement.dataset.sort; + Doctors.sort = Doctors.sort.key === key ? { key, dir: -Doctors.sort.dir } : { key, dir: key === 'services' ? -1 : 1 }; + Doctors.render(); + }; +} +$('#dr-add').insertAdjacentHTML('afterbegin', drIcon('plus')); +$('#dr-export').insertAdjacentHTML('afterbegin', drIcon('download')); +$('#dr-spec').insertAdjacentHTML('afterbegin', drIcon('list')); +$('#dr-total-icon').innerHTML = drIcon('people'); +$('#dr-note-icon').innerHTML = drIcon('info'); +$('#dr-note-close').innerHTML = drIcon('close'); +$('#dr-search-icon').innerHTML = drIcon('search'); +$('#dr-add').onclick = () => Doctors.add(); +$('#dr-export').onclick = () => Doctors.exportCSV(); +$('#dr-spec').onclick = () => Specialties.open(); +$('#sp-add').onclick = () => Specialties.add(); +$('#dr-note-close').onclick = () => { + $('#dr-note').hidden = true; + try { localStorage.setItem(Doctors.noteKey, '1'); } catch { /* приватный режим */ } +}; +try { if (localStorage.getItem(Doctors.noteKey)) $('#dr-note').hidden = true; } catch { /* приватный режим */ } +document.addEventListener('click', (e) => { if (!e.target.closest('#dr-menu')) Doctors.closeMenu(); }); +document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && $('#dr-menu')) { const a = Doctors.menuAnchor; Doctors.closeMenu(); a?.focus(); } +}); +window.addEventListener('hashchange', () => Doctors.closeMenu()); diff --git a/internal/api/adminui/fonts/montserrat-alternates-cyrillic-300-normal.woff2 b/internal/api/adminui/fonts/montserrat-alternates-cyrillic-300-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fdc79da4f2c01d0d255cc8711916fdc3e9c84970 GIT binary patch literal 12948 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 Kjs{sA0000}y85&L literal 0 HcmV?d00001 diff --git a/internal/api/adminui/fonts/montserrat-alternates-cyrillic-400-normal.woff2 b/internal/api/adminui/fonts/montserrat-alternates-cyrillic-400-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8bf7241a301e48010107d8c885af05e90093f3f3 GIT binary patch literal 13200 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 C(@h-! literal 0 HcmV?d00001 diff --git a/internal/api/adminui/fonts/montserrat-alternates-cyrillic-500-normal.woff2 b/internal/api/adminui/fonts/montserrat-alternates-cyrillic-500-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2b14c05367c49ced6349e4fa9764c2ffed4010e5 GIT binary patch literal 13368 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 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/fonts/montserrat-alternates-latin-400-normal.woff2 b/internal/api/adminui/fonts/montserrat-alternates-latin-400-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f783323a7ae01e7007fba1672963d6b225d023fc GIT binary patch literal 21916 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 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/fonts/montserrat-alternates-latin-500-normal.woff2 b/internal/api/adminui/fonts/montserrat-alternates-latin-500-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..49d7595e0be4afa2ba7c3ad565d54e045b1ca889 GIT binary patch literal 21824 zcmV)5K*_&%Pew8T0RR91098N$5dZ)H0NDfp094!n0RR9100000000000000000000 z0000QffyV7b{vja24Db%GzfuY37TyY2nvDQP=UWf3xs$80X7081BqS)AO(ah2aaV7 zfmIt)#|M;cyNFP32VnkO{a!E%uABb&~hCWR{y!x`@T;weW99t!Npgq252Rlq{ zz7#wQGWVt{L!-oFN#PITX{A{(4LXs@H?+&5r=1HDc^DSG?@V*4=it&hF3OslpC>#6 z+eZE*Coj?S{{4T|YoGV-7c`2<3}#A2LdZx?$05UUIuUsH5zX1m?rGdOVnE~)5>zV+ zg(_bvO3j3d8?>(?yukp!%^qDC+q}Wdfr^EQfD#fB?-4D?qZ}|11ME3ui|^RyM~a}awmZedaiX|&r;#}|36~C&$-XSR+?Bq;Y5KI=B-2%oYhW!o3dO4Mn?lQcg8r(vYjL zPI~>rwQOP&U1D~N1w}?xxV{E*&qu%kC#t%s36PN7_nL(3hMNLt1(p5K$`+Pd9eu1h=r4pBATasnDZMi+}<{|O`YGG)>M@-R0 z7hMqEZV+vBK>RKu%w(m%U-HG|kj&-FZBwz6ijoR{Iz8|Yc6+zouCDHs z*9zdDAh-Q7c-X83ORxkAY!-9X_@CR|3@DvSsQUU8wU3z1VH*tYE%d+F`F5}1XXYF! zP;}|ieQer<-_tWBkrREx*rDAJs$9EYB$gdu`2S6{xAHmf&JZFfZQyL=I@8?1? ziR&0c6>{mKTvxyJaapC*yRUy)n&-b?meT!wlw0X3+eRuQmkDWSCbj2t%wAZ zwMZ~Wrv&YYKtquO8W9aBze@k4QwU7c%P4xpQ9bH`dOdgA1Sq~8He(W`V*tHV1WPbK zCt~md=#u$W0D}WGS7I_=` z9aJ)0cd@ch!#---`!|JVTdWiI3Slo2_8d_>O`Teh4W1oL{!T(7^*UVtzL46i;D2iI9-XvdQ6O6+sY=!kO zC`S9DABpa2(}*URbwxlgMpO6Da+C=GP}m|ZIp#)b6|YeFhXIw>##VM+ETU9|BY^+p z_xwokHGC@Hr|C~~KR5GcuC?KsxYn9^IcH%6Vo-j7QdjNte7-NlLa?%?<||1}tLYdJ zhrlja)m5YKs*M=54d1R(A}lr`OQPLyQonGHb`<(L%#KJv-jpsXB!k?WhXYCJyy@fGaY$E}U28U7z%|zC&`q>G z>V3H`J%D|2IoTkJgH=2sIbz_Yj!;E2+8}gr-p7tD&*gAx`ijd9mg)d-wu+nAH|VpX zQ`6n(^wLCg;r!09y_BHa3(J@&QY5y0Q*hgyr(HVVU?(O^%~jZ!uQ6kk-9nXO=Y`!` zB_<}cOo(d%?5|9#g)*SnJB%utzkdM5pmXT}0MOCIy3EaJ|N8cU<)dBI8n2e68{R6y zQ>T-S^rA2L?*EEqiKC@+QRK|pZ3(J#x1F5j`UidmnRxhCN>Q;uNB#v~8BU~#M@Q@whY<0Dp)lqZl=;{_~)CMEb9$h#S%@_r$ z$5s^<5g&?RIW|>!%3^RX8W3KMu_0W4OQ9*eYC13{FqrQlVGB$Ug)&nsv_-lVdEfi| z5R(|Zbth#>fvk3rd1?o_*Aa7rr9c*b-jFRp0+EWe55T~gpSPcqS}J{G6d>X#0~MoX z)a#J3V<&zSPG`Fw9|YnCSd5~;DjE(p3LzdAB9$%QFubhsbomUqAm#N8((rM4vUHG` zz`5Buv;gZshM`Bt$mG>2f^R>$D@uZMoJ&OoUHNK6fEf|nPBkg-{bDq%iP$-&tsD;M2780n zq)VpVu5Ob*xLZ%)T&;gjexiEpdPnLk4V`SLfF$j~euzHPuH??vfV2|=m_WCa08ke9 zYK8F-pt-OnA->pR?VM{g>jt^dZMThev}1IttdgnJp3nK;0}1*-4BN*H0H-|>>kk4> z>`CD@0hS`!u%pqaT%95Rk)%~-MJQZ#2l&9F1W$N^&QqSH_ky>Xed=?mzVs!(?|diV zKlmSlNhBd1k;G6iQ&K`IDt2lbl$MU2o|)yQnOQZ;2KH_@BhQzVNa17W7e+-y*~w&7 zOpIMc5;~da%Sgz<%GEe|8Y{mquRtNCB2BBfuThCAaMgY1>kI@}yAilLc3B3gmZA|P z%P>K3O_P>oyaqQx%QCTVwkcxL&D2uO($dZDn`Vv<>E>$)i~26GSP#J!P4K>^_o1fq zgJ$*1bm|ZI6DWe>Yp&znKws*nTNG|EDYf=hx$gn3hc)UEP3KXmM+;Dg zhMv}lXEedHeMK*LQ5GV7M-#rQN#E0i?`zTzH0_6)??+nT$C~daTHvRe_WQn(Klmf* zPqm0704Z#Kd%^BN7rY9wfbM2TTbS_tj%6{XT$BO-x9_Oa3XQ5+&Jz-BDQecib$Y4U zTT8y!t_jxIJ5QmmQf)u&lnN!se%5KRBt!%iB1w=Gf2lMv@?xo2ni%cOa884;x$*4! zP^e8GF6$$;;iFaiSOq>&n?6}1pRUAHaq&)sw0?Yy^$iSDqN|#xz8s=)eNy|D?}$(o ziiG-dgM zH_(k;4P7tzhZAQ1Z5FT>Lim-x`lo;UpZ}v;Rxac}M(p1)0^g-QM$}{2%E#~o63&U> zY(nv#rRyQHWe(jz&D^cfz0A0rM$Vs{dXDFs@meFC8?WLUGj@-{G}8WLzEz@9$LdUr&3wLo$h%8pg8TP_D>G+@;3Z;Jze@ptr&LJ2FVd`V9G= zlIcprgdC60Zey8DmPVDBC717vP>w`|Fn?zL9d2;WkDR?z^k~K*c;;UlItyKZ&Qi;C zXmjdP!@zI!!p%&#C+(-R+rx3a8|{ZI|L!tDszHclaB-QJdvrH6uJ9zal`W%E`i7Dg zSj4hnh3co#Bp-T9sGukvtQ*JAK(N$lOhQ{mT?`&&m#hjSj#v zrg%UaHfL+khy~i!YS>PEGzpJQ6Q1!x)>hg*sfM9rE|k>UB~HBkPgW2!mRyy+K) z#QkMse{rbHhI&-bXOCtJo||26Ir>~p9~byL@o0wpOrVPxX1gUjmvBnlrL>xbtxB(y z9F-4je#s)aGtO3e zCsjlDhn`*)m_>79-tlZ&OsdsJ9(||MkDdTZ)_HY@8k zc`e#GaX3Z=0Zkf@h*gvBZg=p=?$%3mKZ-E;YvH|`nER;Ac;POvo*rQ~<>Su5*d$Mn zN98N`J=>+BN8TQB60LWH{2lN1o?hbDe(S_vNPFBrv?u&aJ~;~5>xoqb*KMdYxd`O8 zr_0!rlMR=m+`$N256zW(qO}vvr zK`OnY*i^Sd)WV^tNNGgNKrXe|=e341J)Vh?nK2eN$&w{YmaLYwMU`zqW%;mlJ}Tnl z&$AYA)cnFX_3k4WV6GNd?Y7Pe)ASq5>&nKE$?vC0pl6XOR}CWzhoz?IKl2f$UIlW5FEwEvvTc-A9N(I{u6)Z{L zFqHBW%4||wYcBhnnIN6vUlew|8CF}^4_cMIU+{_H8xSv%CF8>aa?s6Zw0bRxA{7X& z=)&Z}OWucA{ICq^>(%fC(h^s)n6*IL=*@LKW!rY9%?hmojZe0I*p2|!y;-b~Ad1W? zs@QjFL7@AAV6e1VPOnklN7uAu&y|K{Y&nnR-&LuNVLZj!gm+}cv}9GB)oEEyP_#P? z`xmRD&g1MXM9{r7%98#O(Y7Ng;zK?SlcS}Y)Y+zSnm9yXitIS#9tpLPIXFJ=oDGt( zWE=^Pz(T>oVo1e+#el^?#)QDM2oq&L{`#^|S(aCZ6=k=wY*tmS)n&0JS#Gvkhw@wh z`7}$JD8NgV*z8c8ikgOwmpdL~Sx$0X0+S_~0y3#+g=vMUr1Npcr+ahIC!tI}joX42 z5D-Y7%^amR#xZM>nvn4vy-vnqKa0j}Q%5&YVim&U)- zCl<%tQcJ?ka4psIyRS_4wDNtCxJG(qT0+HaSR@-$T{)J_sd{RaLzqD6V->NIS3kAy z7^!o?i+c%;7iEwQGFUGj(MM@p`~sJ8!d2*A4^`rvHf8#hK7sC10SGOwhw7D2Ne-D8>nRh&c>yMs+VoxE8=S*MUpEwQezvYW^JD1Dx#QbeTr1*nPjRTs1Ytu z5(1$}ip%6-(wXFFIg}EORd!j^mmHTYLL`XDzR+wT#?By<93aqTx-hcFPIx z5|>p(r5uNkF=SXsTUNT8TNoJIS}}c@S4OLP{>92`e17;1-K7;I4h2X7o+av3_B_zM zVikp>%}k{4_9>?O!rgl1%7)|r(fFZt)>;*p`gLHrYGYnQ{;J$p& z1x};=Q75g`AOm(9OucAcPJ7}OMsPob?_W<`K=`Ny0)c4>wE_R&e8lAaKmF>{_(RXA zmRvm**2mg^YPhiKOgQMml*=Af9}c<-U8uV1(|xZp=v^Q(a!Vu2Q$h2(jlVW<35v>9 zP7OQp^qc}Ba+FcM+~MdrMw5HlN_B;uSXy>IAz4avdD)4fVdIq{saWTiM=3R{)a6b> zkxuY5hETDPWGYl`Ht=ht9Fl$+SD*?yS)1wE5RzHT|;5+l+Vb%iSryXzc z{cmcsU3S}JuYLAA;GjbugR~faC5GAKh@*};xu(5~M&O10(|h79i(Rhd>xy0}a|?XCRFiQfuEs?<3DoXJR^B zz+;-YLmCV1x`KRu)r8+{1(Z_Ib_ApY)cNw-r+09RFq1UZU(Q${(V#mNgXd)lhrW^D z@E=lm++CM7TfoZ(K;=N0Xb1@)amJ6t9v>2O5K>d#NJlaS@`#CRQ@yGq7^&J+D_9)6 z1D(dAwv(dfeC?o0sZn_(6@V#$lj^(51S3^cAPOCcx&SGLkzN#H^GI{-amEvGt9>r; zr>B6mQ_{O^M4mmR2=Wh}Rgl8TcYH8`0gi@3>U?aq1L6LLdFX$jVdRrVn0X?&j>Zu5 zdJ9>KxO$`>&CWWGv(~cAU_m`l8<^w2-=Lg>oRYu20wZ62T{-!@T=FnjcSN7=EErBs zngpK!)Vyi|)}YbQ3JXx^G^P$04kIwp zY7p0=Rhv$4`5T_}op;>vzK?z26aT>PnBxyv?4Iwv=ugbk2H!g8d?mTZNhCK2H)BOxRvr2x{<0@#^Z zSgAk)ynL4Mn>JcZqbAMTg>@)5HsoY-VHn!oFCfo*&gLG^fV zD{KF!=NZl$2F8*@dfOg7di0C?sq@}nz5Rzbjy^K%zW*aq*4O)XIoY$9GUNU~3SGbb z5GA%jJf=efcK;!`yG!x*QF-jh#d};JKc=Lb{wvo*IhBd?o>&uCWYB?tF*GD#oh=$t zUaeBn$_a8J5^X)RBUNfniF-=4)DaOnQmWGGHT{d+G?CJl{~TAC z&61JW(o2XtI&ayK0Yi{$n%RuQBb_rLP|OME{n+HJA&2--idk`rfLxqj$gzuG;*{Ba zob2vT;zSM``qHwM{dPkhk2M~-bYd#z4P2e-!*YUPLkH%=9Z<6BKw%}ql#~$=?)pSp zEQ~1caXR;VQUc>FHk|(Bt~d|) z8wR+WK+@+<-N5%m2*%(KxCcR|VTh-U#Boymj(t$Wht`VGZpkmzO6&Qd6xz4S=(R#t@e4_|66p0CHgis7()d$2uwg=0|cfZ zFav=G1m+;H2?7g{+&0kaDA_5FOfvLkFZ*&diMO`RG_s3M#<+7|y7vH#t1B=oX-)96 z_EO7Dob0pvY%$|xWGiv+UL3z3>SLRrJP=JC<+(%P1=H!HSR(VJ;Zh{VM%BF2BKrm4 zh&QrU4t;N$Z<1p`i!tkQ;)>Jz@22DEU27Yjpo@?+1EW(p*!Kp`tn0AJ_RCUjA5={lqO!Z+qZOC>*m+I$LwoE|~1s|a}&te6B} zfFn~^$NG~#uGTJ2#MRsawiXnTr^=bT|odxg)yAkY|f zB8f}8cHDk8Mpb5xfIXrD!{_oTCk$l0+UkM!(295b!2(M*FB}$HG;D_mOoCMn-B6Sj z^xb=&i7;hl#Wu23~y36L&QKs#(pIU*$_R zPg!r1FV{S6xqhN~J(>9wT@g8Z+*Ok+F=VD0ilE06^@H(^F9!-*^kl8;%+J;N0e=mjBp& zTW(IjRTmI@iN~{fyDv_@Nqbw!hJqeOd71+ieW=#vVYZ|E6g^yb)c~g!bVXupCcsok zEDfB2tw@~B1e6pOm+=k2k%5XTscs>lrjpbz00z$jo~L`v=0xQk5C(WaOgI@+9Bprb9(`MF_*Bd+u0+8Ac`*0zD1wQX@q zkT-XSHr-_Jqk8a>9LMmCaB6#bCJK2G^cJqhExGwf?mmjgt37zO1+TW?-4=Wdkgt#A z=cD+eec-kYytaYgHV6!m&_@#acuyPKJ^~{7q*gH%C*&k51 zfMqe5^9Hc^1YqJH(B1{x?jh0Q!K-Hp8jhCpAz8XiURMm|qPjQTHDe@b7{lfYLU;mo zM)j$R)oWH$#k{yD(?)px#|Ed7+#=Lf^5sPV3={uamOQi)iv)u5P*w^_LC~7 zcx@tAUSL=1Xs-d>lUFrX6?V-pHWcBzsSI6Jdfqavjfsqyrcso7p4-$Z?r!_N)?+hD zIFEQ5ZMoW-)qP#FAaj#(V0mbQsg0ryH%gb&gT+>AWo_!=`^>{x?nb5;88IlsVidTg zF=Y@LJt)GW@(=YW4L*7}G;U@MtLuj@Pk0ugM0jb;!X*mMcSVzlQbAzjLWJvbg{-2f zl)Gi$+$(N4=d9tmxSC{K_gqlL69>nVD9WQKbd?`brbv9mFNKfJb(UhH=QN~8>-u@wD%cRGyxU1_k3;-(Rb zWt*I=BV`avbWbSKhr;z;=cv3lL}cl^h_JbHy#wAEm!ZLhk0-rVrn1R}I)<=B-V$sa z0w?zXx)9#KyD7Yy_tY<|2(;f=t@prQvneKchL_ zE$-Ys+4{UM@peR-5kehLP9|URBfk<|T&=w`CiaeRP4+Cu#|6>?SRv;Cpp&A;VZKGp z_O$L4JwM%&dDHM-*n)kVGWXE@{r0WSc^kLz5X!aMxc(vT0=~qJT~-(1<+17T$`!{x z^AJFRhtNg#WX8;=SlnEAjoL7@2NxzZX*_4rjN_>(1N@&Icdt-3hC;f_rmSGfD0qe$ z>AZ*&_I>O&%3ESCR_q?+<`kC0mDu>Tv(NvX^xXC4%CKYKQ>E78`KKruk>qB4ZfG~` z6GR^5NWaN#u&1Crm|g#IHxoxTQZ5N()_zzG+qSRun_hPxU}+Tk#KGf$T)mmSoVPA2 zUj$pu=vPID{CJD8l;vtut%QI7X^j(uYK7LZR5p)FHLlf6BByMNA%*Pq))=5DYDR43 z`!yqZ^KI7+QTW#w_c16&^199e^x?cil4w_cbl4QzvtoPCC_whk%-*t7k(I{LifmkQ$Q*0V|Q3$$(!U7?*)&WBbe8vsIiKhYRt4uS$kSDm`6~d}bq3 zy(m99_cHsgIfhAMdcpuwQ>qG-tOr_UX`W+L85awdVEqunW-^LlmHFCE78MW|N-Lx( zB~)A6aV$}BS8C%|hGUm~3f87~Po2=IZgbi3iGWGK%a~L+@d<8Aa~sbc(>b2P9cI5N zI&7h1M>Pv)#`R=EO=#TM;}&inL{T0iy$Be_gC@#bq}KrJcu{d(=;$^ zzamq~t=uGpMbSP^T!XD(9(}
>`x7rYpzLK$7C0B+z_D0FAtBZ4)nAX)IPtq(%(M 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 literal 0 HcmV?d00001 diff --git a/internal/api/adminui/index.html b/internal/api/adminui/index.html new file mode 100644 index 0000000..92502d5 --- /dev/null +++ b/internal/api/adminui/index.html @@ -0,0 +1,2427 @@ + + + + + +DANA — админка + + + + + + + + + + + + + + +
+
Специальности
+
+

Специальность — раздел на странице «Врачи» и часть адреса страницы врача. Поэтому адрес раздела + после создания не меняется, а удалить можно только раздел без врачей.

+
+
+
+ + +
+
+
+ + +
+
Редактирование
+
+
+ + +
+
+
+ + + +
+
+ +

Заявка

Просмотр и редактирование информации о заявке

+ +
+
+

Информация о заявке

+
+

Дополнительные настройки

+
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + diff --git a/internal/api/adminui/leads.css b/internal/api/adminui/leads.css new file mode 100644 index 0000000..3e6837e --- /dev/null +++ b/internal/api/adminui/leads.css @@ -0,0 +1,151 @@ +/* «Заявки»: отдельная голубая тема по предоставленному макету. Остальные вкладки не затрагивает. */ +body.leads-view { color: #20324c; background: radial-gradient(1100px 680px at 78% 8%, #d6eaff 0%, transparent 75%), #f2f8ff; } +body.leads-view .shell { padding: 0 34px 22px; background: radial-gradient(950px 540px at 96% 0%, #cfe5ff, transparent 80%); } +body.leads-view .top { min-height: 97px; gap: 17px; border-bottom: 1px solid #e1edfb; } +body.leads-view .top .search { width: min(720px, 48vw); } +body.leads-view .top .search input { border: 1px solid #d6e4f7; border-right: 0; border-radius: 16px 0 0 16px; height: 46px; font-size: 13px; } +body.leads-view .top .search__neck { display: none; } +body.leads-view .top .search__btn { margin-left: 0; width: 45px; height: 46px; border: 1px solid #d6e4f7; border-left: 0; border-radius: 0 16px 16px 0; } +body.leads-view .top .search__btn span { background: none; } +body.leads-view .round, body.leads-view .who__avatar { background: #fff; } +body.leads-view .round--date { width: auto; min-width: 192px; border-radius: 18px; padding: 0 16px; } +body.leads-view .who { margin-left: 12px; } +body.leads-view .page-title, body.leads-view .page-sub, body.leads-view .subtabs { display: none !important; } + +#tab-leads { max-width: 1500px; margin: 0 auto; } +.ld-heading { padding: 18px 0 20px; } +.ld-heading h1 { margin: 0 0 3px; font: 600 clamp(30px, 3vw, 42px)/1.15 var(--font-display); letter-spacing: -.04em; color: #1f3049; } +.ld-heading p { margin: 0; color: #637891; font-size: 14px; } +.ld-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; margin-bottom: 18px; } +.ld-stat { min-height: 99px; padding: 16px 23px; display: flex; align-items: center; gap: 24px; border-radius: 18px; background: #fff; box-shadow: 0 6px 30px rgba(62, 113, 175, .045); } +.ld-stat__icon { flex: 0 0 62px; height: 62px; display: grid; place-items: center; border-radius: 16px; color: #3186f4; background: linear-gradient(145deg, #edf5ff, #dcecff); } +.ld-stat__icon svg { width: 33px; height: 33px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.ld-stat:nth-child(2) .ld-stat__icon { color: #31455e; } +.ld-stat__copy { color: #617590; line-height: 1.4; white-space: nowrap; } +.ld-stat__copy strong { display: block; font: 600 27px/1.15 var(--font-display); color: #1e304a; } +.ld-stat__badge { margin-left: auto; min-width: 68px; padding: 7px 11px; border-radius: 99px; text-align: center; font-weight: 600; color: #20324c; } +.ld-stat__badge::before { content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 12px; background: #4595fb; } +.ld-stat__badge--blue { background: #e9f3ff; } +.ld-stat__badge--orange { background: #fff1df; } +.ld-stat__badge--orange::before { background: #f5a426; } +.ld-panel { padding: 24px 24px 12px; background: #fff; border-radius: 21px; box-shadow: 0 6px 30px rgba(62, 113, 175, .04); } +.ld-toolbar { display: grid; grid-template-columns: minmax(165px, 1fr) minmax(260px, 1.85fr) minmax(230px, 1.4fr) auto 52px; gap: 11px; margin-bottom: 22px; } +.ld-toolbar select, .ld-toolbar input { height: 47px; border: 1px solid #d6e3f5; border-radius: 11px; background: #fff; color: #465c79; } +.ld-toolbar select { padding-right: 30px; } +.ld-search { position: relative; } +.ld-search span { position: absolute; top: 3px; left: 15px; color: #38577c; font-size: 29px; pointer-events: none; } +.ld-search input { padding-left: 49px; } +.ld-date { display: flex; gap: 4px; border: 1px solid #d6e3f5; border-radius: 11px; align-items: center; padding: 0 8px; } +.ld-date label { min-width: 0; display: flex; align-items: center; color: #647992; font-size: 11px; } +.ld-date input { min-width: 0; width: 100%; height: 42px; padding: 2px 3px; border: 0; font-size: 11px; } +.ld-export, .ld-refresh { height: 47px; border: 0; border-radius: 11px; cursor: pointer; } +.ld-export { min-width: 160px; padding: 0 17px; display: inline-flex; align-items: center; justify-content: center; gap: 10px; color: #fff; background: linear-gradient(110deg, #6aaefb, #3287f1); font-weight: 600; } +.ld-export svg { width: 21px; height: 21px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.ld-export:disabled { opacity: .5; cursor: not-allowed; } +.ld-refresh { color: #324f70; background: #f2f7ff; border: 1px solid #d7e4f5; font-size: 26px; } +.ld-table-wrap { overflow-x: auto; border: 1px solid #e3edfa; border-radius: 11px; } +.ld-table { min-width: 830px; } +.ld-table th { padding: 13px 19px; background: #f2f7ff; color: #526782; font-size: 12px; font-weight: 600; } +.ld-table td { padding: 15px 19px; border-bottom-color: #e9f0fb; font-size: 13px; } +.ld-table tbody tr:hover { background: #f7fbff; } +.ld-table td:nth-child(5) { max-width: 320px; overflow-wrap: anywhere; } +.ld-table .pill { background: #e9f3ff; color: #2879d9; } +.ld-table .ld-empty-cell { padding: 0; } +.ld-empty { min-height: 326px; padding: 27px 20px 14px; text-align: center; display: flex; align-items: center; flex-direction: column; justify-content: center; } +.ld-empty__art { width: 245px; height: 135px; } +.ld-empty h2 { margin: 7px 0 4px; font: 600 22px var(--font-display); color: #20324c; } +.ld-empty p { max-width: 520px; margin: 0; color: #637891; } +.ld-empty__hint { margin-top: 12px; padding: 10px 24px; border-radius: 99px; color: #3078d6; background: #edf5ff; font-size: 12px; } +.ld-pager { display: flex; justify-content: space-between; align-items: center; gap: 14px; min-height: 54px; color: #56708f; font-size: 12px; } +.ld-pager__actions { display: flex; align-items: center; gap: 12px; } +.ld-pager .btn.ghost { border-radius: 10px; color: #547092; background: #f2f7ff; } +.ld-pager .btn.ghost:disabled { opacity: .5; cursor: default; } +.ld-privacy { margin: 0; padding-top: 4px; color: #879ab1; font-size: 11px; text-align: right; } +@media (max-width: 1100px) { + body.leads-view .shell { padding: 0 20px 20px; } + .ld-summary { gap: 12px; } + .ld-stat { gap: 12px; padding: 12px; } + .ld-stat__badge { min-width: 48px; } + .ld-toolbar { grid-template-columns: 1fr 2fr; } + .ld-export, .ld-refresh { width: 100%; } +} +@media (max-width: 700px) { + body.leads-view .top { min-height: 64px; } + body.leads-view .top .search { width: 100%; } + body.leads-view .top .round--date, body.leads-view .who { display: none; } + .ld-summary { grid-template-columns: 1fr; } + .ld-stat { min-height: 72px; } + .ld-stat__icon { flex-basis: 46px; height: 46px; } + .ld-stat__icon svg { width: 26px; height: 26px; } + .ld-panel { padding: 14px; } + .ld-toolbar { grid-template-columns: 1fr 1fr; } + .ld-toolbar select, .ld-search, .ld-date { grid-column: 1 / -1; } + .ld-pager { flex-wrap: wrap; } + .ld-empty__hint { border-radius: 12px; } +} + +/* ---------- карточка заявки ---------- */ +dialog.ldd { max-width: 960px; width: calc(100% - 32px); max-height: calc(100dvh - 32px); border-radius: 30px; color: #20324c; + overflow-y: auto; overscroll-behavior: contain; + background: radial-gradient(260px 160px at 72% 10%, rgba(175, 208, 250, .45), transparent 70%), + radial-gradient(220px 150px at 92% 18%, rgba(247, 196, 214, .45), transparent 70%), #f3f7fd; + box-shadow: 0 40px 90px rgba(37, 67, 110, .22); } +dialog.ldd::backdrop { background: rgba(214, 228, 245, .55); backdrop-filter: blur(8px); } +.ldd__form { padding: 22px 26px 24px; } +.ldd__head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 18px; } +.ldd__head h2 { margin: 2px 0 2px; font: 600 26px/1.2 var(--font-display); letter-spacing: -.02em; color: #1d2f49; } +.ldd__head p { margin: 0; font-size: 14.5px; color: #6a7d95; } +.ldd__icon { flex: 0 0 58px; height: 58px; display: grid; place-items: center; border-radius: 18px; color: #3d86f0; + background: linear-gradient(145deg, #edf5ff, #dcecff); box-shadow: 0 8px 20px rgba(61, 134, 240, .14); } +.ldd__icon svg { width: 28px; height: 28px; fill: currentColor; stroke: #fff; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; } +.ldd__close { margin-left: auto; width: 42px; height: 42px; border: 0; border-radius: 14px; background: #fff; color: #53657d; cursor: pointer; + display: grid; place-items: center; box-shadow: 0 4px 14px rgba(37, 67, 110, .08); } +.ldd__close:hover { color: #1d2f49; } +.ldd__close svg, .ldd__sec svg, .ldd__ticon svg, .ldd__save svg { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.ldd__close svg { width: 18px; height: 18px; } +.ldd__card { padding: 20px 22px 22px; border-radius: 24px; background: #fff; box-shadow: 0 10px 30px rgba(37, 67, 110, .06); } +.ldd__sec { display: flex; align-items: center; gap: 12px; margin: 0 0 16px; font-size: 16.5px; font-weight: 600; color: #1d2f49; } +.ldd__sec svg { width: 22px; height: 22px; color: #3d86f0; } +.ldd__sec--line { margin-top: 22px; padding-top: 22px; border-top: 1px solid #e8eef6; } +.ldd__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 22px; align-items: start; } +.ldd__col { display: flex; flex-direction: column; gap: 12px; min-width: 0; } +.ldd__tile { display: flex; align-items: center; gap: 16px; min-height: 64px; padding: 12px 16px; border-radius: 16px; background: #f4f8fd; } +.ldd__tile small { display: block; font-size: 12.5px; color: #6a7d95; } +.ldd__tile b { display: block; margin-top: 3px; font-size: 15.5px; font-weight: 500; color: #1d2f49; overflow-wrap: anywhere; white-space: pre-line; } +.ldd__tile a { color: inherit; text-decoration: none; } +.ldd__tile a:hover { color: #2f76f0; text-decoration: underline; } +.ldd__tile > div { min-width: 0; } +.ldd__ticon { flex: 0 0 44px; height: 44px; display: grid; place-items: center; border-radius: 13px; background: #fff; color: #5b86c7; } +.ldd__ticon svg { width: 21px; height: 21px; } +.ldd__tile--msg .ldd__ticon { background: #fdeef2; color: #e0587a; } +.ldd__field { display: flex; flex-direction: column; gap: 8px; font-size: 13.5px; color: #33475f; font-weight: 500; } +.ldd__field textarea { min-height: 90px; padding: 12px 16px; border: 1px solid #dde6f1; border-radius: 16px; background: #fff; font: inherit; font-weight: 400; + font-size: 14.5px; resize: vertical; } +.ldd__select { position: relative; display: block; } +.ldd__select select { width: 100%; height: 50px; padding: 0 44px 0 42px; border: 1.5px solid #9ec3f5; border-radius: 16px; background: #fff; font: inherit; + font-size: 15.5px; font-weight: 500; color: #1d2f49; appearance: none; cursor: pointer; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231d2f49' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; background-position: right 16px center; background-size: 18px; } +.ldd__select select:focus { outline: none; box-shadow: 0 0 0 4px rgba(63, 134, 242, .14); } +.ldd__dot { position: absolute; left: 18px; top: 50%; width: 11px; height: 11px; margin-top: -5.5px; border-radius: 50%; background: #22b07d; pointer-events: none; } +.ldd__dot[data-status="in_progress"] { background: #f5a426; } +.ldd__dot[data-status="done"] { background: #3d86f0; } +.ldd__dot[data-status="spam"] { background: #a3adbb; } +.ldd__foot { display: flex; justify-content: flex-end; gap: 14px; margin-top: 22px; } +.ldd__cancel, .ldd__save { height: 52px; padding: 0 28px; border: 0; border-radius: 16px; font: inherit; font-size: 15.5px; font-weight: 600; cursor: pointer; } +.ldd__cancel { background: #fff; color: #1d2f49; box-shadow: 0 4px 14px rgba(37, 67, 110, .08); } +.ldd__save { display: inline-flex; align-items: center; gap: 10px; padding: 0 32px; color: #fff; background: linear-gradient(100deg, #3a7ff0, #5c9cf6); + box-shadow: 0 12px 24px rgba(58, 127, 240, .3); } +.ldd__save:hover { filter: brightness(1.05); } +.ldd__save:disabled { opacity: .6; cursor: default; } +.ldd__save svg { width: 20px; height: 20px; } +@media (max-width: 760px) { + .ldd__form { padding: 16px 14px 16px; } + .ldd__head h2 { font-size: 21px; } + .ldd__head p { font-size: 13px; } + .ldd__icon { flex-basis: 46px; height: 46px; border-radius: 14px; } + .ldd__card { padding: 16px 14px; } + .ldd__grid { grid-template-columns: 1fr; } + .ldd__foot { gap: 10px; } + .ldd__cancel, .ldd__save { flex: 1; justify-content: center; padding: 0 14px; } +} diff --git a/internal/api/adminui/leads.js b/internal/api/adminui/leads.js new file mode 100644 index 0000000..18d3174 --- /dev/null +++ b/internal/api/adminui/leads.js @@ -0,0 +1,154 @@ +/* Раздел «Заявки»: заявки с форм сайта, статус и комментарий администратора. + Просмотр списка — доступ к персональным данным, сервер пишет его в журнал. */ + +const Leads = { + offset: 0, limit: 30, total: 0, loaded: false, visible: [], + + async load() { + const status = $('#ld-status').value; + const params = new URLSearchParams({ status, q: $('#ld-q').value.trim(), limit: Leads.limit, offset: Leads.offset }); + if ($('#ld-from').value) params.set('from', $('#ld-from').value); + if ($('#ld-to').value) params.set('to', $('#ld-to').value); + let r; + try { + r = await api(`/admin/leads?${params}`); + } catch (e) { toast(e.message, true); return; } + Leads.total = r.meta?.total || 0; + const counts = r.meta?.counts || {}; + for (const [id, value] of [['ld-new', counts.new || 0], ['ld-new-badge', counts.new || 0], + ['ld-progress', counts.in_progress || 0], ['ld-progress-badge', counts.in_progress || 0], + ['ld-today', r.meta?.today || 0], ['ld-today-badge', r.meta?.today || 0]]) $('#' + id).textContent = value; + for (const o of $('#ld-status').options) { + const n = o.value ? counts[o.value] || 0 : Object.values(counts).reduce((a, b) => a + b, 0); + o.textContent = o.textContent.replace(/ \(\d+\)$/, '') + ` (${n})`; + } + const badge = $('#ld-badge'); + if (badge) { badge.textContent = counts.new || ''; badge.hidden = !counts.new; } + $('#ld-count').textContent = `Показано ${r.data?.length || 0} из ${Leads.total} ${plural(Leads.total, 'заявки', 'заявок', 'заявок')}`; + const tb = $('#ld-body'); tb.textContent = ''; + const list = r.data || []; + Leads.visible = list; + if (!list.length) { + const tr = el('tr'); const td = el('td', { colSpan: 7, className: 'ld-empty-cell' }); + const empty = el('div', { className: 'ld-empty' }); + empty.innerHTML = ``; + const initialEmpty = Leads.total === 0 && !$('#ld-q').value && !$('#ld-from').value && !$('#ld-to').value && status === 'new'; + empty.append(el('h2', { textContent: initialEmpty ? 'Пока нет заявок' : 'Заявки не найдены' })); + if (!initialEmpty) empty.append(el('p', { textContent: 'Попробуйте изменить статус, даты или поисковый запрос.' })); + td.append(empty); tr.append(td); tb.append(tr); + } + const df = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' }); + const tf = new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }); + for (const l of list) { + const tr = el('tr'); + const when = el('td'); when.append(df.format(new Date(l.created_at)), el('small', { className: 'muted', textContent: ' ' + tf.format(new Date(l.created_at)) })); + const [label, cls] = LEAD_STATUS[l.status] || [l.status, 'grey']; + const st = el('td'); st.append(el('span', { className: 'pill ' + cls, textContent: label })); + const phone = el('td'); phone.append(el('a', { href: 'tel:' + l.phone.replace(/[^\d+]/g, ''), textContent: l.phone, onclick: (e) => e.stopPropagation() })); + tr.append(when, el('td', { textContent: l.name || '—' }), phone, el('td', { className: 'muted', textContent: l.form_title || '—' }), + el('td', { className: 'muted rv-text', textContent: l.message || l.doctor_name || l.service_name || '—' }), st); + const act = el('td', { className: 'right' }); + const e = el('button', { className: 'btn ghost', textContent: 'Открыть' }); + e.onclick = (ev) => { ev.stopPropagation(); Leads.open(l); }; + act.append(e); tr.append(act); + tr.style.cursor = 'pointer'; + tr.onclick = () => Leads.open(l); + tb.append(tr); + } + const from = Leads.total ? Leads.offset + 1 : 0; + $('#ld-page').textContent = `${from}–${Math.min(Leads.offset + Leads.limit, Leads.total)} из ${Leads.total}`; + $('#ld-prev').disabled = Leads.offset === 0; + $('#ld-next').disabled = Leads.offset + Leads.limit >= Leads.total; + $('#ld-export').disabled = list.length === 0; + }, + + exportVisible() { + if (!Leads.visible.length) return; + const columns = ['Дата', 'Имя', 'Телефон', 'Форма', 'Сообщение', 'Статус']; + const safe = (value) => { + let text = String(value ?? '').replace(/\r?\n/g, ' '); + if (/^[=+\-@\t]/.test(text)) text = "'" + text; + return '"' + text.replace(/"/g, '""') + '"'; + }; + const rows = Leads.visible.map((l) => [new Date(l.created_at).toLocaleString('ru-RU'), l.name, l.phone, l.form_title, l.message, LEAD_STATUS[l.status]?.[0] || l.status]); + const blob = new Blob(['\ufeff' + [columns, ...rows].map((row) => row.map(safe).join(';')).join('\r\n')], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob), a = document.createElement('a'); + a.href = url; a.download = `dana-leads-page-${new Date().toISOString().slice(0, 10)}.csv`; a.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + }, + + // карточка заявки: слева — когда и кто, справа — к кому и откуда; ниже статус и комментарий + open(l) { + const df = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + const left = [['Когда', 'calendar', df.format(new Date(l.created_at)).replace(', ', ' в ')], ['Форма', 'doc', l.form_title], + ['Имя', 'user', l.name], ['Телефон', 'phone', l.phone], ['E-mail', 'mail', l.email]]; + // желаемые дата и время из формы записи (payload): «2026-10-20» и «10:00» или старое «Утро, 08:00–12:00» + const p = l.payload || {}; + const wishDate = typeof p.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(p.date) ? p.date.split('-').reverse().join('.') : ''; + const wish = [wishDate, typeof p.time === 'string' ? p.time : ''].filter(Boolean).join(', '); + const right = [['Врач', 'doctor', l.doctor_name], ['Желаемое время', 'calendar', wish], ['Услуга', 'heart', l.service_name], ['Сообщение', 'chat', l.message], + ['Страница', 'link', l.page_url]]; + for (const [box, rows] of [[$('#ldd-left'), left], [$('#ldd-right'), right]]) { + box.textContent = ''; + for (const [label, icon, value] of rows.filter((r) => r[2])) { + const tile = el('div', { className: 'ldd__tile' + (icon === 'chat' ? ' ldd__tile--msg' : '') }); + tile.innerHTML = `${lddIcon(icon)}
`; + tile.querySelector('small').textContent = label; + const v = tile.querySelector('b'); + if (icon === 'phone') v.append(el('a', { href: 'tel:+' + String(value).replace(/\D/g, ''), textContent: value })); + else if (icon === 'mail') v.append(el('a', { href: 'mailto:' + value, textContent: value })); + else if (icon === 'link') v.append(el('a', { href: value, target: '_blank', rel: 'noopener', textContent: value.replace(/^https?:\/\/[^/]+/, '') || value })); + else v.textContent = value; + box.append(tile); + } + } + const sel = $('#ldd-status'); + sel.textContent = ''; + for (const [value, [label]] of Object.entries(LEAD_STATUS)) sel.append(el('option', { value, textContent: label })); + sel.value = l.status; + const dot = () => { $('#ldd-dot').dataset.status = sel.value; }; + sel.onchange = dot; dot(); + $('#ldd-comment').value = l.comment || ''; + const dlg = $('#ld-dlg'); + $('#ldd-save').onclick = async (e) => { + e.preventDefault(); + const b = $('#ldd-save'); b.disabled = true; + try { + await api('/admin/leads/' + l.id, { method: 'PUT', body: JSON.stringify({ status: sel.value, comment: $('#ldd-comment').value }) }); + toast('Заявка сохранена'); + dlg.close(); + Leads.load(); + } catch (err) { toast(err.message, true); } + b.disabled = false; + }; + dlg.showModal(); + }, +}; + +const LDD_ICONS = { + doc: '', + close: '', + user: '', + doctor: '', + gear: '', + calendar: '', + phone: '', + mail: '', + heart: '', + chat: '', + link: '', + save: '', +}; +const lddIcon = (n) => ``; +document.querySelectorAll('[data-ldd-icon]').forEach((e) => { e.innerHTML = lddIcon(e.dataset.lddIcon); }); +$('#ldd-close').onclick = () => $('#ld-dlg').close(); +$('#ldd-cancel').onclick = () => $('#ld-dlg').close(); +$('#ld-dlg').addEventListener('click', (e) => { if (e.target === e.currentTarget) e.currentTarget.close(); }); + +$('#ld-status').onchange = () => { Leads.offset = 0; Leads.load(); }; +$('#ld-q').addEventListener('input', debounce(() => { Leads.offset = 0; Leads.load(); }, 300)); +for (const id of ['ld-from', 'ld-to']) $('#' + id).onchange = () => { Leads.offset = 0; Leads.load(); }; +$('#ld-refresh').onclick = () => Leads.load(); +$('#ld-export').onclick = () => Leads.exportVisible(); +$('#ld-prev').onclick = () => { Leads.offset = Math.max(0, Leads.offset - Leads.limit); Leads.load(); }; +$('#ld-next').onclick = () => { Leads.offset += Leads.limit; Leads.load(); }; diff --git a/internal/api/adminui/onec.css b/internal/api/adminui/onec.css new file mode 100644 index 0000000..52b79e5 --- /dev/null +++ b/internal/api/adminui/onec.css @@ -0,0 +1,27 @@ +#tab-onec .oc-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 4px; } +#tab-onec .oc-head h2 { margin: 0; font-size: 17px; } +#tab-onec .oc-head .spacer { flex: 1; } +#tab-onec .oc-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 12px 0 16px; } +#tab-onec .oc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; } +#tab-onec .oc-stat { background: var(--field); border-radius: 14px; padding: 14px 16px; } +#tab-onec .oc-stat b { display: block; font-size: 24px; } +#tab-onec .oc-stat span { font-size: 12px; color: var(--muted); } +#tab-onec .oc-warn { margin: 12px 0 0; padding: 12px 14px; border-radius: 12px; background: #fbf3d9; color: var(--warn); font-size: 13px; } +#tab-onec .oc-small { padding: 5px 12px; font-size: 12px; margin-left: 6px; } +#tab-onec tr.oc-off { opacity: .55; } +#tab-onec td select { max-width: 280px; width: 100%; } +#tab-onec .oc-err { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; } +#tab-onec .oc-scroll { overflow-x: auto; } +#tab-onec .oc-scroll table { min-width: 720px; } +#tab-onec .oc-grid-row { display: grid; grid-template-columns: 240px 1fr; gap: 16px; padding: 12px 0; border-bottom: 1px solid var(--line); } +#tab-onec .oc-grid-row:last-child { border-bottom: 0; } +#tab-onec .oc-grid-doc { display: flex; flex-direction: column; gap: 2px; font-size: 13px; } +#tab-onec .oc-cells { display: flex; flex-wrap: wrap; gap: 6px; align-content: flex-start; } +#tab-onec .oc-cell { padding: 4px 10px; border-radius: 8px; font-size: 12px; font-variant-numeric: tabular-nums; } +#tab-onec .oc-cell.free { background: #e6f6ee; color: var(--mint-text); } +#tab-onec .oc-cell.busy { background: #eeefed; color: var(--muted); text-decoration: line-through; } +#tab-onec .oc-num { width: 64px; margin: 0 4px; } +#tab-onec label.oc-line { cursor: pointer; } +@media (max-width: 720px) { + #tab-onec .oc-grid-row { grid-template-columns: 1fr; gap: 8px; } +} diff --git a/internal/api/adminui/onec.js b/internal/api/adminui/onec.js new file mode 100644 index 0000000..6f277e4 --- /dev/null +++ b/internal/api/adminui/onec.js @@ -0,0 +1,281 @@ +/* «Запись через 1С»: статус синхронизации, привязка врачей 1С к карточкам сайта, + сетка окон на день и записи, сделанные с сайта. */ + +const ocEl = (t, p = {}, kids = []) => { const n = el(t, p); n.append(...kids); return n; }; + +const Onec = { + data: null, + siteDoctors: [], + gridDate: '', + busy: false, + + fmtTime(iso, opts = { hour: '2-digit', minute: '2-digit' }) { + return new Intl.DateTimeFormat('ru-RU', { timeZone: 'Europe/Moscow', ...opts }).format(new Date(iso)); + }, + fmtDateTime(iso) { + return iso ? Onec.fmtTime(iso, { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '—'; + }, + ago(iso) { + if (!iso) return 'ещё не было'; + const m = Math.round((Date.now() - new Date(iso).getTime()) / 60000); + if (m < 1) return 'только что'; + if (m < 60) return `${m} мин назад`; + return Onec.fmtDateTime(iso); + }, + docName(d) { + return [d.last_name, d.first_name, d.middle_name].filter(Boolean).join(' '); + }, + + async open() { + if (!Onec.gridDate) Onec.gridDate = new Date().toLocaleDateString('sv-SE', { timeZone: 'Europe/Moscow' }); + $('#oc-date').value = Onec.gridDate; + try { + const [main, docs, booking] = await Promise.all([api('/admin/onec'), api('/admin/doctors'), api('/admin/booking-settings')]); + Onec.data = main.data; + Onec.booking = booking.data; + Onec.siteDoctors = (docs.data || []).sort((a, b) => Onec.docName(a).localeCompare(Onec.docName(b), 'ru')); + } catch (e) { + toast(e.message, true); + return; + } + Onec.renderStatus(); + Onec.renderConfirm(); + Onec.renderDoctors(); + Onec.loadGrid(); + Onec.loadAppointments(); + }, + + renderStatus() { + const d = Onec.data, s = d.status || {}; + const box = $('#oc-status'); + box.textContent = ''; + if (!d.configured) { + box.append(ocEl('p', { className: 'oc-warn', textContent: 'Связь с 1С не настроена: на сервере не заданы ONEC_API_URL и ONEC_API_KEY. Пока запись работает как обычная заявка.' })); + $('#oc-sync').disabled = true; + return; + } + $('#oc-sync').disabled = false; + const state = s.last_error ? ['off', 'Ошибка'] : (s.last_ok ? ['', 'Работает'] : ['wait', 'Ждём первую синхронизацию']); + const stale = s.last_ok && Date.now() - new Date(s.last_ok).getTime() > 30 * 60000; + if (stale) { state[0] = 'off'; state[1] = 'Данные устарели — окна на сайте скрыты'; } + const stat = (label, value) => ocEl('div', { className: 'oc-stat' }, [ocEl('b', { textContent: value }), ocEl('span', { textContent: label })]); + box.append( + ocEl('div', { className: 'oc-line' }, [ + ocEl('span', { className: 'pill dot ' + state[0], textContent: state[1] }), + ocEl('span', { className: 'muted', textContent: `1С: ${d.host} · последняя удачная синхронизация: ${Onec.ago(s.last_ok)}` }), + ]), + ocEl('div', { className: 'oc-stats' }, [ + stat('врачей в 1С', s.doctors ?? 0), + stat('привязано к карточкам', s.linked ?? 0), + stat('свободных окон', s.free_slots ?? 0), + stat('записей с сайта', s.appointments ?? 0), + ]), + ); + if (s.last_error) box.append(ocEl('p', { className: 'oc-warn', textContent: 'Последняя ошибка: ' + s.last_error })); + }, + + // Подтверждение записи оператором в Битрикс24 и проверочный звонок пациента. + renderConfirm() { + const b = Onec.booking || {}, s = b.settings || {}; + const box = $('#oc-confirm'); + box.textContent = ''; + box.append(b.confirm_enabled + ? ocEl('div', { className: 'oc-line' }, [ + ocEl('span', { className: 'pill dot', textContent: 'Включено' }), + ocEl('span', { className: 'muted', textContent: 'Запись сразу занимает окно в 1С и создаёт лид в Битрикс24 («Не обработан»). «Клиент записался» — подтверждена, любая провальная стадия — запись в 1С отменяется.' }), + ]) + : ocEl('p', { className: 'oc-warn', textContent: b.bitrix_configured + ? 'Не работает: нет связи с 1С.' + : 'Не работает: на сервере не задан BITRIX_WEBHOOK_URL. Записи с сайта не попадают в Битрикс24 и не требуют подтверждения.' })); + + const num = (value, max, label) => ocEl('input', { type: 'number', min: '0', max: String(max), step: '1', value: String(value ?? 0), className: 'oc-num', 'aria-label': label }); + const after = num(s.autocancel_hours, 168, 'Часов после записи'); + const before = num(s.cancel_before_hours, 48, 'Часов до приёма'); + const phone = ocEl('input', { type: 'checkbox', checked: !!s.phone_check, disabled: !b.smsc_configured }); + const perDoc = num(s.max_per_doctor_day, 50, 'Неподтверждённых к врачу в день'); + const perHour = num(s.max_per_hour, 1000, 'Онлайн-записей за час'); + const perPhone = num(s.max_active_per_phone, 20, 'Будущих записей на один номер'); + const save = ocEl('button', { type: 'button', className: 'btn', textContent: 'Сохранить' }); + save.onclick = () => Onec.saveConfirm({ + phone_check: phone.checked, + autocancel_hours: Number(after.value) || 0, + cancel_before_hours: Number(before.value) || 0, + max_per_doctor_day: Number(perDoc.value) || 0, + max_per_hour: Number(perHour.value) || 0, + max_active_per_phone: Number(perPhone.value) || 0, + }, save); + box.append( + ocEl('p', {}, [ + 'Отменять неподтверждённую запись через ', after, ' ч после записи или за ', before, + ' ч до приёма — что наступит раньше (но не раньше чем через 30 минут после записи). 0 — правило выключено.', + ]), + ocEl('label', { className: 'oc-line' }, [phone, ocEl('span', { + textContent: 'Требовать проверочный звонок от пациента перед записью' + (b.smsc_configured ? '' : ' (недоступно: не настроен SMSC)'), + })]), + ocEl('p', { className: 'muted', textContent: 'Защита расписания от ботов (0 — правило выключено). Обычно пациент записывается без звонка. ' + + 'Если сработало любое правило ниже, сайт просит проверочный звонок: настоящий пациент записывается дальше, бот останавливается.' + + (b.smsc_configured ? '' : ' Сейчас SMSC не настроен — вместо звонка такая запись придёт оператору заявкой, без окна в 1С.') }), + ocEl('p', {}, ['Просить звонок, если за последний час уже ', perHour, ' онлайн-записей,']), + ocEl('p', {}, ['или у врача на этот день уже ', perDoc, ' неподтверждённых записей,']), + ocEl('p', {}, ['или на этот номер уже ', perPhone, ' будущих записей.']), + ocEl('div', {}, [save]), + ); + }, + + async saveConfirm(body, btn) { + btn.disabled = true; + try { + Onec.booking = (await api('/admin/booking-settings', { method: 'PUT', body: JSON.stringify(body) })).data; + toast('Настройки записи сохранены'); + Onec.renderConfirm(); + } catch (e) { + toast(e.message, true); + btn.disabled = false; + } + }, + + renderDoctors() { + const tb = $('#oc-doctors'); tb.textContent = ''; + const rows = Onec.data.doctors || []; + $('#oc-doctors-count').textContent = `${rows.length} ${plural(rows.length, 'врач', 'врача', 'врачей')}`; + if (!rows.length) { + const tr = ocEl('tr'); tr.append(ocEl('td', { colSpan: 5, className: 'muted dr-empty', textContent: 'Врачей из 1С пока нет — дождитесь синхронизации.' })); tb.append(tr); + return; + } + const takenBy = new Map(rows.filter((r) => r.doctor_id).map((r) => [r.doctor_id, r.onec_id])); + for (const r of rows) { + const tr = ocEl('tr'); + if (!r.active) tr.className = 'oc-off'; + const name = ocEl('td'); + name.append(ocEl('b', { textContent: r.full_name }), ocEl('div', { className: 'muted', textContent: (r.specialties || []).join(', ') + (r.active ? '' : ' · в 1С не работает') })); + + const sel = ocEl('select', { 'aria-label': 'Карточка врача на сайте' }); + sel.append(ocEl('option', { value: '', textContent: '— не привязан —' })); + for (const sd of Onec.siteDoctors) { + const other = takenBy.get(sd.id); + const o = ocEl('option', { value: sd.id, textContent: Onec.docName(sd) + (other && other !== r.onec_id ? ' (занят другим врачом 1С)' : '') }); + if (sd.id === r.doctor_id) o.selected = true; + sel.append(o); + } + sel.onchange = () => Onec.link(r, sel.value ? Number(sel.value) : null); + const link = ocEl('td'); link.append(sel); + + const way = ocEl('td'); + const bookingNative = r.booking === 'native'; + if (!r.doctor_id) { + way.append(ocEl('span', { className: 'muted', textContent: 'сначала привяжите карточку' })); + } else { + const label = { native: '1С', medflex: 'Medflex', none: 'заявка', '': 'заявка' }[r.booking] || r.booking; + way.append(ocEl('span', { className: 'pill ' + (bookingNative ? '' : 'grey'), textContent: label })); + const b = ocEl('button', { type: 'button', className: 'btn ghost oc-small', textContent: bookingNative ? 'Отключить' : 'Включить запись через 1С' }); + b.onclick = () => Onec.setBooking(r, !bookingNative); + way.append(' ', b); + } + tr.append(name, link, way, ocEl('td', { textContent: String(r.free_slots ?? 0) })); + tb.append(tr); + } + }, + + async link(r, doctorID) { + try { + await api(`/admin/onec/doctors/${encodeURIComponent(r.onec_id)}/link`, { method: 'PUT', body: JSON.stringify({ doctor_id: doctorID }) }); + toast(doctorID ? 'Врач привязан к карточке. Включите запись через 1С.' : 'Привязка снята'); + } catch (e) { toast(e.message, true); } + Onec.open(); + }, + + async setBooking(r, enabled) { + try { + await api(`/admin/onec/doctors/${encodeURIComponent(r.onec_id)}/booking`, { method: 'PUT', body: JSON.stringify({ enabled }) }); + toast(enabled ? 'Запись через 1С включена: на карточке врача появятся окна' : 'Запись через 1С отключена: остаётся обычная заявка'); + // кэш врачей на других вкладках админки устарел + if (typeof loadAdminDoctors === 'function') loadAdminDoctors().catch(() => {}); + } catch (e) { toast(e.message, true); } + Onec.open(); + }, + + async sync() { + if (Onec.busy) return; + Onec.busy = true; + const b = $('#oc-sync'); b.disabled = true; b.textContent = 'Синхронизируем…'; + try { + await api('/admin/onec/sync', { method: 'POST' }); + toast('Расписание обновлено'); + } catch (e) { toast(e.message, true); } + Onec.busy = false; b.disabled = false; b.textContent = 'Синхронизировать сейчас'; + Onec.open(); + }, + + async loadGrid() { + const box = $('#oc-grid'); box.textContent = ''; + let rows; + try { rows = (await api('/admin/onec/grid?date=' + encodeURIComponent(Onec.gridDate))).data || []; } catch (e) { toast(e.message, true); return; } + if (!rows.length) { box.append(ocEl('p', { className: 'muted', textContent: 'На эту дату окон в расписании нет.' })); return; } + const byDoc = new Map(); + for (const r of rows) { + const key = r.doctor_onec_id + '|' + r.branch; + if (!byDoc.has(key)) byDoc.set(key, { name: r.doctor_name, branch: r.branch, cabinet: r.cabinet, slots: [] }); + byDoc.get(key).slots.push(r); + } + for (const g of byDoc.values()) { + const row = ocEl('div', { className: 'oc-grid-row' }); + const head = ocEl('div', { className: 'oc-grid-doc' }, [ocEl('b', { textContent: g.name }), ocEl('span', { className: 'muted', textContent: g.branch })]); + const cells = ocEl('div', { className: 'oc-cells' }); + for (const s of g.slots) { + cells.append(ocEl('span', { className: 'oc-cell ' + (s.status === 'free' ? 'free' : 'busy'), textContent: Onec.fmtTime(s.start), title: s.status === 'free' ? 'Свободно' : 'Занято' })); + } + row.append(head, cells); + box.append(row); + } + }, + + async loadAppointments() { + const tb = $('#oc-appts'); tb.textContent = ''; + let rows; + try { rows = (await api('/admin/onec/appointments?limit=100')).data || []; } catch (e) { toast(e.message, true); return; } + if (!rows.length) { + const tr = ocEl('tr'); tr.append(ocEl('td', { colSpan: 7, className: 'muted dr-empty', textContent: 'Записей с сайта через 1С пока нет.' })); tb.append(tr); + return; + } + const STATUS = { booked: ['', 'Записан'], pending: ['wait', 'Отправляется'], failed: ['off', 'Не записан'], cancelled: ['grey', 'Отменён'], arrived: ['', 'Пришёл'] }; + const CONFIRM = { + waiting: ['wait', 'Ждёт оператора'], confirmed: ['', 'Подтверждена'], cancelled: ['grey', 'Отменена оператором'], + expired: ['off', 'Не подтвердили — отменена'], missed: ['grey', 'Приём без подтверждения'], + lead_only: ['wait', 'Отправляется в Битрикс'], lead_sent: ['', 'Заявка в Битриксе'], + }; + for (const a of rows) { + const st = STATUS[a.status] || ['grey', a.status]; + const tr = ocEl('tr'); + const patient = ocEl('td'); + patient.append(ocEl('b', { textContent: a.patient_name }), ocEl('div', { className: 'muted', textContent: a.patient_phone })); + const doc = ocEl('td'); + doc.append(ocEl('span', { textContent: a.doctor_name || a.doctor_onec_id }), ocEl('div', { className: 'muted', textContent: a.branch || '' })); + const state = ocEl('td'); + state.append(ocEl('span', { className: 'pill ' + st[0], textContent: st[1] })); + if (a.error) state.append(ocEl('div', { className: 'muted oc-err', textContent: a.error, title: a.error })); + const confirm = ocEl('td'); + const cs = CONFIRM[a.confirm_state]; + if (cs) { + confirm.append(ocEl('span', { className: 'pill ' + cs[0], textContent: cs[1] })); + if (a.bitrix_lead_id) confirm.append(ocEl('div', { className: 'muted', textContent: 'лид № ' + a.bitrix_lead_id })); + if (a.confirm_error) confirm.append(ocEl('div', { className: 'muted oc-err', textContent: a.confirm_error, title: a.confirm_error })); + } else { + confirm.append(ocEl('span', { className: 'muted', textContent: '—' })); + } + tr.append( + ocEl('td', { textContent: Onec.fmtDateTime(a.created_at) }), + patient, doc, + ocEl('td', { textContent: Onec.fmtDateTime(a.start) }), + state, confirm, + ocEl('td', { className: 'muted', textContent: a.onec_appointment_id || '—' }), + ); + tb.append(tr); + } + }, + + init() { + $('#oc-sync').onclick = Onec.sync; + $('#oc-date').onchange = (e) => { Onec.gridDate = e.target.value; Onec.loadGrid(); }; + }, +}; +Onec.init(); diff --git a/internal/api/adminui/pbx.js b/internal/api/adminui/pbx.js new file mode 100644 index 0000000..893268e --- /dev/null +++ b/internal/api/adminui/pbx.js @@ -0,0 +1,453 @@ +/* «Отчёты» → «Звонки АТС»: подключение Виртуальной АТС МегаФон, загрузка звонков, + отчёт по звонкам и привязка номеров клиники к каналам. Ключ API — только на сервере (.env). */ + +const pxISO = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; +const pxDate = (iso) => { const [y, m, d] = iso.split('-').map(Number); return new Date(y, m - 1, d); }; +const pxRu = (iso) => (iso ? iso.split('-').reverse().join('.') : 'дд.мм.гггг'); +const pxDur = (s) => (s >= 3600 ? `${Math.floor(s / 3600)} ч ${pad2(Math.round((s % 3600) / 60))} мин` : s >= 60 ? `${Math.round(s / 60)} мин` : `${s} с`); +const pxDT = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); +const pxNum = (n) => Number(n).toLocaleString('ru-RU'); + +const PX_ICONS = { + phone: '', + phoneIn: '', + phoneOut: '', + phoneMiss: '', + people: '', + callback: '', + calendar: '', + copy: '', + info: '', + left: '', + right: '', + dots: '', + up: '', + down: '', +}; +const pxIcon = (name) => ``; +document.querySelectorAll('[data-px-icon]').forEach((e) => { e.innerHTML = pxIcon(e.dataset.pxIcon); }); + +/* ---------- выбор периода: два поля и календарь с выделением диапазона ---------- */ +const PX_MONTHS = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']; + +class PxRange { + constructor(box) { + this.box = box; + this.from = $('#' + box.dataset.from); + this.to = $('#' + box.dataset.to); + box.innerHTML = '—' + + ''; + this.pop = box.querySelector('.dp'); + this.active = 'from'; + box.querySelectorAll('.px-date').forEach((b) => { b.onclick = () => this.toggle(b.dataset.end); }); + document.addEventListener('click', (e) => { if (!this.pop.hidden && !box.contains(e.target)) this.close(); }); + box.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !this.pop.hidden) { this.close(); box.querySelector('.px-date').focus(); } }); + } + + // значения полей → подписи кнопок + sync() { + for (const [b, input] of [[this.box.querySelector('[data-end=from]'), this.from], [this.box.querySelector('[data-end=to]'), this.to]]) { + b.innerHTML = pxIcon('calendar') + `${pxRu(input.value)}`; + b.classList.toggle('empty', !input.value); + b.classList.toggle('active', !this.pop.hidden && b.dataset.end === this.active); + b.setAttribute('aria-expanded', String(!this.pop.hidden && b.dataset.end === this.active)); + b.setAttribute('aria-label', (b.dataset.end === 'from' ? 'Период с ' : 'Период по ') + (input.value ? pxRu(input.value) : 'не выбрано')); + } + } + + // Левая и правая кнопки явно выбирают соответствующую границу периода. + // Повторный клик по полю не закрывает календарь: он закрывается выбором конца, + // кликом снаружи или Escape. Так правое поле нельзя случайно принять за начало. + toggle(end) { + if (this.pop.hidden) this.open(end); + else { + this.active = end; + const value = end === 'to' ? (this.to.value || this.from.value) : this.from.value; + if (value) { + const d = pxDate(value); + this.month = new Date(d.getFullYear(), d.getMonth(), 1); + } + this.sync(); this.render(); + } + } + + open(end = 'from') { + this.active = end; + const value = end === 'to' ? (this.to.value || this.from.value) : this.from.value; + const base = value ? pxDate(value) : new Date(); + this.month = new Date(base.getFullYear(), base.getMonth(), 1); + this.pop.hidden = false; + this.box.classList.add('open'); + this.sync(); + this.render(); + // на телефоне календарь может открыться ниже края экрана + requestAnimationFrame(() => this.pop.scrollIntoView({ block: 'nearest', behavior: 'smooth' })); + } + + close() { this.pop.hidden = true; this.box.classList.remove('open'); this.sync(); } + + pick(iso) { + if (this.active === 'to' && this.from.value) { + if (iso < this.from.value) { + // Если конец оказался раньше начала, сохраняем обе выбранные даты, + // просто располагая их в хронологическом порядке. + this.to.value = this.from.value; + this.from.value = iso; + } else { + this.to.value = iso; + } + this.active = 'from'; + this.sync(); this.render(); + setTimeout(() => this.close(), 180); + } else { + this.from.value = iso; + if (this.to.value && this.to.value < iso) this.to.value = ''; + this.active = 'to'; + this.sync(); this.render(); + } + } + + render() { + const y = this.month.getFullYear(), m = this.month.getMonth(); + const first = new Date(y, m, 1), shift = (first.getDay() + 6) % 7; + const start = new Date(y, m, 1 - shift), today = pxISO(new Date()); + const a = this.from.value, b = this.to.value || this.from.value; + let html = `
` + + `${PX_MONTHS[m]} ${y}
`; + ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'].forEach((w, i) => { html += `${w}`; }); + for (let i = 0; i < 42; i++) { + const d = new Date(start.getFullYear(), start.getMonth(), start.getDate() + i); + if (i === 35 && d.getMonth() !== m) break; // шестая неделя не нужна + const iso = pxISO(d), cls = ['dp__day']; + if (d.getMonth() !== m) cls.push('dp__out'); + if (i % 7 > 4) cls.push('dp__we'); + if (a && iso >= a && iso <= b) cls.push('dp__in'); + if (iso === a) cls.push('dp__start'); + if (iso === b && this.to.value) cls.push('dp__end'); + if (iso === today) cls.push('dp__today'); + html += ``; + } + html += '
' + + '
'; + this.pop.innerHTML = html; + this.pop.querySelectorAll('[data-go]').forEach((btn) => { + btn.onclick = () => { this.month = new Date(y, m + Number(btn.dataset.go), 1); this.render(); }; + }); + this.pop.querySelectorAll('[data-d]').forEach((btn) => { btn.onclick = () => this.pick(btn.dataset.d); }); + this.pop.querySelector('[data-act=clear]').onclick = () => { this.from.value = ''; this.to.value = ''; this.active = 'from'; this.sync(); this.render(); }; + this.pop.querySelector('[data-act=today]').onclick = () => { + this.from.value = this.to.value = today; this.active = 'from'; + this.month = new Date(); this.month.setDate(1); + this.sync(); this.render(); setTimeout(() => this.close(), 180); + }; + } +} + +/* ---------- мини-график в карточке ---------- */ +function pxSpark(values, color) { + const w = 220, h = 44, pad = 3; + if (!values || values.length < 2) return ''; + const max = Math.max(...values), min = Math.min(...values); + const span = max - min || 1; + const pts = values.map((v, i) => [(i / (values.length - 1)) * w, h - pad - ((v - min) / span) * (h - pad * 2)]); + const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' '); + const id = 'pxg' + Math.random().toString(36).slice(2, 8); + return ``; +} + +const Pbx = { + status: null, + channels: [], + report: null, + lostFilter: '', + + // период по умолчанию — текущая неделя, как в ВАТС + defaults() { + const now = new Date(), mon = new Date(now); + mon.setDate(now.getDate() - ((now.getDay() + 6) % 7)); + for (const [a, b] of [['#px-from', '#px-to'], ['#px-sync-from', '#px-sync-to'], ['#rp-pbx-from', '#rp-pbx-to']]) { + if (!$(a).value) $(a).value = pxISO(mon); + if (!$(b).value) $(b).value = pxISO(now); + } + Pbx.ranges.forEach((r) => r.sync()); + }, + + async open() { + Pbx.defaults(); + await Pbx.loadStatus(); + await Promise.all([Pbx.loadReport(), Pbx.loadLines()]); + }, + + async loadStatus() { + try { + const r = await api(ReportClinic.q('/admin/pbx/status')); + Pbx.status = r.data; + } catch (e) { toast(e.message, true); return; } + const { configured, domain, status: s } = Pbx.status; + $('#px-conn').classList.toggle('off', !configured); + $('#px-conn-text').textContent = configured ? `Подключена: ${domain}` : 'Не подключена — ключ API ещё не задан на сервере'; + $('#px-copy').hidden = !configured; + const parts = []; + parts.push(s.calls ? `В базе ${pxNum(s.calls)} звонков, с ${ruDate(localDate(s.oldest))} по ${ruDate(localDate(s.newest))}.` : 'Звонков в базе пока нет.'); + if (s.last_sync_at) parts.push(`Последняя загрузка: ${pxDT.format(new Date(s.last_sync_at))}${s.last_error ? ' — ошибка: ' + s.last_error : ', звонков ' + s.last_calls}.`); + if (s.lines_unknown) parts.push(`Линий без канала: ${s.lines_unknown} — их звонки в отчёты не входят (канал задаётся внизу страницы).`); + $('#px-state').textContent = parts.join(' '); + $('#px-state').classList.toggle('px-bad', !!s.last_error); + $('#px-setup').hidden = configured; + // строки .env своей клиники: у DANA MEGAFON_API_*, у «Очевидно» OCHEVIDNO_MEGAFON_API_* + const [urlVar, keyVar] = (Pbx.status.env || 'MEGAFON_API_URL и MEGAFON_API_KEY').split(' и '); + $('#px-setup-env').textContent = `${urlVar}=${ReportClinic.key === 'ochevidno' ? 'vats363024' : 'vats970345'}.megapbx.ru\n${keyVar}=ваш ключ`; + $('#px-check').disabled = !configured; + $('#px-sync').disabled = !configured; + $('#rp-pbx-opt').classList.toggle('px-off', !configured && !s.calls); + }, + + async check() { + const b = $('#px-check'); b.disabled = true; + try { + const r = await api(ReportClinic.q('/admin/pbx/check'), { method: 'POST' }); + toast(`Ключ принят: сотрудников в АТС — ${r.data.users}`); + } catch (e) { toast(e.message, true); } + b.disabled = false; + }, + + async sync() { + if (!$('#px-sync-from').value || !$('#px-sync-to').value) { toast('Выберите период загрузки', true); return; } + const b = $('#px-sync'); b.disabled = true; + const old = b.textContent; b.textContent = 'Загружаем…'; + try { + const r = await api(ReportClinic.q('/admin/pbx/sync'), { method: 'POST', body: JSON.stringify({ from: $('#px-sync-from').value, to: $('#px-sync-to').value }) }); + toast(`Загружено звонков: ${r.data.calls}`); + } catch (e) { toast(e.message, true); } + b.textContent = old; b.disabled = false; + await Pbx.loadStatus(); + await Promise.all([Pbx.loadReport(), Pbx.loadLines()]); + }, + + // Период для сравнения. Месяц (с 1-го числа) — с прошлым месяцем: целый — с целым, начало + // месяца — с теми же числами. Остальное (неделя, несколько дней) — с теми же днями недели + // на целое число недель раньше. Если период включает сегодня — прошлый берётся до того же часа. + // missing: звонков прошлого периода в базе нет целиком — сравнивать не с чем. + previousRange(from, to) { + const oldest = Pbx.status?.status?.oldest; + if (!from || !to) return null; + const now = new Date(), today = pxISO(now); + if (from > today) return null; + const f = pxDate(from), t = pxDate(to); + // будущие дни звонков ещё не имеют: сравниваем то, что уже прошло, — по сегодня + const cut = to > today ? pxDate(today) : t; + const lastDay = (y, m) => new Date(y, m + 1, 0).getDate(); + let pf = null, pt = null, kind = 'week'; + if (f.getDate() === 1) { + const months = (t.getFullYear() - f.getFullYear()) * 12 + t.getMonth() - f.getMonth() + 1; + const full = t.getDate() === lastDay(t.getFullYear(), t.getMonth()); + if (full || months === 1) { + kind = 'month'; + pf = new Date(f.getFullYear(), f.getMonth() - months, 1); + if (full && cut === t) pt = new Date(pf.getFullYear(), pf.getMonth() + months, 0); + else { + // начало месяца (или месяц, который ещё идёт) — те же числа прошлого месяца + const m = pf.getMonth() + (cut.getMonth() - f.getMonth()) + (cut.getFullYear() - f.getFullYear()) * 12; + pt = new Date(pf.getFullYear(), m, Math.min(cut.getDate(), lastDay(pf.getFullYear(), m))); + } + } + } + if (!pf) { + const shift = Math.ceil((Math.round((t - f) / 864e5) + 1) / 7) * 7; + pf = new Date(f); pf.setDate(pf.getDate() - shift); + pt = new Date(cut); pt.setDate(pt.getDate() - shift); + } + const mins = now.getHours() * 60 + now.getMinutes(); + const until = pxISO(cut) === today && mins > 0 ? `${pxISO(pt)}T${pad2(now.getHours())}:${pad2(now.getMinutes())}` : ''; + return { from: pxISO(pf), to: pxISO(pt), until, kind, missing: !oldest || pxISO(pf) < localDate(oldest) }; + }, + + async loadReport() { + const from = $('#px-from').value, to = $('#px-to').value; + if (from && !to) { toast('Выберите конец периода', true); return; } + const q = from ? `?from=${from}&to=${to}` : ''; + let r, prev = null; + const pr = Pbx.previousRange(from, to); + try { + [r, prev] = await Promise.all([api(ReportClinic.q('/admin/pbx/report' + q)), + pr && !pr.missing ? api(ReportClinic.q(`/admin/pbx/report?from=${pr.from}&to=${pr.to}${pr.until ? '&until=' + pr.until : ''}`)).catch(() => null) : null]); + } catch (e) { toast(e.message, true); return; } + const d = r.data, t = d.total, p = prev?.data; + Pbx.report = d; + const pt = p && (p.total.incoming + p.outgoing) > 0 ? p.total : null; + // с чем сравниваются проценты в карточках + let cmp = ''; + if (pr) { + const span = `${pxRu(pr.from)}–${pxRu(pr.to)}${pr.until ? ', до ' + pr.until.slice(11) : ''}`; + const what = pr.kind === 'month' ? 'прошлый месяц' : 'те же дни недели раньше'; + cmp = pr.missing ? `Сравнения нет: звонки за ${span} не загружены.` + : pt ? `Проценты — к периоду ${span} (${what}).` : `Сравнения нет: за ${span} звонков нет.`; + } + $('#px-cmp').textContent = cmp; + $('#px-cmp').hidden = !cmp; + // [подпись, значение, подсказка, иконка, цвет, ряд, сравнение: [сейчас, раньше, рост — хорошо?, в п.п.?]] + const kpis = [ + ['Все звонки', pxNum(t.incoming + d.outgoing), 'входящие и исходящие', 'phone', '#3f86f2', d.series.all, + pt && [t.incoming + d.outgoing, pt.incoming + p.outgoing, true]], + ['Входящих звонков', pxNum(t.incoming), `разных клиентов: ${pxNum(t.clients)}`, 'phoneIn', '#22b07d', d.series.incoming, + pt && [t.incoming, pt.incoming, true]], + ['Принято', `${Math.round(t.answered_rate)}%`, `${pxNum(t.answered)} звонков, ожидание ~${Math.round(t.avg_wait)} с`, 'people', '#8b6cf0', + d.series.answered_rate, pt && [t.answered_rate, pt.answered_rate, true, true]], + ['Пропущено', pxNum(t.missed), 'входящих без ответа', 'phoneMiss', '#ec5a72', d.series.missed, pt && [t.missed, pt.missed, false]], + ['Не перезвонили', pxNum(t.missed_lost), 'клиентов после пропущенного', 'callback', '#f29a3c', d.series.lost, + pt && [t.missed_lost, pt.missed_lost, false]], + ['Исходящих', pxNum(d.outgoing), `разговоры: ${pxDur(t.out_talk)}`, 'phoneOut', '#1fa6c4', d.series.outgoing, + pt && [d.outgoing, p.outgoing, true]], + ]; + const k = $('#px-kpis'); k.textContent = ''; + for (const [label, value, hint, icon, color, series, cmp] of kpis) { + const c = el('div', { className: 'px-kpi' }); + c.style.setProperty('--c', color); + let trend = ''; + if (cmp) { + const [now, before, upGood, points] = cmp; + const diff = points ? now - before : before ? ((now - before) / before) * 100 : null; + if (diff !== null && Number.isFinite(diff)) { + const v = Math.round(diff); + const good = v === 0 ? null : (v > 0) === upGood; + trend = `` + + `${v ? pxIcon(v > 0 ? 'up' : 'down') : ''}${v > 0 ? '+' : v < 0 ? '−' : ''}${Math.abs(v)}${points ? ' п.п.' : '%'}`; + } + } + c.innerHTML = `
${pxIcon(icon)}
` + + `
${trend}
` + + `${pxSpark(series, color)}`; + c.querySelector('.px-kpi__value').textContent = value; + c.querySelector('.px-kpi__label').textContent = label; + c.querySelector('.px-kpi__hint').textContent = hint; + k.append(c); + } + const tb = $('#px-channels'); tb.textContent = ''; + if (!d.channels.length) tb.append(Pbx.empty(9, 'За период нет звонков на линии с каналом. Загрузите звонки из АТС, выберите другие даты или задайте каналы линиям внизу страницы.')); + // строка «Итого» — сверка с фильтром по тем же номерам в «Истории» ВАТС + const rows = d.channels.length > 1 ? [...d.channels, { ...t, outgoing: d.outgoing, channel: 'Итого', total: true }] : d.channels; + for (const c of rows) { + const tr = el('tr', { className: c.total ? 'px-total' : '' }); + const name = el('td'); + name.append(c.total ? el('b', { textContent: c.channel }) : Pbx.chip(c.channel)); + const menu = el('td', { className: 'px-menu-cell' }); + if (!c.total) { + const btn = el('button', { type: 'button', className: 'px-more' }); + btn.innerHTML = pxIcon('dots'); + btn.setAttribute('aria-label', 'Действия: ' + c.channel); + btn.onclick = (e) => { e.stopPropagation(); Pbx.menu(btn, c); }; + menu.append(btn); + } + tr.append(name, el('td', { textContent: pxNum(c.incoming) }), el('td', { textContent: pxNum(c.clients) }), + el('td', { textContent: c.incoming ? `${pxNum(c.answered)} · ${Math.round(c.answered_rate)}%` : '—' }), + el('td', { className: c.total && c.missed ? 'px-lost' : '', textContent: pxNum(c.missed) }), + el('td', { textContent: pxNum(c.outgoing) }), el('td', { textContent: pxDur(c.talk_seconds + c.out_talk) }), + el('td', { textContent: c.incoming ? `${Math.round(c.avg_wait)} с` : '—' }), menu); + tb.append(tr); + } + Pbx.renderLost(); + const ub = $('#px-users'); ub.textContent = ''; + if (!d.users.length) ub.append(Pbx.empty(4, 'Нет звонков за период.')); + for (const u of d.users) { + const tr = el('tr'); + tr.append(el('td', { textContent: u.name }), el('td', { textContent: u.answered }), el('td', { textContent: u.outgoing }), el('td', { textContent: pxDur(u.talk_seconds) })); + ub.append(tr); + } + }, + + renderLost() { + const d = Pbx.report; + if (!d) return; + const list = Pbx.lostFilter ? d.lost.filter((l) => l.channel === Pbx.lostFilter) : d.lost; + $('#px-lost-title').textContent = Pbx.lostFilter ? `Кому перезвонить · ${Pbx.lostFilter}` : 'Кому перезвонить'; + $('#px-lost-all').hidden = !Pbx.lostFilter; + const lb = $('#px-lost'); lb.textContent = ''; + if (!list.length) lb.append(Pbx.empty(4, 'Всем, кого пропустили, перезвонили или они дозвонились сами.')); + for (const l of list.slice(0, 100)) { + const tr = el('tr'); + const ph = el('td'); + ph.append(el('a', { href: 'tel:+' + String(l.client).replace(/\D/g, ''), textContent: l.client })); + const ch = el('td'); ch.append(Pbx.chip(l.channel)); + tr.append(ph, ch, el('td', { textContent: pxDT.format(new Date(l.last_call)) }), el('td', { textContent: l.attempts })); + lb.append(tr); + } + }, + + // меню строки канала: кому перезвонить по каналу, номера и каналы + menu(btn, c) { + document.querySelector('.px-pop')?.remove(); + const pop = el('div', { className: 'px-pop' }); + pop.setAttribute('role', 'menu'); + const item = (text, fn) => { + const b = el('button', { type: 'button', textContent: text }); + b.setAttribute('role', 'menuitem'); + b.onclick = () => { pop.remove(); fn(); }; + pop.append(b); + }; + item(`Кому перезвонить${c.missed_lost ? ` (${c.missed_lost})` : ''}`, () => { + Pbx.lostFilter = c.channel; Pbx.renderLost(); + $('#px-lost-card').scrollIntoView({ behavior: 'smooth', block: 'start' }); + }); + item('Номера клиники и каналы', () => $('#px-lines-card').scrollIntoView({ behavior: 'smooth', block: 'start' })); + btn.parentElement.append(pop); + pop.querySelector('button').focus(); + const close = (e) => { if (!pop.contains(e.target)) { pop.remove(); document.removeEventListener('click', close); } }; + setTimeout(() => document.addEventListener('click', close)); + pop.addEventListener('keydown', (e) => { if (e.key === 'Escape') { pop.remove(); btn.focus(); } }); + }, + + async loadLines() { + let r; + try { r = await api(ReportClinic.q('/admin/pbx/lines')); } catch (e) { toast(e.message, true); return; } + Pbx.channels = r.meta?.channels || []; + const tb = $('#px-lines'); tb.textContent = ''; + if (!r.data.length) tb.append(Pbx.empty(4, 'Номера появятся после первой загрузки звонков.')); + for (const l of r.data) { + const tr = el('tr'); + const sel = el('select'); + sel.setAttribute('aria-label', 'Канал номера ' + (l.via || l.via_key)); + sel.append(el('option', { value: '', textContent: '— не учитывать —' })); + for (const c of Pbx.channels) sel.append(el('option', { value: c.key, textContent: c.title })); + if (l.channel && !Pbx.channels.some((c) => c.key === l.channel)) sel.append(el('option', { value: l.channel, textContent: l.channel })); + sel.value = l.channel; + sel.onchange = async () => { + try { + await api(ReportClinic.q('/admin/pbx/lines'), { method: 'PUT', body: JSON.stringify({ via_key: l.via_key, channel: sel.value }) }); + toast(sel.value ? 'Канал номера сохранён' : 'Номер больше не учитывается'); + Pbx.loadReport(); Pbx.loadStatus(); + } catch (e) { toast(e.message, true); } + }; + const ch = el('td'); ch.append(sel); + tr.append(el('td', { textContent: l.via || l.via_key }), el('td', { className: 'muted', textContent: (l.user_names || []).join(', ') || '—' }), + el('td', { textContent: l.calls }), ch); + tb.append(tr); + } + }, + + // канал цветом — те же цвета, что в «Истории отчётов»; основной номер — как колл-центр + chip(name) { + const [color, , soft] = name === 'Основной номер' ? RH_CHANNEL_COLORS['колл-центр'] : rhChannelColor(name); + const s = el('span', { className: 'px-ch', textContent: name }); + s.style.setProperty('--ch', color); + s.style.setProperty('--ch-soft', soft); + return s; + }, + + empty(cols, text) { + const tr = el('tr'); + tr.append(el('td', { colSpan: cols, className: 'muted dr-empty', textContent: text })); + return tr; + }, +}; +Pbx.ranges = [new PxRange($('#px-range')), new PxRange($('#px-sync-range'))]; + +$('#px-check').onclick = () => Pbx.check(); +$('#px-sync').onclick = () => Pbx.sync(); +$('#px-show').onclick = () => Pbx.loadReport(); +$('#px-lost-all').onclick = () => { Pbx.lostFilter = ''; Pbx.renderLost(); }; +$('#px-copy').onclick = async () => { + try { await navigator.clipboard.writeText(Pbx.status.domain); toast('Адрес АТС скопирован'); } catch { toast('Не удалось скопировать', true); } +}; diff --git a/internal/api/adminui/reports-history.css b/internal/api/adminui/reports-history.css new file mode 100644 index 0000000..877f3b5 --- /dev/null +++ b/internal/api/adminui/reports-history.css @@ -0,0 +1,134 @@ +/* Оформление только вкладки истории: светлое стекло и цветные акценты. */ +body.reports-history-view { + background: radial-gradient(ellipse at 90% 14%, #f9e6f2 0, transparent 32%), radial-gradient(ellipse at 40% 0, #e1f5ff 0, transparent 50%), #f0f7ff; +} +body.reports-history-view .top .search input, +body.reports-history-view .round { background: #fff; } +#tab-reports.rp-history-view { --rp-ink: #142447; --rp-soft: #62799e; --rp-line: #e1edf9; color: var(--rp-ink); } +.rp-history-view .rp-waves { display: none; } +.rp-history-view .rp-head { margin: 22px 0 18px; } +.rp-history-view .rp-head h1 { font-family: var(--font); font-size: 34px; font-weight: 700; letter-spacing: -.035em; } +.rp-history-view .rp-tabs { border: 1px solid #fff; border-radius: 16px; padding: 4px; box-shadow: 0 5px 14px #638cb016; margin-bottom: 22px; } +.rp-history-view .rp-tabs button { border-radius: 12px; padding: 12px 24px; } +.rp-history-view .rp-tabs button[aria-selected="true"] { background: linear-gradient(135deg, #d7eeff, #bfe2ff); color: #087ff2; box-shadow: 0 4px 9px #419bdf20; } +[data-rp-view="history"] { --rh-line: #e4edf8; --rh-muted: #61789a; } +[data-rp-view="history"] svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.rh-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 24px; flex-wrap: wrap; } +.rh-toolbar input, .rh-toolbar select, .rh-selection select { background: #ffffffed; border: 1px solid #e0eaf7; border-radius: 12px; height: 44px; color: #29446c; font-size: 13px; } +.rh-search { display: flex; align-items: center; flex: 1 1 230px; background: #ffffffed; border: 1px solid #e0eaf7; border-radius: 12px; padding-left: 14px; color: #6586ab; } +.rh-search > span { display: flex; } +.rh-search input { border: 0; background: none; min-width: 0; padding-left: 10px; } +.rh-search:focus-within, .rh-dates:focus-within { outline: 2px solid #6caaf0; outline-offset: 2px; } +.rh-search input:focus-visible, .rh-dates input:focus-visible { outline: none; box-shadow: none; } +.rh-toolbar select { width: auto; min-width: 155px; } +.rh-dates { display: flex; align-items: center; gap: 5px; padding: 0 12px; background: #fff; border: 1px solid #e0eaf7; border-radius: 12px; color: #637d9c; } +.rh-dates > span:first-child { display: flex; margin-right: 4px; } +.rh-dates input { width: 128px; border: 0; padding: 6px 0; font-size: 12px; } +.rh-icon-button { display: grid; place-items: center; width: 44px; height: 44px; border: 1px solid #e0eaf7; background: #fff; border-radius: 12px; color: #3d6497; cursor: pointer; } +.rh-icon-button span { display: flex; } +.rh-icon-button:disabled { opacity: .5; cursor: wait; } +.rh-reset { padding: 8px; color: #367cb8; font-size: 12px; background: none; border: 0; cursor: pointer; } +.rh-feedback { padding: 12px 16px; border-radius: 12px; color: #9b4e36; background: #fff0e6; } +.rh-selection { display: flex; align-items: center; gap: 20px; margin: 0 0 18px; } +.rh-selection label { font-weight: 600; font-size: 16px; white-space: nowrap; } +.rh-selection select { flex: 1; min-width: 0; max-width: 620px; text-overflow: ellipsis; } +.rh-download { margin-left: auto; display: inline-flex; align-items: center; justify-content: center; gap: 9px; min-height: 44px; padding: 11px 20px; border: 0; border-radius: 14px; background: linear-gradient(115deg, #7268ff 0%, #289cff 47%, #25d6d4 100%); box-shadow: 0 7px 16px #5488fa27; color: #fff; text-decoration: none; cursor: pointer; font-size: 13px; font-weight: 500; white-space: nowrap; } +.rh-download:hover { filter: brightness(1.04); color: #fff; } +.rh-download > span { display: flex; } +.rh-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; margin-bottom: 16px; } +.rh-kpi { display: flex; align-items: center; gap: 14px; min-width: 0; padding: 20px 18px; border: 1px solid #fff; border-radius: 20px; background: #ffffffed; box-shadow: 0 7px 25px #7d9abc08; } +.rh-kpi-icon { display: grid; place-items: center; flex: 0 0 50px; height: 50px; border-radius: 15px; color: #108cf3; background: #e7f5ff; } +.rh-kpi-icon svg { width: 26px; height: 26px; } +.rh-kpi--violet .rh-kpi-icon { background: #f0eaff; color: #9861f5; } +.rh-kpi--mint .rh-kpi-icon { background: #dffafa; color: #00b4a4; } +.rh-kpi--peach .rh-kpi-icon { background: #fff4e4; color: #ffad30; } +.rh-kpi > div { min-width: 0; } +.rh-kpi strong { display: block; font-size: clamp(18px, 1.6vw, 25px); line-height: 1.2; font-weight: 700; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; } +.rh-kpi div > span { display: block; color: #4b6891; font-size: 12px; margin-top: 6px; } +.rh-kpi small { display: block; color: #8b9db5; font-size: 10px; margin-top: 5px; line-height: 1.4; } +.rh-card { min-width: 0; padding: 23px; background: #fffffff2; border: 1px solid #fff; border-radius: 23px; box-shadow: 0 10px 28px #81a1c309; } +.rh-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 20px; } +.rh-card h2 { margin: 0; font-size: 18px; font-weight: 650; color: #192b4b; } +.rh-card h3 { margin: 0 0 14px; font-size: 14px; font-weight: 600; } +.rh-tag { color: #6a8fb7; font-size: 11px; } +.rh-result { margin-bottom: 16px; } +.rh-result-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 36px; align-items: center; } +.rh-conversion { display: flex; align-items: center; gap: 25px; min-width: 0; } +.rh-donut { position: relative; isolation: isolate; width: 174px; height: 174px; flex: 0 0 174px; display: flex; align-items: center; justify-content: center; flex-direction: column; border-radius: 50%; background: conic-gradient(from -90deg, #21c7ef 0%, #5387ff 22%, #c07aff 46%, #ff8cc2 70%, #ffba6d 88%, #80e9e5 100%); } +.rh-donut::before { content: ''; position: absolute; inset: 0; z-index: -1; border-radius: 50%; background: conic-gradient(from -90deg, transparent 0 var(--rh-progress), #edf4fb var(--rh-progress) 100%); } +.rh-donut::after { content: ''; position: absolute; inset: 16px; z-index: -1; border-radius: 50%; background: #fff; box-shadow: 0 2px 8px #acc5dc14; } +.rh-donut strong { font-size: 29px; font-weight: 700; letter-spacing: -.03em; } +.rh-donut span { color: #6780a2; font-size: 12px; } +.rh-facts { flex: 1; min-width: 0; } +.rh-facts > div, .rh-check-row { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; padding: 10px 12px; margin-bottom: 5px; border-radius: 12px; background: linear-gradient(90deg, #f3f8fe, #ecf6ff); font-size: 13px; } +.rh-facts span, .rh-check-row span { color: #5d79a0; } +.rh-facts strong, .rh-check-row strong { font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; } +.rh-control { padding-left: 30px; border-left: 1px solid #e4effa; } +.rh-check-row { padding: 8px 0; background: none; border-bottom: 1px solid #eef3fa; border-radius: 0; } +.rh-check-note { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; font-size: 11px; padding: 10px 12px; border-radius: 12px; background: #f0f5fb; color: #69809e; } +.rh-check-note svg { width: 17px; height: 17px; flex-shrink: 0; } +.rh-check-note--ok { color: #139e89; background: #e7fbf6; } +.rh-check-note--warning { color: #b16a20; background: #fff4e4; } +.rh-rankings { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; } +.rh-ranking { list-style: none; padding: 0; margin: 0; } +.rh-rank { display: flex; align-items: flex-start; gap: 14px; margin-top: 16px; --rank-start: #198fff; --rank-end: #3cd7e7; --rank-soft: #e1f3ff; } +.rh-rank-number { flex: 0 0 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; background: var(--rank-soft); color: var(--rank-start); font-size: 16px; font-weight: 600; } +.rh-rank-data { flex: 1; min-width: 0; } +.rh-rank-title { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; font-size: 12px; } +.rh-rank-title span { overflow-wrap: anywhere; } +.rh-rank-title strong { white-space: nowrap; font-weight: 600; } +.rh-track { background: #eaf2fa; height: 7px; border-radius: 9px; margin: 7px 0 4px; overflow: hidden; } +.rh-track i { display: block; height: 100%; background: linear-gradient(90deg, var(--rank-start), var(--rank-end)); border-radius: inherit; } +.rh-rank small { font-size: 10px; color: #879bb6; } +.rh-ranking-empty { color: #7188a7; padding: 30px 0; font-size: 13px; } +.rh-note { margin: 12px 0 18px; font-size: 11px; line-height: 1.55; color: #7f93af; } +.rh-history-card { margin: 0; border: 1px solid #fff; box-shadow: 0 10px 28px #81a1c309; background: #fffffff2; } +.rh-history-card .rp-recent__head { align-items: center; margin-bottom: 15px; } +.rh-history-card .rh-note { margin: 4px 0 0; } +.rh-history-card .rp-table th { background: #eff6fd; color: #6580a3; font-weight: 500; } +.rh-history-card .rp-table td { border-color: #edf3fa; color: #42618a; } +.rh-history-card .rp-table .dr-name { color: #284976; } +.rh-history-card .rp-table .bn-edit { color: #198afa; background: #eef7ff; border-color: #dfedff; } +.rh-retention { font-size: 11px; color: #8ca1ba; } +.rh-empty { text-align: center; padding: 40px 24px; margin-bottom: 20px; } +.rh-empty > span { display: inline-grid; place-items: center; width: 64px; height: 64px; background: #e9f4ff; color: #4697ed; border-radius: 20px; margin-bottom: 16px; } +.rh-empty > span svg { width: 32px; height: 32px; } +.rh-empty p { font-size: 13px; color: #6a81a0; } +.rh-empty .rh-download { margin: 10px auto 0; } +@media (max-width: 1200px) { + .rh-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .rh-result-grid { gap: 22px; } + .rh-conversion { gap: 16px; } + .rh-donut { width: 144px; height: 144px; flex-basis: 144px; } + .rh-control { padding-left: 22px; } + .rh-selection { flex-wrap: wrap; gap: 12px; } + .rh-selection select { max-width: none; } +} +@media (max-width: 960px) { + .rh-result-grid { grid-template-columns: 1fr; } + .rh-control { padding: 20px 0 0; border-left: 0; border-top: 1px solid #e4effa; } + .rh-selection > div { flex-basis: 100%; } +} +@media (max-width: 600px) { + .rp-history-view .rp-head h1 { font-size: 28px; } + .rh-toolbar { gap: 8px; } + .rh-search { flex-basis: 100%; } + .rh-toolbar select { flex: 1; } + .rh-dates { order: 2; width: 100%; justify-content: space-between; } + .rh-dates input { width: 42%; } + .rh-selection select { width: 100%; flex-basis: 100%; } + .rh-download { margin-left: 0; } + .rh-kpis { gap: 10px; } + .rh-kpi { flex-direction: column; align-items: flex-start; gap: 12px; padding: 16px; } + .rh-kpi-icon { flex-basis: auto; width: 40px; height: 40px; border-radius: 12px; } + .rh-kpi strong { font-size: 21px; } + .rh-kpi small { font-size: 9px; } + .rh-card { padding: 18px; } + .rh-card-head { align-items: flex-start; } + .rh-card h2 { font-size: 16px; } + .rh-tag { text-align: right; } + .rh-conversion { flex-direction: column; } + .rh-facts { width: 100%; } + .rh-rankings { grid-template-columns: 1fr; } + .rh-history-card .rp-recent__head { flex-wrap: wrap; } +} diff --git a/internal/api/adminui/reports-history.js b/internal/api/adminui/reports-history.js new file mode 100644 index 0000000..a2c7234 --- /dev/null +++ b/internal/api/adminui/reports-history.js @@ -0,0 +1,158 @@ +/* История: показатели одной обработанной книги, без суммирования разных выгрузок. */ +const RH_ICONS = { + search: '', + calendar: '', + channels: '', + wallet: '', + receipt: '', +}; +const rhIcon = (name) => ``; +const rhNumber = (value, digits = 0) => typeof value === 'number' && Number.isFinite(value) + ? value.toLocaleString('ru-RU', { maximumFractionDigits: digits }) : '—'; +const rhMoney = (value) => rhNumber(value) + (typeof value === 'number' && Number.isFinite(value) ? ' ₽' : ''); +const rhPercent = (value) => rhNumber(value, 1) + (typeof value === 'number' && Number.isFinite(value) ? '%' : ''); + +// Цвет закреплён за каналом, а не за местом в рейтинге: в обоих списках канал одного цвета. +const RH_CHANNEL_COLORS = { + 'колл-центр': ['#198fff', '#3cd7e7', '#e1f3ff'], + 'инстаграм': ['#ff8a1f', '#ffc36b', '#fff0dc'], + 'яндекс': ['#ee2d2d', '#ff8a80', '#ffe4e2'], + '2гис': ['#1fae4b', '#7bdc8f', '#e2f7e7'], + 'сайт': ['#7261ff', '#c892ff', '#ede9ff'], + 'продокторов': ['#04b9a8', '#60e5d7', '#dcfbf4'], + 'zoon': ['#f838ad', '#ffa0d9', '#ffe5f4'], + 'напоправку': ['#3f51d8', '#8a9bff', '#e6e9ff'], + 'сберздоровье': ['#0aa6d6', '#6fd8f5', '#dff5fc'], + 'баннер': ['#d4a300', '#f5d75e', '#fdf6d6'], + 'докту': ['#5b6b82', '#a3b1c4', '#eef1f5'], + 'тв': ['#b0359f', '#e58ad6', '#fbe6f7'], + 'маршрутки': ['#8d6e63', '#c7a89c', '#f3ebe8'], +}; +const RH_EXTRA_COLORS = [['#6d4cff', '#b3a2ff', '#eeeaff'], ['#00897b', '#6cc9bd', '#e0f4f1'], ['#c2185b', '#f07aa6', '#fce4ec']]; +function rhChannelColor(name) { + const n = String(name || '').toLowerCase(); + for (const [key, color] of Object.entries(RH_CHANNEL_COLORS)) if (n.startsWith(key)) return color; + let h = 0; + for (const ch of n) h = (h * 31 + ch.charCodeAt(0)) >>> 0; + return RH_EXTRA_COLORS[h % RH_EXTRA_COLORS.length]; +} + +const ReportsHistory = { + selected: null, + + render() { + const query = $('#rh-search').value.trim().toLocaleLowerCase('ru-RU'); + const status = $('#rh-status').value; + const from = $('#rh-from').value, to = $('#rh-to').value; + const invalidRange = from && to && from > to; + const message = invalidRange ? 'Дата начала должна быть не позже даты окончания.' + : Reports.loadError ? 'Не удалось обновить историю. ' + Reports.loadError : ''; + $('#rh-feedback').textContent = message; + $('#rh-feedback').hidden = !message; + const list = Reports.list.filter((r) => { + const date = new Date(r.created_at); + const day = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; + const text = `${RP_KINDS[r.kind]?.title || r.title} ${r.source_name || ''} ${r.result_name || ''}`.toLocaleLowerCase('ru-RU'); + return !invalidRange && (!query || text.includes(query)) && (!status || r.status === status) + && (!from || day >= from) && (!to || day <= to); + }); + Reports.renderTable('#rp-history', list); + $('#rh-count').textContent = `Показано ${list.length} из ${Reports.list.length}`; + if (!list.length) $('#rp-history td').textContent = Reports.loadError ? 'История недоступна. Попробуйте обновить страницу.' + : Reports.list.length ? 'По выбранным условиям отчёты не найдены.' : 'Отчётов пока нет. Загрузите книгу, чтобы сформировать первый.'; + + const ready = list.filter((r) => r.kind === 'phone_match' && r.status === 'done' && Array.isArray(r.summary?.channels) && r.summary?.internet); + if (!ready.some((r) => String(r.id) === String(ReportsHistory.selected))) ReportsHistory.selected = ready[0]?.id ?? null; + const select = $('#rh-report'); select.textContent = ''; + const df = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + for (const r of ready) select.append(el('option', { value: String(r.id), textContent: `${r.source_name || r.title} · ${df.format(new Date(r.created_at))} · №${r.id}` })); + select.disabled = ready.length === 0; + if (!ready.length) select.append(el('option', { textContent: 'Нет готовых отчётов по выбранным условиям' })); + else select.value = String(ReportsHistory.selected); + const report = ready.find((r) => String(r.id) === String(ReportsHistory.selected)); + $('#rh-dashboard').hidden = !report; + $('#rh-empty').hidden = !!report; + $('#rh-download').hidden = !report; + if (!report) { + $('#rh-download').removeAttribute('href'); + $('#rh-empty h2').textContent = Reports.loadError ? 'Не удалось загрузить показатели' + : query || status || from || to ? 'Нет готового отчёта по выбранным условиям' : 'Пока нет готового отчёта по каналам'; + $('#rh-empty p').textContent = Reports.loadError ? 'Нажмите «Обновить историю», чтобы повторить загрузку.' + : query || status || from || to ? 'Измените фильтры или создайте новый отчёт по каналам.' : 'После обработки книги здесь появятся показатели и сравнение каналов.'; + return; + } + $('#rh-download').href = `/api/v1/admin/reports/${report.id}/download`; + ReportsHistory.dashboard(report.summary); + }, + + dashboard(s) { + const internet = s.internet, totals = s.totals || {}, control = s.control || {}; + const cards = [ + ['channels', 'Лиды из интернета', rhNumber(internet.leads), 'Без повторов между интернет-каналами', 'blue'], + ['calendar', 'Услуги по базе 1С', rhNumber(totals.services, 1), 'Из выбранной выгрузки', 'violet'], + ['wallet', 'Выручка по базе 1С', rhMoney(totals.revenue), 'Из выбранной выгрузки', 'mint'], + ['receipt', 'Средний чек', rhMoney(totals.avg_check), 'По базе 1С', 'peach'], + ]; + const kpis = $('#rh-kpis'); kpis.textContent = ''; + for (const [icon, label, value, note, color] of cards) { + const card = el('article', { className: `rh-kpi rh-kpi--${color}` }); + card.innerHTML = `${rhIcon(icon)}
`; + card.querySelector('strong').textContent = value; + card.querySelector('div > span').textContent = label; + card.querySelector('small').textContent = note; + kpis.append(card); + } + const donut = $('#rh-donut'); donut.textContent = ''; + const hasConversion = Number.isFinite(internet.conv); + donut.style.setProperty('--rh-progress', hasConversion ? Math.max(0, Math.min(100, internet.conv)) + '%' : '0%'); + donut.append(el('strong', { textContent: rhPercent(internet.conv) }), el('span', { textContent: 'Конверсия' })); + donut.setAttribute('aria-label', `Конверсия интернет-каналов: ${rhPercent(internet.conv)}`); + const facts = $('#rh-internet'); facts.textContent = ''; + facts.append(el('h3', { textContent: 'Интернет-каналы' })); + for (const [label, value] of [['Лиды', rhNumber(internet.leads)], ['Услуги', rhNumber(internet.services, 1)], ['Выручка', rhMoney(internet.revenue)]]) { + const row = el('div'); row.append(el('span', { textContent: label }), el('strong', { textContent: value })); facts.append(row); + } + const check = $('#rh-control'); check.textContent = ''; + const hasTotal = control.has_total && Number.isFinite(control.revenue) && Number.isFinite(control.revenue_total); + const diff = hasTotal ? Math.round((control.revenue - control.revenue_total) * 100) / 100 : null; + for (const [label, value] of [['По строкам 1С', rhMoney(control.revenue)], ['Строка «Итого»', hasTotal ? rhMoney(control.revenue_total) : 'Нет в файле'], ['Расхождение', diff === null ? '—' : rhNumber(diff, 2) + ' ₽']]) { + const row = el('div', { className: 'rh-check-row' }); row.append(el('span', { textContent: label }), el('strong', { textContent: value })); check.append(row); + } + const state = diff === null ? 'unknown' : diff === 0 ? 'ok' : 'warning'; + const note = el('p', { className: 'rh-check-note rh-check-note--' + state }); + note.innerHTML = rhIcon(state === 'ok' ? 'check' : 'info'); + note.append(el('span', { textContent: diff === null ? 'В книге нет строки «Итого» для сверки.' : diff === 0 ? 'Выручка совпадает с итогом 1С' : 'Есть расхождение — проверьте выгрузку 1С' })); + check.append(note); + ReportsHistory.ranking('#rh-leads', s.channels, 'leads'); + ReportsHistory.ranking('#rh-revenue', s.channels, 'revenue'); + }, + + ranking(selector, channels, metric) { + const box = $(selector); box.textContent = ''; + const sorted = channels.filter((c) => Number.isFinite(c[metric])).slice().sort((a, b) => b[metric] - a[metric]); + const max = Math.max(0, ...sorted.map((c) => c[metric])); + if (!sorted.length) { box.append(el('li', { className: 'rh-ranking-empty', textContent: 'В этом отчёте нет данных по каналам.' })); return; } + sorted.forEach((c, index) => { + const row = el('li', { className: 'rh-rank' }); + const [start, end, soft] = rhChannelColor(c.name); + row.style.setProperty('--rank-start', start); + row.style.setProperty('--rank-end', end); + row.style.setProperty('--rank-soft', soft); + row.innerHTML = '
'; + row.querySelector('.rh-rank-number').textContent = index + 1; + row.querySelector('.rh-rank-title span').textContent = c.name; + row.querySelector('strong').textContent = metric === 'revenue' ? rhMoney(c.revenue) : rhNumber(c.leads); + row.querySelector('i').style.width = (max > 0 ? Math.max(0, c[metric]) / max * 100 : 0) + '%'; + row.querySelector('small').textContent = metric === 'revenue' ? 'Средний чек: ' + rhMoney(c.avg_check) : 'Конверсия: ' + rhPercent(c.conv); + box.append(row); + }); + }, +}; + +for (const icon of document.querySelectorAll('[data-rh-icon]')) icon.innerHTML = rhIcon(icon.dataset.rhIcon); +$('#rh-search').addEventListener('input', debounce(() => ReportsHistory.render(), 200)); +for (const id of ['rh-status', 'rh-from', 'rh-to']) $('#' + id).onchange = () => ReportsHistory.render(); +$('#rh-report').onchange = () => { ReportsHistory.selected = $('#rh-report').value; ReportsHistory.render(); }; +$('#rh-reset').onclick = () => { for (const id of ['rh-search', 'rh-status', 'rh-from', 'rh-to']) $('#' + id).value = ''; ReportsHistory.render(); }; +$('#rh-refresh').onclick = async () => { $('#rh-refresh').disabled = true; try { await Reports.load(); } finally { $('#rh-refresh').disabled = false; } }; +$('#rh-new').onclick = () => Reports.view('new'); diff --git a/internal/api/adminui/reports.js b/internal/api/adminui/reports.js new file mode 100644 index 0000000..ad52733 --- /dev/null +++ b/internal/api/adminui/reports.js @@ -0,0 +1,515 @@ +/* «Отчёты»: загрузка книги Excel, ход расчёта на сервере, история и шаблоны. + Проценты и время этапов приходят с сервера — это настоящий расчёт, а не анимация. */ + +const RP_KINDS = { + phone_match: { title: 'Отчёт по каналам', file: true, + text: 'Книга «отчёт по каналам»: выгрузка 1С и листы каналов с номерами (все номера, сайт, инстаграм, яндекс, 2гис…). Лиды, пациенты, конверсия, услуги, выручка и средний чек по каждому каналу — как на листе «ОТЧЁТ».' }, +}; +const RP_STAGES = [ + ['Загрузка файла', 'Файл принят сервером'], + ['Проверка данных', 'Читаем листы книги и находим выгрузку 1С'], + ['Обработка данных', 'Сопоставляем номера 1С с каналами'], + ['Формирование отчёта', 'Собираем таблицу для Excel'], + ['Завершение', 'Сохраняем результат'], +]; +const RP_STATUS = { running: ['В обработке', 'sky'], done: ['Готов', ''], failed: ['Ошибка', 'off'], cancelled: ['Отменён', 'grey'] }; +const RP_ICONS = { + doc: '', + file: '', + refresh: '', + play: '', + clock: '', + info: '', + x: '', + down: '', + dots: '', + check: '', +}; +const rpIcon = (n) => ``; +const rpSize = (b) => (b >= 1 << 20 ? (b / (1 << 20)).toFixed(1).replace('.', ',') + ' МБ' : Math.max(1, Math.round(b / 1024)) + ' КБ'); +const rpDur = (ms) => (ms < 1000 ? '0,' + String(Math.max(1, Math.round(ms / 10))).padStart(2, '0') + ' с' + : ms < 60000 ? (ms / 1000).toFixed(1).replace('.', ',') + ' с' : new Date(ms).toISOString().slice(11, 19)); +const rpClock = (ms) => new Date(Math.max(0, ms)).toISOString().slice(11, 19); + +const Reports = { + file: null, + filePeriod: null, + periodLoading: false, + periodRequest: 0, + autoOpenReport: null, + current: null, // задание, которое показывает правая карточка + list: [], + timer: null, + poll: null, + + view(name) { + // Temporarily hidden tabs stay unreachable through secondary UI actions. + const tab = [...document.querySelectorAll('#rp-tabs button')].find((b) => b.dataset.view === name); + if (!tab || tab.hidden) name = 'new'; + $('#tab-reports').classList.toggle('rp-history-view', name === 'history'); + document.body.classList.toggle('reports-history-view', name === 'history' && location.hash === '#reports'); + const heads = { new: 'Отчёты', history: 'История отчётов', pbx: 'Звонки АТС', weekly: WeeklyDashboard.mode === 'months' ? 'Дашборд по месяцам' : 'Дашборд по неделям' }; + $('#tab-reports .rp-head h1').textContent = heads[name]; + for (const b of document.querySelectorAll('#rp-tabs button')) b.setAttribute('aria-selected', String(b.dataset.view === name && (!b.dataset.mode || b.dataset.mode === WeeklyDashboard.mode))); + for (const v of document.querySelectorAll('[data-rp-view]')) v.hidden = v.dataset.rpView !== name; + if (name === 'history') ReportsHistory.render(); + if (name === 'pbx') Pbx.open(); + if (name === 'weekly') WeeklyFullscreen.open(); + }, + + async open() { + await Reports.load(); + const running = Reports.list.find((r) => r.status === 'running'); + if (running) Reports.watch(running.id); + else if (!Reports.current) Reports.renderProgress(null); + }, + + async load() { + try { + const r = await api(ReportClinic.q('/admin/reports')); + Reports.list = r.data || []; + Reports.loadError = ''; + } catch (e) { Reports.loadError = e.message; toast(e.message, true); } + Reports.renderTable('#rp-recent', Reports.list.slice(0, 5)); + if (!$('[data-rp-view="history"]').hidden) ReportsHistory.render(); + }, + + /* ---------- файл ---------- */ + setFile(f) { + if (!f) return; + const ext = (f.name.split('.').pop() || '').toLowerCase(); + if (!['xlsx', 'csv'].includes(ext)) { toast('Нужен файл .xlsx или .csv', true); return; } + if (f.size > 10 * 1024 * 1024) { toast('Файл больше 10 МБ', true); return; } + Reports.file = f; + Reports.renderDrop(); + Reports.detectFilePeriod(f); + }, + async detectFilePeriod(file) { + const request = ++Reports.periodRequest; + Reports.filePeriod = null; + Reports.periodLoading = true; + const hint = $('#rp-file-period'); + hint.hidden = false; + hint.dataset.state = 'loading'; + hint.textContent = 'Читаем период из строки 2 файла 1С…'; + Reports.syncStart(); + try { + const data = new FormData(); data.append('file', file); + const response = await fetch('/api/v1/admin/reports/period', {method: 'POST', body: data, credentials: 'same-origin'}); + const result = await response.json(); + if (!response.ok) throw new Error(result.error?.message || 'Не удалось прочитать период'); + if (request !== Reports.periodRequest) return; + Reports.filePeriod = result.data; + hint.dataset.state = 'ready'; + hint.textContent = `${result.data.mode === 'months' ? 'Месячный' : 'Недельный'} отчёт · ${ReportClinic.title} · ${result.data.label}`; + Reports.renderPartners(); + } catch (error) { + if (request !== Reports.periodRequest) return; + hint.dataset.state = 'warning'; + hint.textContent = `${error.message}. В строке 2 файла должна быть запись «Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ».`; + } finally { + if (request === Reports.periodRequest) { + Reports.periodLoading = false; + Reports.syncStart(); + } + } + }, + openDashboard(period) { + WeeklyDashboard.mode = period.mode; + $('#wk-filter-from').value = ''; $('#wk-filter-to').value = ''; + Reports.view('weekly'); + }, + renderDrop() { + const f = Reports.file, box = $('#rp-drop'); + box.classList.toggle('has-file', !!f); + if (f) { + box.innerHTML = `${rpIcon('doc')}Файл загружен + ${rpIcon('check')} + ${rpSize(f.size)} · ${f.name.split('.').pop().toUpperCase()}`; + box.querySelector('.rp-drop__name span').textContent = f.name; + } else { + box.innerHTML = `${rpIcon('doc')}Перетащите книгу Excel сюда + или нажмите, чтобы выбрать файл · .xlsx или .csv до 10 МБ`; + } + $('#rp-replace').hidden = !f; + Reports.syncStart(); + }, + // Необязательные файлы кабинетов: сервер сам их скачать не может (кабинеты отвечают 403 с VPS). + sber: null, + prodoc: [], + setSber(f) { + if (!f) return; + if (!/\.xls$/i.test(f.name)) { toast('СберЗдоровье: нужен файл .xls из кабинета', true); return; } + Reports.sber = f; + Reports.renderPartners(); + }, + addProdoc(list) { + for (const f of list) { + if (!/\.xlsx$/i.test(f.name)) { toast('ПроДокторов: нужен файл .xlsx из кабинета', true); continue; } + if (Reports.prodoc.some((x) => x.name === f.name && x.size === f.size)) continue; + if (Reports.prodoc.length >= 6) { toast('ПроДокторов: не больше 6 файлов (на месяц — онлайн-запись и телефонная запись)', true); break; } + Reports.prodoc.push(f); + } + Reports.renderPartners(); + }, + renderPartners() { + const draw = (box, files, remove) => { + box.replaceChildren(...files.map((f, i) => { + const row = el('div', { className: 'rp-partner__file' }); + row.append(el('span', { textContent: `${f.name} · ${rpSize(f.size)}` })); + const x = el('button', { type: 'button', title: 'Убрать файл', textContent: '×' }); + x.setAttribute('aria-label', 'Убрать файл ' + f.name); + x.onclick = () => remove(i); + row.append(x); + return row; + })); + }; + draw($('#rp-sber-files'), Reports.sber ? [Reports.sber] : [], () => { Reports.sber = null; Reports.renderPartners(); }); + draw($('#rp-prodoc-files'), Reports.prodoc, (i) => { Reports.prodoc.splice(i, 1); Reports.renderPartners(); }); + $('#rp-sber-add').textContent = Reports.sber ? 'Заменить файл' : 'Добавить файл'; + // какие месяцы нужны ПроДокторову для найденного в файле 1С периода + const need = $('#rp-prodoc-need'), p = Reports.filePeriod; + const months = []; + if (p && p.from && p.to) { + const names = ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь']; + const [fy, fm] = p.from.split('-').map(Number), [ty, tm] = p.to.split('-').map(Number); + for (let y = fy, m = fm; y < ty || (y === ty && m <= tm); m++) { + if (m > 12) { m = 1; y++; } + months.push(`${names[m - 1]} ${y}`); + if (months.length > 13) break; + } + } + need.textContent = months.length ? 'Для этого периода нужны файлы за: ' + months.join(', ') : ''; + }, + syncStart() { + const running = Reports.current && Reports.current.status === 'running'; + // АТС и Битрикс24 берут даты из файла, поэтому без найденного периода запуск недоступен. + $('#rp-start').disabled = !Reports.file || running || Reports.periodLoading || !Reports.filePeriod; + }, + + /* ---------- запуск и ход ---------- */ + async start(kind = 'phone_match') { + const fd = new FormData(); + fd.append('kind', kind); + fd.append('clinic', ReportClinic.key); + if (RP_KINDS[kind].file) { + if (!Reports.file) { toast('Сначала выберите файл', true); return; } + fd.append('file', Reports.file); + // Всегда весь отчёт: номера АТС МегаФон и Битрикс24 за период из файла. + fd.append('source', 'all'); + fd.append('bitrix', '1'); + if (Reports.sber) fd.append('sber_file', Reports.sber); + for (const f of Reports.prodoc) fd.append('prodoctorov_file', f); + } + $('#rp-start').disabled = true; + try { + const res = await fetch('/api/v1/admin/reports', { method: 'POST', body: fd, credentials: 'same-origin' }); + const j = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(j.error?.message || 'Не удалось запустить отчёт'); + Reports.view('new'); + Reports.autoOpenReport = j.data.id; + Reports.watch(j.data.id); + Reports.load(); + } catch (e) { toast(e.message, true); Reports.syncStart(); } + }, + + // Ссылка «только просмотр» на дашборд выбранной клиники — для маркетолога без входа в админку. + async shareDialog() { + const clinic = ReportClinic.title; + const dlg = el('dialog', { className: 'rp-share' }); + const close = () => { dlg.close(); dlg.remove(); }; + const head = el('h2', { textContent: `Ссылка на дашборд «${clinic}»` }); + const note = el('p', { className: 'muted', textContent: 'По ссылке открывается только дашборд по неделям и месяцам этой клиники, без входа и без права что-либо менять. Данных пациентов там нет. Кто получил ссылку — тот и видит, поэтому отправляйте её только маркетологу.' }); + const state = el('p', { className: 'rp-share__state', textContent: 'Проверяем…' }); + const out = el('div', { className: 'rp-share__out', hidden: true }); + const urlBox = el('input', { type: 'text', readOnly: true, className: 'rp-share__url', ariaLabel: 'Ссылка для маркетолога' }); + const copy = el('button', { type: 'button', className: 'px-btn', textContent: 'Скопировать' }); + copy.onclick = async () => { try { await navigator.clipboard.writeText(urlBox.value); toast('Ссылка скопирована'); } catch { urlBox.select(); } }; + out.append(urlBox, copy, el('p', { className: 'muted', textContent: 'Ссылка показывается один раз — сохраните её сейчас. Потом можно только выпустить новую.' })); + const issue = el('button', { type: 'button', className: 'px-btn' }); + const revoke = el('button', { type: 'button', className: 'bn-edit', textContent: 'Отключить ссылку', hidden: true }); + const done = el('button', { type: 'button', className: 'bn-edit', textContent: 'Закрыть' }); + done.onclick = close; + const actions = el('div', { className: 'rp-share__actions' }); + actions.append(issue, revoke, done); + dlg.append(head, note, state, out, actions); + dlg.addEventListener('cancel', (e) => { e.preventDefault(); close(); }); + document.body.append(dlg); + dlg.showModal(); + const df = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + const show = (info) => { + state.textContent = info.exists + ? `Ссылка выпущена ${df.format(new Date(info.created_at))}` + (info.last_used_at ? `, последний раз открывали ${df.format(new Date(info.last_used_at))}` : ', ещё не открывали') + : 'Ссылки пока нет.'; + issue.textContent = info.exists ? 'Выпустить новую (старая перестанет работать)' : 'Создать ссылку'; + revoke.hidden = !info.exists; + }; + try { show((await api(ReportClinic.q('/admin/share-link'))).data); } catch (e) { state.textContent = e.message; } + issue.onclick = async () => { + issue.disabled = true; + try { + const r = await api(ReportClinic.q('/admin/share-link'), { method: 'POST' }); + urlBox.value = location.origin + r.data.path; + out.hidden = false; + show((await api(ReportClinic.q('/admin/share-link'))).data); + urlBox.select(); + } catch (e) { toast(e.message, true); } finally { issue.disabled = false; } + }; + revoke.onclick = async () => { + if (!confirm('Отключить ссылку? Маркетолог больше не сможет открыть дашборд, пока вы не выпустите новую.')) return; + try { + await api(ReportClinic.q('/admin/share-link'), { method: 'DELETE' }); + out.hidden = true; urlBox.value = ''; + show({ exists: false }); + toast('Ссылка отключена'); + } catch (e) { toast(e.message, true); } + }; + }, + + // Другая клиника: свои отчёты, дашборд и звонки. Выбранные файлы сбрасываются, чтобы книга 1С + // одной клиники не ушла в отчёт другой. + onClinicChange() { + clearTimeout(Reports.poll); + Reports.current = null; + Reports.autoOpenReport = null; + Reports.periodRequest++; + Reports.periodLoading = false; + Reports.file = null; Reports.filePeriod = null; Reports.sber = null; Reports.prodoc = []; + $('#rp-file-period').hidden = true; + Reports.renderDrop(); + Reports.renderPartners(); + Reports.renderProgress(null); + Reports.open(); + if (!$('[data-rp-view="weekly"]').hidden) WeeklyDashboard.load(); + if (!$('[data-rp-view="pbx"]').hidden) Pbx.open(); + }, + + watch(id) { + clearTimeout(Reports.poll); + const tick = async () => { + try { + const r = await api('/admin/reports/' + id); + Reports.current = r.data; + Reports.renderProgress(r.data); + if (r.data.status === 'running') { Reports.poll = setTimeout(tick, 400); return; } + Reports.load(); + if (Reports.autoOpenReport === r.data.id) { + Reports.autoOpenReport = null; + const dashboard = r.data.summary?.dashboard; + if (r.data.status === 'done' && dashboard?.id) Reports.openDashboard(dashboard); + } + } catch (e) { toast(e.message, true); } + }; + tick(); + }, + + async cancel(id) { + try { + await api('/admin/reports/' + id + '/cancel', { method: 'POST' }); + toast('Отчёт отменён'); + } catch (e) { toast(e.message, true); } + if (Reports.current?.id === id) Reports.watch(id); + Reports.load(); + }, + + renderProgress(r) { + clearInterval(Reports.timer); + const card = $('#rp-progress'); + const st = r ? r.status : 'idle'; + card.dataset.state = st; + const pct = r ? (st === 'done' ? 100 : r.progress) : 0; + const titles = { idle: 'Отчёт ещё не запущен', running: 'Выполняется отчёт', done: 'Отчёт готов', failed: 'Отчёт не сформирован', cancelled: 'Отчёт отменён' }; + const subs = { + idle: 'Загрузите книгу слева и нажмите «Запустить отчёт».', + running: 'Идёт обработка данных на сервере — процент показывает, сколько строк уже обработано.', + done: r ? `${RP_KINDS[r.kind]?.title || r.title}${r.source_name ? ' · ' + r.source_name : ''}` : '', + failed: r?.error || 'Расчёт остановился с ошибкой.', + cancelled: 'Расчёт остановлен, результат не сохранён.', + }; + $('#rp-title').textContent = titles[st]; + $('#rp-sub').textContent = subs[st]; + // кольцо + const C = 2 * Math.PI * 70; + $('#rp-ring-bar').setAttribute('stroke-dasharray', `${(pct / 100) * C} ${C}`); + $('#rp-ring-bar').style.opacity = pct ? 1 : 0; // скруглённый кончик полосы виден и при 0% + $('#rp-pct').textContent = pct + '%'; + $('#rp-pct-label').textContent = st === 'done' ? 'Готово' : st === 'running' ? 'Выполнено' : st === 'idle' ? 'Ожидание' : 'Остановлено'; + // время + const clock = $('#rp-clock'); + clock.hidden = !r; + if (r) { + const t0 = new Date(r.created_at).getTime(); + const upd = () => { + const end = r.finished_at ? new Date(r.finished_at).getTime() : Date.now(); + clock.querySelector('span').textContent = st === 'running' ? 'Прошло ' + rpClock(end - t0) + : 'Заняло ' + (end - t0 < 60000 ? rpDur(end - t0) : rpClock(end - t0)); + }; + upd(); + if (st === 'running') Reports.timer = setInterval(upd, 1000); + } + // этапы + const times = r ? (Array.isArray(r.stages) ? r.stages : []) : []; + const ol = $('#rp-stages'); ol.textContent = ''; + RP_STAGES.forEach(([name, hint], i) => { + let s = 'wait'; + if (r) { + if (i < times.length || st === 'done') s = 'done'; + else if (i === r.stage && st === 'running') s = 'now'; + else if (i === r.stage && st !== 'running') s = 'stop'; + } + const li = el('li', { className: 'rp-stage ' + s }); + li.innerHTML = `${s === 'done' ? rpIcon('check') : ''} +
${name}${s === 'now' ? hint + '…' : s === 'wait' ? 'Ожидание…' : s === 'stop' ? (st === 'failed' ? 'Ошибка на этом этапе' : 'Остановлено') : ''}
+ `; + ol.append(li); + }); + // итоги и действия + const out = $('#rp-result'); out.textContent = ''; + out.hidden = st !== 'done' && st !== 'running'; + if (st === 'running') { + const b = el('button', { type: 'button', className: 'rp-btn-ghost' }); + b.innerHTML = rpIcon('x') + 'Отменить'; + b.onclick = () => Reports.cancel(r.id); + out.append(b); + } + if (st === 'done') { + const a = el('a', { className: 'rp-download', href: `/api/v1/admin/reports/${r.id}/download` }); + a.innerHTML = rpIcon('down') + `Скачать отчёт · CSV для Excel · ${rpSize(r.result_size)}`; + out.append(a); + const s = r.summary || {}; + if (s.dashboard?.id) { + const open = el('button', {type: 'button', className: 'rp-download'}); + open.textContent = `Открыть ${s.dashboard.mode === 'months' ? 'месячный' : 'недельный'} отчёт · ${s.dashboard.label}`; + open.onclick = () => Reports.openDashboard(s.dashboard); + out.append(open); + } else if (s.dashboard?.warning) { + out.append(el('p', {className: 'rp-control bad', textContent: s.dashboard.warning})); + } + if (r.kind === 'phone_match' && s.channels) { + const rub = (v) => Math.round(v).toLocaleString('ru-RU') + ' ₽'; + const c = s.control || {}; + const diff = c.has_total ? Math.round((c.revenue - c.revenue_total) * 100) / 100 : null; + out.append(el('p', { className: 'rp-control ' + (diff === 0 ? 'ok' : diff === null ? '' : 'bad'), + textContent: diff === null ? 'Строки «Итого» в выгрузке 1С нет — сверить выручку не с чем.' + : diff === 0 ? `Контроль базы: выручка по строкам 1С совпала с «Итого» — ${rub(c.revenue)}.` + : `Контроль базы: расхождение с «Итого» 1С — ${rub(diff)}. Проверьте выгрузку.` })); + const t = el('table', { className: 'rp-sum' }); + t.innerHTML = 'КаналЛидыПациентовКонверсияВыручкаСредний чек'; + const tb = el('tbody'); + const pctf = (v) => v.toFixed(1).replace('.', ',') + '%'; + for (const x of s.channels.concat([s.internet])) { + const tr = el('tr', { className: x === s.internet ? 'rp-sum__total' : '' }); + tr.append(el('td', { textContent: x.name }), el('td', { textContent: x.leads }), el('td', { textContent: x.patients }), + el('td', { textContent: pctf(x.conv) }), el('td', { textContent: rub(x.revenue) }), el('td', { textContent: rub(x.avg_check) })); + tb.append(tr); + } + t.append(tb); out.append(t); + if (s.bitrix) out.append(el('p', { className: 'rp-control', textContent: + `Номера из Битрикс24: записей ${s.bitrix.records}, с номером ${s.bitrix.phones}` + + (s.bitrix.skipped ? `, не из DANA пропущено ${s.bitrix.skipped}.` : '.') })); + if (s.prodoctorov) out.append(el('p', { className: 'rp-control', textContent: + `ПроДокторов: записей (онлайн и по телефону) по дате создания ${s.prodoctorov.records}, уникальных номеров до исключения заглушек ${s.prodoctorov.phones}.` })); + if (s.sberhealth) out.append(el('p', { className: 'rp-control', textContent: + `СберЗдоровье: записей по дате создания ${s.sberhealth.records}, уникальных номеров до исключения заглушек ${s.sberhealth.phones}; строк выгрузки проверено ${s.sberhealth.export_records}.` })); + } + } + Reports.syncStart(); + }, + + /* ---------- таблицы ---------- */ + renderTable(sel, list) { + const tb = $(sel); tb.textContent = ''; + if (!list.length) { + const tr = el('tr'); tr.append(el('td', { colSpan: 5, className: 'muted dr-empty', textContent: 'Отчётов пока нет — запустите первый.' })); tb.append(tr); + return; + } + const df = new Intl.DateTimeFormat('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + for (const r of list) { + const tr = el('tr'); + const name = el('td'); + const nb = el('button', { type: 'button', className: 'dr-name', textContent: RP_KINDS[r.kind]?.title || r.title }); + nb.onclick = () => { Reports.view('new'); Reports.watch(r.id); window.scrollTo({ top: 0, behavior: 'smooth' }); }; + name.append(nb); + const file = el('td', { className: 'rp-file' }); + file.innerHTML = rpIcon('file'); + file.append(r.source_name || r.result_name || '—'); + const [label, cls] = RP_STATUS[r.status] || [r.status, 'grey']; + const st = el('td'); st.append(el('span', { className: 'pill dot ' + cls, textContent: label })); + if (r.status === 'failed' && r.error) st.title = r.error; + const act = el('td', { className: 'dr-actions' }); + if (r.status === 'running') { + const b = el('button', { type: 'button', className: 'bn-edit' }); + b.innerHTML = rpIcon('x') + 'Отменить'; + b.onclick = () => Reports.cancel(r.id); + act.append(b); + } else if (r.status === 'done') { + const a = el('a', { className: 'bn-edit', href: `/api/v1/admin/reports/${r.id}/download` }); + a.innerHTML = rpIcon('down') + 'Скачать'; + act.append(a); + } + const more = el('button', { type: 'button', className: 'icon-btn', title: 'Ещё' }); + more.setAttribute('aria-label', 'Ещё действия с отчётом'); + more.setAttribute('aria-haspopup', 'menu'); + more.innerHTML = rpIcon('dots'); + more.onclick = (e) => { e.stopPropagation(); Reports.menu(r, more); }; + act.append(more); + tr.append(name, file, el('td', { className: 'muted', textContent: df.format(new Date(r.created_at)).replace(',', '') }), st, act); + tb.append(tr); + } + }, + + menu(r, anchor) { + Doctors.closeMenu(); + const m = el('div', { className: 'menu', id: 'dr-menu', role: 'menu' }); + const item = (text, fn, danger) => { + const x = el('button', { type: 'button', role: 'menuitem', className: danger ? 'danger' : '', textContent: text }); + x.onclick = () => { Doctors.closeMenu(); fn(); }; + m.append(x); + }; + item('Показать ход и итоги', () => { Reports.view('new'); Reports.watch(r.id); window.scrollTo({ top: 0, behavior: 'smooth' }); }); + if (r.status !== 'running') item('Удалить отчёт', () => Reports.remove(r), true); + document.body.append(m); + const b = anchor.getBoundingClientRect(); + m.style.top = (b.bottom + window.scrollY + 6) + 'px'; + m.style.left = Math.max(12, b.right + window.scrollX - m.offsetWidth) + 'px'; + m.querySelector('button').focus(); + anchor.setAttribute('aria-expanded', 'true'); + Doctors.menuAnchor = anchor; + }, + + async remove(r) { + if (!confirm('Удалить отчёт «' + (RP_KINDS[r.kind]?.title || r.title) + '»? Файл результата тоже удалится.')) return; + try { + await api('/admin/reports/' + r.id, { method: 'DELETE' }); + toast('Отчёт удалён'); + if (Reports.current?.id === r.id) { Reports.current = null; Reports.renderProgress(null); } + Reports.load(); + } catch (e) { toast(e.message, true); } + }, + +}; + +/* ---------- обработчики ---------- */ +$('#rp-start').insertAdjacentHTML('afterbegin', rpIcon('play')); +$('#rp-replace').insertAdjacentHTML('afterbegin', rpIcon('refresh')); +$('#rp-clock').insertAdjacentHTML('afterbegin', rpIcon('clock')); +$('#rp-start').onclick = () => Reports.start('phone_match'); +$('#rp-sber-add').onclick = () => $('#rp-sber-input').click(); +$('#rp-sber-input').onchange = () => { Reports.setSber($('#rp-sber-input').files[0]); $('#rp-sber-input').value = ''; }; +$('#rp-prodoc-add').onclick = () => $('#rp-prodoc-input').click(); +$('#rp-prodoc-input').onchange = () => { Reports.addProdoc([...$('#rp-prodoc-input').files]); $('#rp-prodoc-input').value = ''; }; +Reports.renderPartners(); +$('#rp-replace').onclick = () => $('#rp-input').click(); +$('#rp-input').onchange = () => { Reports.setFile($('#rp-input').files[0]); $('#rp-input').value = ''; }; +{ + const drop = $('#rp-drop'); + drop.onclick = () => $('#rp-input').click(); + drop.onkeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); $('#rp-input').click(); } }; + drop.addEventListener('dragover', (e) => { e.preventDefault(); drop.classList.add('over'); }); + drop.addEventListener('dragleave', () => drop.classList.remove('over')); + drop.addEventListener('drop', (e) => { e.preventDefault(); drop.classList.remove('over'); Reports.setFile(e.dataTransfer.files[0]); }); +} +for (const b of document.querySelectorAll('#rp-tabs button')) b.onclick = () => b.dataset.mode + ? Reports.openDashboard({mode: b.dataset.mode}) : Reports.view(b.dataset.view); +$('#rp-all').onclick = () => Reports.view('history'); +Reports.renderDrop(); diff --git a/internal/api/adminui/services.js b/internal/api/adminui/services.js new file mode 100644 index 0000000..a75e790 --- /dev/null +++ b/internal/api/adminui/services.js @@ -0,0 +1,278 @@ +/* «Услуги и цены» → «Услуги»: направления раскрываются, внутри — услуги направления. + Поиск и фильтры работают в браузере: услуг меньше сотни. Форма — прежняя editService. */ + +const SV_ICONS = { + 'detskaya-klinika': '', + ginekologiya: '', + oko: '', + estetic: '', + endokrinologiya: '', + urologiya: '', + khirurgiya: '', + mammologiya: '', + osteo: '', + proktologiya: '', + onkologiya: '', + 'meditsinskiy-turizm': '', + _default: '', +}; +const SV_TINTS = ['#fdeaec', '#e7f6ee', '#e5f1fb', '#fdf1e3', '#efeafb', '#e8f5f4']; +const SV_INK = ['#d9546d', '#2f9a6a', '#3a7bd5', '#d8822c', '#7a5cc9', '#2c8f88']; +const svIcon = (d) => ``; + +const Services = { + open: new Set(), + openKey: 'dana-admin:sv-open', + + dirs() { return servicesCache.filter((s) => !s.parent_id); }, + children(id) { return servicesCache.filter((s) => s.parent_id === id); }, + + match(s) { + const q = $('#s-q').value.trim().toLowerCase(); + const st = $('#s-status').value; + return (!q || (s.name + ' /' + s.path).toLowerCase().includes(q)) && + (st !== 'published' || s.published) && (st !== 'hidden' || !s.published) && + (st !== 'no-price' || !s.price_count); + }, + + render() { + const dirFilter = $('#s-dir').value; + const box = $('#s-groups'); box.textContent = ''; + const filtering = $('#s-q').value.trim() || $('#s-status').value; + let found = 0; + Services.dirs().forEach((dir, i) => { + if (dirFilter && String(dir.id) !== dirFilter) return; + const kids = Services.children(dir.id).sort((a, b) => a.sort - b.sort || a.id - b.id); + const shown = kids.filter(Services.match); + if (filtering && !shown.length) return; + found += shown.length; + box.append(Services.group(dir, kids, shown, i, filtering || dirFilter)); + }); + $('#s-count').innerHTML = `Найдено ${found} ${plural(found, 'услуга', 'услуги', 'услуг')}`; + if (!box.children.length) box.append(el('p', { className: 'muted dr-empty', textContent: 'Ничего не нашлось — измените поиск или фильтры.' })); + }, + + group(dir, kids, shown, i, forceOpen) { + const open = forceOpen || Services.open.has(dir.id); + const g = el('section', { className: 'sv-group' + (open ? ' open' : '') }); + const head = el('button', { type: 'button', className: 'sv-head' }); + head.setAttribute('aria-expanded', String(!!open)); + const ic = el('span', { className: 'sv-icon' }); + ic.style.background = SV_TINTS[i % SV_TINTS.length]; + ic.style.color = SV_INK[i % SV_INK.length]; + ic.innerHTML = svIcon(SV_ICONS[dir.slug] || SV_ICONS._default); + const txt = el('span', { className: 'sv-head__text' }); + txt.append(el('b', { textContent: dir.name }), + el('small', { textContent: `${kids.length} ${plural(kids.length, 'услуга', 'услуги', 'услуг')}` + + (dir.published ? '' : ' · направление скрыто') })); + const chev = el('span', { className: 'sv-chev' }); + chev.innerHTML = svIcon(''); + head.append(ic, txt, chev); + head.onclick = () => { + if (Services.open.has(dir.id)) Services.open.delete(dir.id); else Services.open.add(dir.id); + try { localStorage.setItem(Services.openKey, JSON.stringify([...Services.open])); } catch { /* приватный режим */ } + Services.render(); + }; + const editDir = el('button', { type: 'button', className: 'icon-btn sv-dir-edit', title: 'Изменить направление' }); + editDir.setAttribute('aria-label', 'Изменить направление «' + dir.name + '»'); + editDir.innerHTML = svIcon(BN_ICONS.pencil); + editDir.onclick = () => editService(dir); + const top = el('div', { className: 'sv-top' }); + top.append(head, editDir); + g.append(top); + if (open) { + const wrap = el('div', { className: 'dr-scroll' }); + const t = el('table', { className: 'sv-table' }); + t.innerHTML = 'НазваниеАдресЦена отПозицииПорядокСтатусДействия'; + const tb = el('tbody'); + if (!shown.length) { + const tr = el('tr'); tr.append(el('td', { colSpan: 8, className: 'muted dr-empty', textContent: 'В направлении пока нет услуг.' })); tb.append(tr); + } + const draggable = !$('#s-q').value.trim() && !$('#s-status').value; + for (const s of shown) tb.append(Services.row(s, dir, draggable)); + t.append(tb); wrap.append(t); g.append(wrap); + } + return g; + }, + + row(s, dir, draggable) { + const tr = el('tr'); + const grip = el('td'); + if (draggable) { + const h = el('button', { type: 'button', className: 'grip', title: 'Перетащите, чтобы поменять порядок' }); + h.setAttribute('aria-label', `Переместить «${s.name}». Стрелки вверх и вниз меняют порядок`); + h.innerHTML = svIcon(BN_ICONS.grip); + h.onkeydown = (e) => { + if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return; + e.preventDefault(); + Services.move(dir.id, s.id, e.key === 'ArrowUp' ? -1 : 1); + }; + grip.append(h); + tr.draggable = true; + tr.addEventListener('dragstart', (e) => { Services.drag = { dir: dir.id, id: s.id }; tr.classList.add('dragging'); e.dataTransfer.effectAllowed = 'move'; }); + tr.addEventListener('dragend', () => { tr.classList.remove('dragging'); Services.drag = null; }); + tr.addEventListener('dragover', (e) => { if (Services.drag?.dir === dir.id && Services.drag.id !== s.id) { e.preventDefault(); tr.classList.add('drop'); } }); + tr.addEventListener('dragleave', () => tr.classList.remove('drop')); + tr.addEventListener('drop', (e) => { + e.preventDefault(); tr.classList.remove('drop'); + const ids = Services.children(dir.id).sort((a, b) => a.sort - b.sort || a.id - b.id).map((x) => x.id); + const from = ids.indexOf(Services.drag.id), to = ids.indexOf(s.id); + ids.splice(to, 0, ids.splice(from, 1)[0]); + Services.reorder(ids); + }); + } + const st = el('td'); + st.append(el('span', { className: 'pill dot ' + (s.published ? '' : 'off'), textContent: s.published ? 'Опубликовано' : 'Скрыто' })); + const act = el('td', { className: 'dr-actions' }); + const edit = el('button', { type: 'button', className: 'bn-edit' }); + edit.innerHTML = svIcon(BN_ICONS.pencil) + 'Изменить'; + edit.onclick = () => editService(s); + const more = el('button', { type: 'button', className: 'icon-btn', title: 'Ещё' }); + more.setAttribute('aria-label', 'Ещё действия: ' + s.name); + more.setAttribute('aria-haspopup', 'menu'); + more.innerHTML = svIcon(BN_ICONS.dots); + more.onclick = (e) => { e.stopPropagation(); Services.menu(s, more); }; + act.append(edit, more); + tr.append(grip, + el('td', { className: 'sv-name', textContent: s.name }), + el('td', { className: 'muted sv-path', textContent: '/' + s.path, title: '/services/' + s.path + '/' }), + el('td', { textContent: s.price_from ? 'от ' + Number(s.price_from).toLocaleString('ru-RU') + ' ₽' : '—' }), + el('td', { textContent: s.price_count || '—' }), + el('td', { className: 'muted', textContent: s.sort }), + st, act); + return tr; + }, + + menu(s, anchor) { + Doctors.closeMenu(); + const m = el('div', { className: 'menu', id: 'dr-menu', role: 'menu' }); + const item = (text, fn) => { + const x = el('button', { type: 'button', role: 'menuitem', textContent: text }); + x.onclick = () => { Doctors.closeMenu(); fn(); }; + m.append(x); + }; + if (s.published) item('Открыть страницу на сайте', () => window.open(`/services/${s.path}/`, '_blank', 'noopener')); + item(s.published ? 'Скрыть с сайта' : 'Показать на сайте', () => Services.save(s, { published: !s.published }, + s.published ? 'Услуга скрыта' : 'Услуга на сайте')); + item('Цены этой услуги', () => { go('prices'); $('#p-q').value = s.name; $('#p-q').dispatchEvent(new Event('input')); }); + document.body.append(m); + const r = anchor.getBoundingClientRect(); + m.style.top = (r.bottom + window.scrollY + 6) + 'px'; + m.style.left = Math.max(12, r.right + window.scrollX - m.offsetWidth) + 'px'; + m.querySelector('button').focus(); + anchor.setAttribute('aria-expanded', 'true'); + Doctors.menuAnchor = anchor; + }, + + body(s, change = {}) { + return { name: s.name, short_name: s.short_name, lead: s.lead, sort: s.sort, published: s.published, + seo_title: s.seo_title, seo_description: s.seo_description, seo_h1: s.seo_h1, ...change }; + }, + async save(s, change, message) { + try { + await api('/admin/services/' + s.id, { method: 'PUT', body: JSON.stringify(Services.body(s, change)) }); + toast(message); + await loadServices(); + } catch (e) { toast(e.message, true); } + }, + async reorder(ids) { + const byId = new Map(servicesCache.map((s) => [s.id, s])); + const changed = ids.map((id, i) => [byId.get(id), (i + 1) * 10]).filter(([s, sort]) => s.sort !== sort); + try { + for (const [s, sort] of changed) await api('/admin/services/' + s.id, { method: 'PUT', body: JSON.stringify(Services.body(s, { sort })) }); + if (changed.length) toast('Порядок услуг сохранён'); + } catch (e) { toast(e.message, true); } + await loadServices(); + }, + async move(dirId, id, dir) { + const ids = Services.children(dirId).sort((a, b) => a.sort - b.sort || a.id - b.id).map((x) => x.id); + const i = ids.indexOf(id), j = i + dir; + if (j < 0 || j >= ids.length) return; + [ids[i], ids[j]] = [ids[j], ids[i]]; + await Services.reorder(ids); + [...document.querySelectorAll('#s-groups .grip')].find((g) => g.getAttribute('aria-label').includes(servicesCache.find((s) => s.id === id).name))?.focus(); + }, + + fillDirs() { + const sel = $('#s-dir'); const keep = sel.value; + sel.textContent = ''; + sel.append(el('option', { value: '', textContent: 'Все направления' })); + for (const d of Services.dirs()) sel.append(el('option', { value: d.id, textContent: d.name })); + sel.value = keep; if (sel.value !== keep) sel.value = ''; + }, + + add() { + const dirs = Services.dirs(); + const pre = $('#s-dir').value; + openDialog('Новая услуга', [ + { key: 'parent_id', label: 'Направление — раздел в адресе страницы', type: 'select', value: pre, + options: [{ value: '', label: '— выберите —' }].concat(dirs.map((d) => ({ value: d.id, label: d.name }))) }, + { key: 'name', label: 'Название (например, «Приём кардиолога»)', value: '' }, + { key: 'lead', label: 'Краткое описание — под заголовком на странице услуги', value: '', type: 'textarea' }, + { key: 'slug', label: 'Адрес латиницей — заполнится сам из названия', value: '' }, + ], async (d) => { + if (!d.slug.trim()) d.slug = toSlug(d.name); + d.parent_id = Number(d.parent_id) || 0; + const r = await api('/admin/services', { method: 'POST', body: JSON.stringify(d) }); + toast('Услуга добавлена скрытой — проверьте её и включите «Опубликована»'); + Services.open.add(d.parent_id); + await loadServices(); + const s = servicesCache.find((x) => x.id === r.data.id); + if (s) setTimeout(() => editService(s), 50); + }); + const inputs = [...$('#dlg-body').querySelectorAll('input')]; + const [name, slug] = [inputs[0], inputs[inputs.length - 1]]; + let touched = false; + slug.addEventListener('input', () => { touched = true; }); + name.addEventListener('input', () => { if (!touched) slug.value = toSlug(name.value); }); + }, + + exportCSV() { + const rows = [['Направление', 'Услуга', 'Адрес на сайте', 'Цена от, ₽', 'Позиций в прайсе', 'Статус']]; + for (const dir of Services.dirs()) { + for (const s of Services.children(dir.id).filter(Services.match)) { + rows.push([dir.name, s.name, `${location.origin}/services/${s.path}/`, s.price_from ?? '', s.price_count, s.published ? 'опубликована' : 'скрыта']); + } + } + const esc = (v) => { const x = String(v ?? ''); return /[";\n]/.test(x) ? '"' + x.replace(/"/g, '""') + '"' : x; }; + const a = el('a', { href: URL.createObjectURL(new Blob(['' + rows.map((r) => r.map(esc).join(';')).join('\r\n')], { type: 'text/csv;charset=utf-8' })), + download: `uslugi-${new Date().toISOString().slice(0, 10)}.csv` }); + document.body.append(a); a.click(); a.remove(); + setTimeout(() => URL.revokeObjectURL(a.href), 1000); + toast('Выгружено: ' + (rows.length - 1) + ' ' + plural(rows.length - 1, 'услуга', 'услуги', 'услуг')); + }, +}; + +async function loadServices() { + const r = await api('/admin/services'); + servicesCache = r.data || []; + if (!Services.initDone) { + Services.initDone = true; + try { for (const id of JSON.parse(localStorage.getItem(Services.openKey) || '[]')) Services.open.add(id); } catch { /* приватный режим */ } + if (!Services.open.size && Services.dirs()[0]) Services.open.add(Services.dirs()[0].id); + } + Services.fillDirs(); + Services.render(); +} + +$('#s-q').addEventListener('input', debounce(() => Services.render(), 200)); +$('#s-dir').onchange = () => Services.render(); +$('#s-status').onchange = () => Services.render(); +$('#s-export').insertAdjacentHTML('afterbegin', svIcon(DR_ICONS.download)); +$('#s-search-icon').innerHTML = svIcon(DR_ICONS.search); +$('#s-export').onclick = () => Services.exportCSV(); + +// общая шапка «Услуги и цены»: кнопка «Добавить» зависит от вкладки +const CATALOG_HERO = { + services: ['Добавить услугу', () => Services.add()], + prices: ['Добавить позицию', () => $('#p-add').click()], + checkups: ['Добавить чек-ап', () => $('#ck-add').click()], + promos: ['Добавить акцию', () => $('#pr-add').click()], +}; +function catalogHero(tab) { + const c = CATALOG_HERO[tab]; + if (!c) return; + $('#cat-add').innerHTML = svIcon(BN_ICONS.plus) + c[0]; + $('#cat-add').onclick = c[1]; +} +$('#ck-search-icon').innerHTML = svIcon(DR_ICONS.search); diff --git a/internal/api/adminui/share.html b/internal/api/adminui/share.html new file mode 100644 index 0000000..0600a97 --- /dev/null +++ b/internal/api/adminui/share.html @@ -0,0 +1,56 @@ + + + + + + + +Маркетинговая аналитика + + + + +
+
+

Маркетинговая аналитика

+
+ + +
+
+
+
+

Загрузка…

+
+
+ — + +
+
+ +
+ +
+ + + + + diff --git a/internal/api/adminui/share.js b/internal/api/adminui/share.js new file mode 100644 index 0000000..1d36f26 --- /dev/null +++ b/internal/api/adminui/share.js @@ -0,0 +1,60 @@ +/* Дашборд по ссылке для маркетолога: та же таблица, что «По неделям» в админке (weekly.js), + только просмотр. Токен — в адресе после «#»: он не уходит на сервер в адресе, в журналы и в Referer; + запрос несёт его в заголовке Authorization. */ +const $ = (s) => document.querySelector(s); +const el = (t, p = {}) => Object.assign(document.createElement(t), p); + +function toast(msg, bad) { + if (!$('#sh-error').hidden) return; // страница ошибки уже всё сказала + const t = $('#toast'); + t.textContent = msg; t.className = 'toast show' + (bad ? ' bad' : ''); + setTimeout(() => { t.className = 'toast'; }, 2600); +} + +// Клиника приходит с сервера вместе с данными: ссылка привязана к одной клинике. +const ReportClinic = { + key: '', + brand: '', + brands: { dana: 'DANA Medical', ochevidno: 'Очевидно' }, + q: (path) => path, +}; + +const Share = { + token: decodeURIComponent(location.hash.slice(1)), + + fail(text) { + $('#sh-main').hidden = true; + $('#sh-error').hidden = false; + if (text) $('#sh-error-text').textContent = text; + }, + + start() { + WeeklyDashboard.readOnly = true; + if (Share.token.length < 40) { Share.fail(); return; } + $('#sh-main').hidden = false; + for (const b of document.querySelectorAll('.sh-head [data-mode]')) { + b.onclick = () => { + WeeklyDashboard.setMode(b.dataset.mode); + for (const x of document.querySelectorAll('.sh-head [data-mode]')) x.setAttribute('aria-selected', String(x === b)); + }; + } + WeeklyDashboard.load(); + }, +}; + +async function api(path) { + if (path !== '/share/weekly-reports') throw new Error('Недоступно по ссылке'); + const r = await fetch('/api/v1/share/weekly-reports', { + headers: { Authorization: 'Bearer ' + Share.token }, + credentials: 'omit', + cache: 'no-store', + }); + const body = await r.json().catch(() => ({})); + if (r.status === 401) { Share.fail(body.error?.message); throw new Error(body.error?.message || 'Ссылка недействительна'); } + if (!r.ok) throw new Error(body.error?.message || 'Ошибка ' + r.status); + ReportClinic.key = body.meta?.clinic || ''; + ReportClinic.brand = ReportClinic.brands[ReportClinic.key] || body.meta?.title || ''; + document.title = `${ReportClinic.brand} — маркетинговая аналитика`; + $('#sh-title').textContent = `${ReportClinic.brand} — маркетинговая аналитика`; + return body; +} diff --git a/internal/api/adminui/shell.js b/internal/api/adminui/shell.js new file mode 100644 index 0000000..a1f18f5 --- /dev/null +++ b/internal/api/adminui/shell.js @@ -0,0 +1,316 @@ +/* Оболочка админки: Sidebar (органическая лента навигации), Topbar (поиск, календарь, + уведомления, профиль) и маршрутизация разделов через #адрес. */ + +const ICONS = { + home: '', + leads: '', + doctors: '', + catalog: '', + content: '', + reviews: '', + settings: '', + stats: '', + reports: '', + logout: '', + search: '', + calendar: '', + bell: '', +}; +const svg = (name) => ``; + +// Разделы ленты. Подразделы — прежние вкладки: их разметка и код не менялись. +const NAV = [ + { key: 'home', label: 'Главная', icon: 'home', tabs: [['home', 'Главная']] }, + { key: 'leads', label: 'Заявки', icon: 'leads', tabs: [['leads', 'Заявки']] }, + { key: 'doctors', label: 'Врачи', icon: 'doctors', tabs: [['doctors-dash', 'Дашборд'], ['doctors', 'Список врачей'], ['onec', 'Запись через 1С']] }, + { key: 'catalog', label: 'Услуги и цены', icon: 'catalog', + tabs: [['services', 'Услуги'], ['prices', 'Прайс'], ['checkups', 'Чек-апы'], ['promos', 'Акции']] }, + { key: 'content', label: 'Контент сайта', icon: 'content', + tabs: [['posts', 'Новости и статьи'], ['banners', 'Слайды на главной'], ['pages', 'Страницы'], ['faq', 'Вопросы'], + ['documents', 'Документы'], ['vacancies', 'Вакансии'], ['partners', 'Партнёры']] }, + { key: 'reviews', label: 'Отзывы', icon: 'reviews', tabs: [['reviews', 'Отзывы']] }, + { key: 'stats', label: 'Статистика', icon: 'stats', tabs: [['stats', 'Статистика']] }, + { key: 'reports', label: 'Отчёты', icon: 'reports', tabs: [['reports', 'Отчёты']] }, + { key: 'settings', label: 'Настройки', icon: 'settings', tabs: [['settings', 'Реквизиты и филиалы'], ['redirects', 'Редиректы']] }, +]; +const TAB_GROUP = Object.fromEntries(NAV.flatMap((g) => g.tabs.map(([t]) => [t, g]))); +// Подзаголовок под названием раздела +const TAB_SUB = { + doctors: 'Управление врачами, их услугами и филиалами', + leads: 'Заявки с форм сайта: запись, обратный звонок, отклик на вакансию', +}; +const TAB_TITLE = Object.fromEntries(NAV.flatMap((g) => g.tabs.map(([t, title]) => [t, g.tabs.length > 1 ? g.label : title]))); + +/* ---------- Sidebar ---------- */ +const Sidebar = { + render() { + const nav = $('#side-nav'); + nav.textContent = ''; + for (const g of NAV) { + const b = el('button', { type: 'button', className: 'side__btn' }); + b.dataset.group = g.key; + b.setAttribute('aria-label', g.label); + b.innerHTML = svg(g.icon) + ``; + if (g.key === 'reviews') b.append($('#rv-badge')); + if (g.key === 'leads') b.append(el('span', { id: 'ld-badge', className: 'side__badge', hidden: true })); + b.onclick = () => go(g.tabs[0][0]); + nav.append(b); + } + $('#logout').setAttribute('aria-label', 'Выйти'); + $('#logout').innerHTML = svg('logout') + ''; + Sidebar.shape(); + window.addEventListener('resize', debounce(Sidebar.shape, 100)); + }, + + // Контур ленты: три «капсулы» (логотип, меню, низ), соединённые плавными перетяжками. + shape() { + const side = $('.side'); + const h = side.clientHeight, w = side.clientWidth; + if (!h || getComputedStyle($('.side__shape')).display === 'none') return; + const logo = $('.side__logo'), nav = $('#side-nav'), foot = $('.side__foot'); + const gap = (a, b) => ({ a, b, m: (a + b) / 2 }); + const n1 = gap(logo.offsetTop + logo.offsetHeight + 14, nav.offsetTop - 14); + const n2 = gap(nav.offsetTop + nav.offsetHeight + 14, foot.offsetTop + 46); + const neck = 17; // насколько сужается лента в перетяжке + const r = w / 2, k = 0.55; + const down = (x0, x1, n) => `L ${x0} ${n.a} C ${x0} ${n.a + (n.m - n.a) * k}, ${x1} ${n.m - (n.m - n.a) * k}, ${x1} ${n.m} ` + + `C ${x1} ${n.m + (n.b - n.m) * k}, ${x0} ${n.b - (n.b - n.m) * k}, ${x0} ${n.b} `; + const up = (x0, x1, n) => `L ${x0} ${n.b} C ${x0} ${n.b - (n.b - n.m) * k}, ${x1} ${n.m + (n.b - n.m) * k}, ${x1} ${n.m} ` + + `C ${x1} ${n.m - (n.m - n.a) * k}, ${x0} ${n.a + (n.m - n.a) * k}, ${x0} ${n.a} `; + const d = `M ${r} 0 A ${r} ${r} 0 0 0 0 ${r} ` + down(0, neck, n1) + down(0, neck, n2) + + `L 0 ${h - r} A ${r} ${r} 0 0 0 ${w} ${h - r} ` + up(w, w - neck, n2) + up(w, w - neck, n1) + + `L ${w} ${r} A ${r} ${r} 0 0 0 ${r} 0 Z`; + $('.side__shape').setAttribute('viewBox', `0 0 ${w} ${h}`); + $('.side__shape path').setAttribute('d', d); + }, + + highlight(tab) { + const g = TAB_GROUP[tab]; + document.querySelectorAll('.side__btn[data-group]').forEach((b) => { + if (b.dataset.group === g.key) b.setAttribute('aria-current', 'page'); else b.removeAttribute('aria-current'); + }); + }, +}; + +// Шапки с фото у разделов с вкладками: кнопка «Добавить» зависит от вкладки и нажимает +// скрытую кнопку самой вкладки. «Услуги и цены» — catalogHero в services.js. +const GROUP_HERO = { + posts: ['Добавить публикацию', '#po-add'], + banners: ['Добавить слайд', '#bn-add'], + faq: ['Добавить вопрос', '#fq-add'], + documents: ['Добавить документ', '#dc-add'], + vacancies: ['Добавить вакансию', '#va-add'], + partners: ['Добавить партнёра', '#pt-add'], + reviews: ['Добавить отзыв', '#rv-add'], +}; +function groupHero(tab) { + const h = document.querySelector('[data-hero-for]:not([hidden])'); + const add = h?.querySelector('[data-hero-add]'); + if (!add) return; + const c = GROUP_HERO[tab]; + add.hidden = !c; + if (!c) return; + add.innerHTML = svIcon(BN_ICONS.plus) + c[0]; + add.onclick = () => $(c[1]).click(); +} + +/* ---------- маршруты ---------- */ +function go(tab) { + if (location.hash.slice(1) !== tab) location.hash = tab; else route(); +} +function route() { + let tab = location.hash.slice(1) || 'home'; + if (!TAB_GROUP[tab]) tab = 'home'; + document.body.classList.toggle('leads-view', tab === 'leads'); + document.body.classList.toggle('reports-history-view', tab === 'reports' && $('#tab-reports').classList.contains('rp-history-view')); + const g = TAB_GROUP[tab]; + document.querySelectorAll('main > section').forEach((s) => { s.hidden = s.id !== 'tab-' + tab; }); + Sidebar.highlight(tab); + const sub = $('#subtabs'); + sub.textContent = ''; + if (g.tabs.length > 1) { + for (const [t, title] of g.tabs) { + const b = el('button', { type: 'button', textContent: title }); + if (t === tab) b.setAttribute('aria-current', 'page'); + b.onclick = () => go(t); + sub.append(b); + } + } + $('#page-title').textContent = tab === 'home' ? '' : TAB_TITLE[tab]; + $('#page-title').hidden = tab === 'home'; + $('#page-sub').textContent = TAB_SUB[tab] || ''; + $('#page-sub').hidden = !TAB_SUB[tab]; + // у раздела со своей шапкой (data-hero) общий заголовок не нужен + if ($('#tab-' + tab)?.hasAttribute('data-hero')) { $('#page-title').hidden = true; $('#page-sub').hidden = true; } + // подразделы — внутри шапки раздела, если в ней есть место для них + // общие шапки групп разделов (data-hero-for="services prices …") + document.querySelectorAll('[data-hero-for]').forEach((h) => { h.hidden = !h.dataset.heroFor.split(' ').includes(tab); }); + if (typeof catalogHero === 'function') catalogHero(tab); + groupHero(tab); + const slot = document.querySelector('[data-hero-for]:not([hidden]) [data-subtabs-slot]') || $('#tab-' + tab)?.querySelector('[data-subtabs-slot]'); + if (!Sidebar.subtabsHome) Sidebar.subtabsHome = sub.previousElementSibling; + if (slot) slot.append(sub); else if (sub.parentElement !== Sidebar.subtabsHome.parentElement) Sidebar.subtabsHome.after(sub); + document.title = (tab === 'home' ? 'Панель клиники' : TAB_TITLE[tab]) + ' — DANA'; + if (tab === 'home') Dashboard.load(); + if (tab === 'leads') Leads.load(); + if (tab === 'stats') Stats.open(); + if (tab === 'reports') Reports.open(); + if (tab === 'doctors-dash') DoctorDash.open(); + if (tab === 'onec') Onec.open(); + window.scrollTo({ top: 0 }); +} +window.addEventListener('hashchange', route); + +/* ---------- Topbar ---------- */ +const Topbar = { + searchIndex: null, + + init(user) { + const name = user.name || 'Администратор'; + $('#who-name').textContent = name; + $('#who-mail').textContent = user.email; + $('#who-avatar').textContent = (name.match(/\S+/g) || ['A']).slice(0, 2).map((w) => w[0]).join('').toUpperCase(); + $('#search-btn').innerHTML = '' + svg('search') + ''; + $('#cal-btn').insertAdjacentHTML('afterbegin', svg('calendar')); + // сегодняшняя дата рядом с календарём, как в шапке референса + const now = new Date(); + $('#cal-date').innerHTML = `${new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }).format(now).replace(' г.', '')}` + + `${new Intl.DateTimeFormat('ru-RU', { weekday: 'long' }).format(now).replace(/^./, (c) => c.toUpperCase())}`; + $('#bell-btn').insertAdjacentHTML('afterbegin', svg('bell')); + + const input = $('#search-q'); + input.addEventListener('input', debounce(() => Topbar.search(input.value), 150)); + input.addEventListener('focus', () => { if (input.value.trim()) Topbar.search(input.value); }); + input.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { input.value = ''; Topbar.closeAll(); } + if (e.key === 'ArrowDown') { $('#search-results button')?.focus(); e.preventDefault(); } + }); + $('#search-btn').onclick = () => { input.focus(); Topbar.search(input.value); }; + $('#cal-btn').onclick = (e) => { e.stopPropagation(); Topbar.toggle('#cal-pop'); }; + $('#bell-btn').onclick = (e) => { e.stopPropagation(); Topbar.toggle('#bell-pop'); }; + document.addEventListener('click', (e) => { if (!e.target.closest('.search, .popover, .round')) Topbar.closeAll(); }); + document.addEventListener('keydown', (e) => { if (e.key === 'Escape') Topbar.closeAll(); }); + }, + + closeAll() { + $('#search-results').hidden = true; + ['#cal-pop', '#bell-pop'].forEach((id) => { $(id).hidden = true; }); + ['#cal-btn', '#bell-btn'].forEach((id) => $(id).setAttribute('aria-expanded', 'false')); + }, + toggle(id) { + const open = $(id).hidden; + Topbar.closeAll(); + $(id).hidden = !open; + $(id === '#cal-pop' ? '#cal-btn' : '#bell-btn').setAttribute('aria-expanded', String(open)); + }, + + // Поиск по уже загруженным спискам админки: врачи, услуги, чек-апы, публикации, страницы. + async buildIndex() { + const [news, articles, pages] = await Promise.all(['/admin/news', '/admin/articles', '/admin/pages'] + .map((p) => api(p).then((r) => r.data || []).catch(() => []))); + const idx = []; + for (const d of adminDoctors) idx.push({ text: fullName(d) + ' ' + (d.position || ''), title: fullName(d), kind: 'Врач', open: () => { go('doctors'); editDoctor(d); } }); + for (const s of servicesCache) idx.push({ text: s.name, title: s.name, kind: s.parent_id ? 'Услуга' : 'Направление', open: () => { go('services'); editService(s); } }); + for (const c of checkupsCache) idx.push({ text: c.name, title: c.name, kind: 'Чек-ап', open: () => { go('checkups'); editCheckup(c); } }); + for (const [list, kind, key] of [[news, 'Новость', 'news'], [articles, 'Статья', 'articles']]) { + for (const p of list) idx.push({ text: p.title, title: p.title, kind, open: () => { go('posts'); $('#po-kind').value = key; loadPosts().then(() => editPost(p)); } }); + } + for (const p of pages) idx.push({ text: p.title, title: p.title, kind: 'Страница', open: () => { go('pages'); editPage(p); } }); + Topbar.searchIndex = idx; + }, + + async search(raw) { + const q = raw.trim().toLowerCase(); + const box = $('#search-results'); + if (!q) { box.hidden = true; return; } + if (!Topbar.searchIndex) await Topbar.buildIndex(); + const hits = Topbar.searchIndex.filter((x) => x.text.toLowerCase().includes(q)).slice(0, 12); + box.textContent = ''; + if (!hits.length) box.append(el('div', { className: 'search__empty', textContent: 'Ничего не нашлось. Ищем по врачам, услугам, чек-апам, публикациям и страницам.' })); + for (const h of hits) { + const b = el('button', { type: 'button' }); + b.append(el('span', { textContent: h.title }), el('small', { textContent: h.kind })); + b.onclick = () => { Topbar.closeAll(); $('#search-q').value = ''; h.open(); }; + b.onkeydown = (e) => { + if (e.key === 'ArrowDown') { b.nextElementSibling?.focus(); e.preventDefault(); } + if (e.key === 'ArrowUp') { (b.previousElementSibling || $('#search-q')).focus(); e.preventDefault(); } + }; + box.append(b); + } + box.hidden = false; + }, + + // Календарь: реальные сроки акций и слайдов на главной на ближайшие 60 дней. + calendar(items) { + const box = $('#cal-list'); + box.textContent = ''; + if (!items.length) { box.append(el('div', { className: 'popover__empty', textContent: 'В ближайшие 60 дней акций и слайдов со сроками нет.' })); return; } + const fmt = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }); + for (const it of items) { + const b = el('button', { type: 'button', className: 'popover__item' }); + const kind = it.kind === 'promo' ? 'Акция' : 'Слайд'; + b.append(el('b', { textContent: fmt.format(new Date(it.at)).replace('.', '') })); + const t = el('span', { textContent: it.title }); + t.append(el('small', { textContent: `${kind} · ${it.event === 'ends' ? 'последний день показа' : 'начало показа'}` })); + b.append(t); + b.onclick = () => { Topbar.closeAll(); go(it.kind === 'promo' ? 'promos' : 'banners'); }; + box.append(b); + } + }, + + // Уведомления: незакрытые дела по данным базы, без выдуманных событий. + todo(items) { + const TEXT = { + new_leads: ['новых заявок', 'Ответьте пациентам', 'leads'], + pending_reviews: ['отзывов ждут проверки', 'Опубликуйте или отклоните', 'reviews'], + doctors_without_services: ['врачей без услуг', 'Отметьте, какие услуги ведут', 'doctors'], + doctors_without_branches: ['врачей без филиала', 'Укажите, где принимают', 'doctors'], + doctors_without_photo: ['врачей без фото', 'Загрузите фото', 'doctors'], + patients_page_hidden: ['страница «Пациенту» скрыта', 'Проверьте телефон в тексте и включите', 'pages'], + }; + const box = $('#bell-list'); + box.textContent = ''; + $('#bell-dot').hidden = !items.length; + if (!items.length) { box.append(el('div', { className: 'popover__empty', textContent: 'Всё заполнено — дел нет.' })); return; } + for (const it of items) { + const [label, hint, tab] = TEXT[it.key] || [it.key, '', 'home']; + const b = el('button', { type: 'button', className: 'popover__item' }); + b.append(el('b', { textContent: it.key === 'patients_page_hidden' ? '!' : it.count })); + const t = el('span', { textContent: label }); + t.append(el('small', { textContent: hint })); + b.append(t); + b.onclick = () => { + Topbar.closeAll(); go(tab); + if (tab === 'doctors') Doctors.setFilter({ doctors_without_branches: 'no-branches', doctors_without_services: 'no-services', doctors_without_photo: 'no-photo' }[it.key] || ''); + }; + box.append(b); + } + }, +}; + +/* ---------- анимации: переключатель внизу ленты ---------- */ +function initMotionSwitch() { + const sw = $('#motion'); + let off = false; + try { off = localStorage.getItem('dana-admin-motion') === 'off'; } catch { /* приватный режим */ } + const apply = () => { + document.documentElement.dataset.motion = off ? 'off' : 'on'; + sw.setAttribute('aria-checked', String(!off)); + sw.querySelector('.tip').textContent = off ? 'Анимации выключены' : 'Анимации включены'; + }; + sw.onclick = () => { off = !off; try { localStorage.setItem('dana-admin-motion', off ? 'off' : 'on'); } catch { /* без сохранения */ } apply(); }; + apply(); +} + +/* ---------- запуск ---------- */ +const baseShowApp = showApp; +showApp = function (user) { + baseShowApp(user); + Sidebar.render(); + Topbar.init(user); + initMotionSwitch(); + route(); + // на главной сводку загрузит сам экран; на остальных страницах — только для шапки + if (location.hash && location.hash !== '#home') Dashboard.summary().catch(() => {}); +}; +boot(); diff --git a/internal/api/adminui/stats.js b/internal/api/adminui/stats.js new file mode 100644 index 0000000..3a15394 --- /dev/null +++ b/internal/api/adminui/stats.js @@ -0,0 +1,225 @@ +/* «Статистика»: только то, что сайт действительно сохраняет о заявках. + Звонков, посещений и выручки сайт не знает — таких карточек здесь нет. */ + +const ST_SOURCES = { + '': 'Прямой заход / без метки', yandex: 'Яндекс', 'ya.ru': 'Яндекс', 'yandex.ru': 'Яндекс', google: 'Google', 'google.com': 'Google', + vk: 'ВКонтакте', 'vk.com': 'ВКонтакте', 'm.vk.com': 'ВКонтакте', telegram: 'Telegram', 't.me': 'Telegram', + instagram: 'Instagram', '2gis': '2ГИС', '2gis.ru': '2ГИС', whatsapp: 'WhatsApp', 'dzen.ru': 'Дзен', prodoctorov: 'ПроДокторов', + 'prodoctorov.ru': 'ПроДокторов', +}; +const ST_SECTIONS = { + home: 'Главная', services: 'Страницы услуг', doctors: 'Страницы врачей', checkups: 'Чек-апы', promos: 'Акции', + prices: 'Цены', contacts: 'Контакты', vacancy: 'Вакансии', other: 'Другие страницы', +}; +const ST_COLORS = ['#4f8ff0', '#e8708a', '#3fb27f', '#a77ef0', '#f0a04b', '#46b8c8', '#b5b8b6']; +const ST_STATUS_COLORS = { new: '#3fb27f', in_progress: '#4f8ff0', done: '#f0c24b', spam: '#e8708a' }; +const ST_PRESETS = [['today', 'Сегодня', 0], ['7', '7 дней', 6], ['30', '30 дней', 29], ['90', '3 месяца', 89], ['365', 'Год', 364]]; +const ST_ICONS = { + leads: '', + people: '', + check: '', + repeat: '', + shield: '', + calendar: '', +}; +const stIcon = (n) => ``; +const pct = (a, b) => (b ? Math.round((a / b) * 100) : 0); +const isoDay = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; +const shortDay = (s) => s.slice(8, 10) + '.' + s.slice(5, 7); + +const Stats = { + preset: '30', + group: 'day', + data: null, + + range() { + return { from: $('#st-from').value, to: $('#st-to').value }; + }, + setPreset(key) { + Stats.preset = key; + const p = ST_PRESETS.find((x) => x[0] === key); + if (p) { + const to = new Date(); const from = new Date(); from.setDate(from.getDate() - p[2]); + $('#st-from').value = isoDay(from); $('#st-to').value = isoDay(to); + } + for (const b of document.querySelectorAll('#st-presets button')) b.setAttribute('aria-pressed', String(b.dataset.key === key)); + Stats.load(); + }, + + async load() { + const { from, to } = Stats.range(); + const br = $('#st-branch').value; + try { + const r = await api(`/admin/stats?from=${from}&to=${to}${br ? '&branch_id=' + br : ''}`); + Stats.data = r.data; Stats.meta = r.meta; + for (const k of ['by_day', 'by_form', 'by_source', 'by_section', 'by_branch', 'top_services', 'top_doctors']) Stats.data[k] ||= []; + Stats.render(); + } catch (e) { toast(e.message, true); } + }, + + delta(cur, prev) { + if (!prev && !cur) return ''; + if (!prev) return 'новое'; + const d = Math.round(((cur - prev) / prev) * 100); + if (!d) return 'без изменений'; + return `${d > 0 ? '↑' : '↓'} ${Math.abs(d)}%`; + }, + + render() { + const s = Stats.data, m = Stats.meta; + const period = m.days === 1 ? 'за день' : `за ${m.days} ${plural(m.days, 'день', 'дня', 'дней')}`; + const kpis = [ + ['leads', '#efeafb', '#7a5cc9', 'Всего заявок', s.total, Stats.delta(s.total, s.prev_total), period + ' · без спама'], + ['people', '#e5f1fb', '#3a7bd5', 'Разные телефоны', s.unique_phones, Stats.delta(s.unique_phones, s.prev_unique), 'уникальные обращения'], + ['check', '#e7f6ee', '#2f9a6a', 'Обработано', pct(s.done, s.total) + '%', '', `${s.done} из ${s.total} — статус «обработана»`], + ['repeat', '#fdf1e3', '#d8822c', 'Повторные', s.repeat, '', 'номера с 2+ заявками'], + ['shield', '#fdeaec', '#d9546d', 'Спам отсеян', s.spam, '', 'помечены как спам'], + ]; + const k = $('#st-kpis'); k.textContent = ''; + for (const [icon, bg, ink, label, value, delta, hint] of kpis) { + const c = el('div', { className: 'card kpi' }); + c.innerHTML = `${stIcon(icon)} +
${label}${value}${delta}${hint}
`; + k.append(c); + } + Stats.chart(); + Stats.donut('#st-sources', s.by_source.map((x) => [ST_SOURCES[x.key] ?? x.label, x.count]), s.total, + s.total && !s.with_utm ? 'Ни одна заявка за период не пришла с UTM-меткой или с другого сайта. Источник записывается только для заявок, оставленных после обновления сайта.' : ''); + Stats.donut('#st-status', ['new', 'in_progress', 'done', 'spam'].map((key) => [LEAD_STATUS[key]?.[0] || key, + s[key === 'in_progress' ? 'in_progress' : key], ST_STATUS_COLORS[key]]), s.total + s.spam, '', 'заявок'); + Stats.tops('#st-services', s.top_services, 'Услуга', 'Заявки с выбранной услугой или со страницы услуги.'); + Stats.tops('#st-doctors', s.top_doctors, 'Врач', 'Заявки к врачу или со страницы врача.'); + Stats.bars('#st-branches', s.by_branch.map((x) => [x.label ? shortAddress(x.label) : 'Филиал не выбран', x.count]), s.total); + Stats.bars('#st-forms', s.by_form.map((x) => [x.label, x.count]), s.total); + Stats.bars('#st-pages', s.by_section.map((x) => [ST_SECTIONS[x.key] || x.key, x.count]), s.total); + }, + + // Динамика: по дням или по неделям, с подсказкой при наведении + chart() { + const box = $('#st-chart'); box.textContent = ''; + let pts = Stats.data.by_day.map((d) => ({ label: shortDay(d.date), full: ruDate(d.date), v: d.count })); + if (Stats.group === 'week' && pts.length > 13) { + const w = []; + for (let i = 0; i < pts.length; i += 7) { + const part = pts.slice(i, i + 7); + w.push({ label: part[0].label, full: `${part[0].full} — ${part[part.length - 1].full}`, v: part.reduce((a, b) => a + b.v, 0) }); + } + pts = w; + } + const W = Math.max(box.clientWidth, 320), H = 230, L = 34, R = 10, T = 12, B = 26; + const max = Math.max(4, ...pts.map((p) => p.v)); + const step = Math.ceil(max / 4); + const top = step * 4; + const x = (i) => L + (pts.length === 1 ? (W - L - R) / 2 : (i * (W - L - R)) / (pts.length - 1)); + const y = (v) => T + (H - T - B) * (1 - v / top); + const line = pts.map((p, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)} ${y(p.v).toFixed(1)}`).join(' '); + const area = `${line} L${x(pts.length - 1).toFixed(1)} ${y(0)} L${x(0).toFixed(1)} ${y(0)} Z`; + let grid = ''; + for (let g = 0; g <= 4; g++) { + const v = step * g; + grid += `${v}`; + } + const every = Math.ceil(pts.length / 10); + const labels = pts.map((p, i) => (i % every === 0 || i === pts.length - 1) ? `${p.label}` : '').join(''); + const svg = ` + + ${grid}${labels} + `; + box.innerHTML = svg + ''; + const s = box.querySelector('svg'), tip = box.querySelector('.st-tip'), guide = s.querySelector('.st-guide'), dot = s.querySelector('.st-dot'); + s.addEventListener('mousemove', (e) => { + const r = s.getBoundingClientRect(); + const px = ((e.clientX - r.left) / r.width) * W; + let i = Math.round(((px - L) / (W - L - R)) * (pts.length - 1)); + i = Math.max(0, Math.min(pts.length - 1, i)); + const p = pts[i]; + guide.setAttribute('x1', x(i)); guide.setAttribute('x2', x(i)); guide.hidden = false; + dot.setAttribute('cx', x(i)); dot.setAttribute('cy', y(p.v)); dot.hidden = false; + tip.hidden = false; + tip.innerHTML = `${p.v} ${plural(p.v, 'заявка', 'заявки', 'заявок')}${p.full}`; + tip.style.left = (x(i) / W) * r.width + 'px'; tip.style.top = (y(p.v) / H) * r.height + 'px'; + }); + s.addEventListener('mouseleave', () => { tip.hidden = true; guide.hidden = true; dot.hidden = true; }); + }, + + donut(sel, items, total, note, unit = 'заявок') { + const box = $(sel); box.textContent = ''; + items = items.filter((x) => x[1] > 0); + const sum = items.reduce((a, b) => a + b[1], 0); + if (!sum) { box.append(el('p', { className: 'st-empty', textContent: 'За этот период заявок нет.' })); return; } + const R = 60, C = 2 * Math.PI * R; + let off = 0, arcs = ''; + items.forEach((it, i) => { + const len = (it[1] / sum) * C; + arcs += ``; + off += len; + }); + const wrap = el('div', { className: 'st-donut' }); + wrap.innerHTML = `${arcs} + ${sum}${unit}`; + const legend = el('ul', { className: 'st-legend' }); + items.forEach((it, i) => { + const li = el('li'); + li.innerHTML = `${pct(it[1], sum)}%${it[1]}`; + li.querySelector('span').textContent = it[0]; + legend.append(li); + }); + wrap.append(legend); box.append(wrap); + if (note) box.append(el('p', { className: 'st-note', textContent: note })); + }, + + bars(sel, items, total) { + const box = $(sel); box.textContent = ''; + if (!items.length) { box.append(el('p', { className: 'st-empty', textContent: 'За этот период заявок нет.' })); return; } + const max = Math.max(...items.map((x) => x[1])); + for (const [label, n] of items) { + const row = el('div', { className: 'st-bar' }); + row.innerHTML = `
${pct(n, total)}%${n}`; + row.querySelector('span').textContent = label; + box.append(row); + } + }, + + tops(sel, list, what, note) { + const box = $(sel); box.textContent = ''; + if (!list.length) { box.append(el('p', { className: 'st-empty', textContent: 'За этот период нет заявок, привязанных к ' + (what === 'Врач' ? 'врачу.' : 'услуге.') })); return; } + const t = el('table', { className: 'st-top' }); + t.innerHTML = `#${what}ЗаявкиОбработано`; + const tb = el('tbody'); + list.slice(0, 5).forEach((x, i) => { + const tr = el('tr'); + tr.append(el('td', { className: 'muted', textContent: i + 1 }), el('td', { textContent: x.name }), + el('td', { textContent: x.count }), el('td', { className: 'muted', textContent: pct(x.done, x.count) + '%' })); + tb.append(tr); + }); + t.append(tb); box.append(t); + box.append(el('p', { className: 'st-note', textContent: note })); + }, + + init() { + const pre = $('#st-presets'); + for (const [key, label] of ST_PRESETS) { + const b = el('button', { type: 'button', textContent: label }); + b.dataset.key = key; + b.onclick = () => Stats.setPreset(key); + pre.append(b); + } + $('#st-cal').innerHTML = stIcon('calendar'); + for (const id of ['#st-from', '#st-to']) $(id).onchange = () => { + Stats.preset = ''; + for (const b of document.querySelectorAll('#st-presets button')) b.setAttribute('aria-pressed', 'false'); + if ($('#st-from').value && $('#st-to').value) Stats.load(); + }; + $('#st-branch').onchange = () => Stats.load(); + $('#st-group').onchange = () => { Stats.group = $('#st-group').value; Stats.chart(); }; + window.addEventListener('resize', debounce(() => { if (Stats.data && !$('#tab-stats').hidden) Stats.chart(); }, 150)); + }, + + open() { + const sel = $('#st-branch'); + if (sel.options.length <= 1) for (const b of branchList) sel.append(el('option', { value: b.id, textContent: shortAddress(b.address) })); + if (!Stats.data) Stats.setPreset(Stats.preset || '30'); else Stats.load(); + }, +}; +Stats.init(); diff --git a/internal/api/adminui/weekly-experiment.css b/internal/api/adminui/weekly-experiment.css new file mode 100644 index 0000000..daf5a0e --- /dev/null +++ b/internal/api/adminui/weekly-experiment.css @@ -0,0 +1,71 @@ +/* Approved reference styling, applied to the real table only while open. */ +.weekly-experiment { + position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none; + margin: 0; padding: 8px; border: 0; border-radius: 0; background: #efefed; color: #25364a; + font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px; box-sizing: border-box; + --wk-channel-width: 226px; --wk-metric-width: 205px; +} +.weekly-experiment::backdrop { background: #efefed; } +.wke-layout { display: flex; flex-direction: column; height: 100%; min-height: 0; border-radius: 20px; background: #fdfdfc; overflow: hidden; } +.wke-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 6px 14px; min-height: 36px; } +.wke-modes { display: inline-flex; flex-shrink: 0; gap: 3px; padding: 3px; border-radius: 11px; background: #f1f2f3; } +.wke-mode { padding: 6px 12px; border: 0; border-radius: 8px; background: transparent; color: #7c8a98; font-size: 13px; cursor: pointer; white-space: nowrap; } +.wke-mode[aria-pressed="true"] { background: #e2efe8; color: #355d4b; box-shadow: 0 1px 3px #2636490a; } +.wke-mode:hover { color: #355d4b; } +.wke-mode:focus-visible { outline: 2px solid #8fbba5; outline-offset: 1px; } +.wke-status { margin-right: auto; font-size: 12px; color: #8b959e; } +.wke-status--error { color: #b85349; } +.wke-refresh { padding: 5px 10px; border: 0; border-radius: 8px; background: #f1f2f3; color: #55656e; cursor: pointer; font-size: 12px; } +.wke-refresh:disabled { opacity: .5; cursor: wait; } +.weekly-experiment #wk-filter { gap: 6px; flex-wrap: wrap; } +.weekly-experiment #wk-filter input { width: 132px; height: 28px; border: 1px solid #e8eded; border-radius: 7px; background: #f6f6f6; padding: 3px 6px; font: 12px 'Segoe UI', sans-serif; } +.weekly-experiment #wk-filter-reset { border: 0; background: none; color: #627a87; font-size: 12px; cursor: pointer; } +.weekly-experiment .wke-scroll { flex: 1; min-height: 0; overflow: auto; padding: 0 0 8px; box-sizing: border-box; scrollbar-width: thin; scrollbar-color: #d7ddde #f6f6f5; } +.weekly-experiment .wk-table { margin: 4px 0 0 8px; width: calc(var(--wk-channel-width) + var(--wk-metric-width) + var(--wk-period-count, 1) * var(--wk-period-width, 112px) + 6px); min-width: 0; table-layout: fixed; border-collapse: separate; border-spacing: 2px; color: #25364a; } +.weekly-experiment .wk-col--channel { width: var(--wk-channel-width); } +.weekly-experiment .wk-col--metric { width: var(--wk-metric-width); } +.weekly-experiment .wk-col--period { width: var(--wk-period-width, 112px); } +.weekly-experiment .wk-table th, .weekly-experiment .wk-table td { box-sizing: border-box; border: 0; height: 30px; padding: 3px 8px; border-radius: 5px; font-size: 14px; line-height: 18px; font-weight: 400; vertical-align: middle; } +.weekly-experiment .wk-table thead th { position: sticky; top: 0; z-index: 8; height: 42px; padding: 4px 7px; white-space: nowrap; text-align: center; border-radius: 10px; background: #f1f2f3; font-weight: 600; color: #152337; } +.weekly-experiment .wk-table .wk-brand { left: 0; z-index: 10; min-width: 0; padding: 0 20px; text-align: left; border-radius: 0; background: #fdfdfc; } +.weekly-experiment .wk-brand span { display: block; font-size: 24px; line-height: 26px; font-weight: 400; letter-spacing: -.025em; } +.weekly-experiment .wk-brand small { display: block; margin-top: 0; font-size: 13px; line-height: 16px; font-weight: 400; color: #8b959e; } +.weekly-experiment .wk-table .wk-metric { position: sticky; left: calc(var(--wk-channel-width) + 2px); z-index: 3; white-space: normal; text-align: left; color: #25364a; background: linear-gradient(135deg, #f6f6f6, #f2f3f3); border-top-left-radius: 0; border-bottom-left-radius: 0; box-shadow: -2px 0 0 #fdfdfc; } +.weekly-experiment .wk-table .wk-value { text-align: right; white-space: nowrap; background: linear-gradient(135deg, #f6f6f6, #f2f3f3); font-variant-numeric: tabular-nums; } +.weekly-experiment .wk-table .wk-heat { background: var(--wk-heat); } +.weekly-experiment .wk-table .wk-channel { position: sticky; left: 0; z-index: 5; padding: 18px 20px; border-right: 6px solid #fdfdfc; border-radius: 13px 0 0 13px; text-align: left; color: #17283b; background: linear-gradient(125deg, var(--wk-tint), #fafaf8); } +/* При прокрутке вправо колонка прижата к краю окна: скруглённые левые углы открывали цветные ячейки под ней. */ +.weekly-experiment.wke-scrolled .wk-table .wk-channel { border-top-left-radius: 0; border-bottom-left-radius: 0; } +/* При прокрутке вниз шапка закреплена у верха: скруглённые углы и зазоры между ячейками открывали цветные строки под ней. */ +.weekly-experiment.wke-scrolled-y .wk-table thead th { border-radius: 0; box-shadow: 2px 0 0 #fdfdfc, 0 2px 0 #fdfdfc, 2px 2px 0 #fdfdfc; } +.weekly-experiment .wk-channel__icon { width: 48px; height: 48px; margin-bottom: 8px; background: transparent; } +.weekly-experiment .wk-channel__icon img, .weekly-experiment .wk-channel__icon svg { width: 32px; height: 32px; } +.weekly-experiment .wk-channel__title { margin: 0 0 12px; color: #14243a; font-size: 18px; line-height: 22px; font-weight: 600; } +.weekly-experiment .wk-channel__hint { margin-top: 0; font-size: 13px; line-height: 18px; white-space: pre-line; } +.weekly-experiment .wk-table .wk-gap td { height: 30px; padding: 0; background: #fdfdfc; } +.weekly-experiment .wk-table .wk-budget { width: 100%; height: 22px; font-size: 14px; } +.wke-close { display: block; flex-shrink: 0; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 8px; color: #7c8a98; background: transparent; font: 24px/28px 'Segoe UI', sans-serif; cursor: pointer; opacity: .65; } +.wke-close:hover, .wke-close:focus-visible { background: #e8edee; color: #213448; opacity: 1; } +.wke-close:focus-visible { outline: 2px solid #8fbba5; outline-offset: 2px; } +.wke-empty { flex-shrink: 0; padding: 10px 20px; text-align: left; color: #6d7d89; font-size: 13px; line-height: 1.6; background: #f6f7f6; } +@media (max-width: 900px) { + .weekly-experiment { --wk-channel-width: 190px; --wk-metric-width: 190px; } + .weekly-experiment .wk-table .wk-channel { padding: 14px 12px; } + .weekly-experiment .wk-table .wk-brand { padding-left: 12px; } +} +@media (max-width: 600px) { + .weekly-experiment { padding: 4px; --wk-channel-width: 126px; --wk-metric-width: 145px; } + .wke-layout { border-radius: 14px; } + .wke-toolbar { gap: 6px; padding: 6px 8px; } + .wke-status { flex: 1; } + .weekly-experiment #wk-filter { order: 1; width: 100%; justify-content: flex-start; } + .weekly-experiment #wk-filter input { flex: 1; min-width: 0; } + .weekly-experiment .wke-scroll { padding: 0 0 5px; } + .weekly-experiment .wk-table { margin: 4px 0 0 5px; } + .weekly-experiment .wk-table .wk-brand { padding-left: 8px; } + .weekly-experiment .wk-brand span { font-size: 20px; } + .weekly-experiment .wk-brand small { font-size: 11px; } + .weekly-experiment .wk-table .wk-channel { padding: 12px 8px; } + .weekly-experiment .wk-channel__title { font-size: 14px; } + .weekly-experiment .wk-channel__hint { font-size: 11px; } +} diff --git a/internal/api/adminui/weekly-experiment.js b/internal/api/adminui/weekly-experiment.js new file mode 100644 index 0000000..cf789e2 --- /dev/null +++ b/internal/api/adminui/weekly-experiment.js @@ -0,0 +1,102 @@ +/* Full-viewport presentation of the real weekly-report table. + No demo periods, metrics or contact numbers: WeeklyDashboard owns all data. */ +const WeeklyFullscreen = { + ensure() { + if (this.dialog) return; + const dialog = el('dialog', { id: 'weekly-fullscreen', className: 'weekly-experiment', ariaLabel: ReportClinic.brand + ' — маркетинговая аналитика по неделям' }); + const layout = el('div', { className: 'wke-layout' }); + const toolbar = el('div', { className: 'wke-toolbar' }); + const modes = el('div', { className: 'wke-modes', role: 'group', ariaLabel: 'Периодичность отчёта' }); + for (const [mode, label] of [['weeks', 'По неделям'], ['months', 'По месяцам']]) { + const button = el('button', { type: 'button', className: 'wke-mode', textContent: label }); + button.dataset.mode = mode; + button.onclick = () => WeeklyDashboard.setMode(mode); + modes.append(button); + } + const status = el('span', { className: 'wke-status', role: 'status', ariaLive: 'polite' }); + const refresh = el('button', { type: 'button', className: 'wke-refresh', textContent: 'Обновить', title: 'Загрузить актуальные данные отчётов' }); + refresh.onclick = () => WeeklyDashboard.load(); + const close = el('button', { type: 'button', className: 'wke-close', ariaLabel: 'Вернуться в админку', title: 'Вернуться в админку · Esc', textContent: '×' }); + close.onclick = () => dialog.close(); + toolbar.append(modes, status, refresh, close); + const scroll = el('div', { className: 'wke-scroll' }); + const empty = el('div', { className: 'wke-empty', role: 'status', hidden: true }); + layout.append(toolbar, empty, scroll); + dialog.append(layout); + scroll.addEventListener('scroll', () => { + dialog.classList.toggle('wke-scrolled', scroll.scrollLeft > 0); + dialog.classList.toggle('wke-scrolled-y', scroll.scrollTop > 0); + }, { passive: true }); + dialog.addEventListener('close', () => { + // Return the actual nodes (including budget/filter handlers), not copies. + this.tableHome.append(this.table); + this.filterHome.append(this.filter); + document.body.style.overflow = this.savedOverflow; + document.title = this.savedTitle; + }); + document.body.append(dialog); + Object.assign(this, { dialog, toolbar, modes, status, refresh, close, scroll, empty }); + this.resizeObserver = new ResizeObserver(() => this.resize()); + this.resizeObserver.observe(scroll); + }, + + async open() { + this.ensure(); + if (this.dialog.open) return; + this.table = $('#wk-table'); + this.filter = $('#wk-filter'); + this.tableHome = this.table.parentElement; + this.filterHome = this.filter.parentElement; + this.savedOverflow = document.body.style.overflow; + this.savedTitle = document.title; + this.scroll.append(this.table); + this.toolbar.insertBefore(this.filter, this.refresh); + this.scroll.scrollTop = 0; + this.scroll.scrollLeft = 0; + this.dialog.classList.remove('wke-scrolled', 'wke-scrolled-y'); + document.body.style.overflow = 'hidden'; + document.title = ReportClinic.brand + ' — маркетинговая аналитика'; + this.dialog.showModal(); + this.close.focus({ preventScroll: true }); + await WeeklyDashboard.load(); + }, + + sync() { + if (!this.dialog?.open) return; + const { list, loading, error } = WeeklyDashboard; + const periods = WeeklyDashboard.periods(); + const monthly = WeeklyDashboard.mode === 'months'; + for (const button of this.modes.children) button.setAttribute('aria-pressed', String(button.dataset.mode === WeeklyDashboard.mode)); + // Keep the page's top tabs in sync when the mode changes inside the report. + for (const button of document.querySelectorAll('#rp-tabs button[data-mode]')) button.setAttribute('aria-selected', String(button.dataset.mode === WeeklyDashboard.mode)); + const heading = $('#tab-reports .rp-head h1'); + if (heading) heading.textContent = monthly ? 'Дашборд по месяцам' : 'Дашборд по неделям'; + this.dialog.setAttribute('aria-label', ReportClinic.brand + ' — маркетинговая аналитика ' + (monthly ? 'по месяцам' : 'по неделям')); + const visible = WeeklyDashboard.filtered(); + this.table.style.setProperty('--wk-period-count', visible.length); + this.refresh.disabled = loading; + this.dialog.setAttribute('aria-busy', String(loading)); + this.status.textContent = loading ? 'Загрузка отчётов…' + : error ? `${error}${list.length ? ' · показаны ранее загруженные данные' : ''}` + : `Периодов: ${periods.length}${visible.length !== periods.length ? ' · показано: ' + visible.length : ''}`; + this.status.classList.toggle('wke-status--error', Boolean(error)); + this.scroll.hidden = false; + this.empty.hidden = Boolean(visible.length); + this.empty.textContent = loading ? 'Загрузка реальных данных…' + : error ? 'Не удалось загрузить отчёты. Нажмите «Обновить», чтобы повторить.' + : periods.length ? 'За выбранные даты отчётов нет. Измените даты или нажмите «Показать все».' + : monthly ? 'Месячных отчётов пока нет. Загрузите выгрузку 1С за полный календарный месяц во вкладке «Новый отчёт». Недельные отчёты не суммируются, чтобы не задвоить пациентов.' + : 'Недельных отчётов пока нет. Загрузите выгрузку 1С с указанным периодом во вкладке «Новый отчёт».'; + this.resize(); + }, + + resize() { + if (!this.dialog?.open) return; + const style = getComputedStyle(this.dialog); + const frozenWidth = parseFloat(style.getPropertyValue('--wk-channel-width')) + parseFloat(style.getPropertyValue('--wk-metric-width')); + const count = Math.max(WeeklyDashboard.filtered().length, 1); + // Few actual weeks must not stretch into enormous columns or get fake dates. + const width = Math.max(112, Math.min(160, (this.scroll.clientWidth - frozenWidth - 24) / count)); + this.table.style.setProperty('--wk-period-width', width + 'px'); + }, +}; diff --git a/internal/api/adminui/weekly.js b/internal/api/adminui/weekly.js new file mode 100644 index 0000000..bcdd169 --- /dev/null +++ b/internal/api/adminui/weekly.js @@ -0,0 +1,521 @@ +/* «Дашборд по неделям»: только просмотр накопленной истории периодов. Сами периоды появляются + автоматически при загрузке выгрузки 1С на вкладке «Новый отчёт» (если в файле есть строка + «Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ»). Здесь — таблица, фильтр по датам и правка бюджета. */ + +// Conditional-format rules read from «Показатели Дана (11).xlsx», «Дана неделя». +// Excel ranges identify the corresponding metric. In the live dashboard they +// extend across that metric's full history, including weeks hidden by the filter. +const WeeklyExcelFormatting = { + rules: { + callcenter: { + unique: { range: 'C8:P8', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C12:AA12', colors: ['#E67C73', '#FFD666', '#57BB8A'] }, + }, + 'сайт': { + leads: { range: 'C17:U17', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C22:Z22', colors: ['#E98972', '#FFD666', '#57BB8A'] }, + }, + '2гис': { + leads: { range: 'C35 / D35:Z35', separatePeriod: '30.03-05.04', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C40:AA40', colors: ['#E67C73', '#FFD666', '#57BB8A'] }, + }, + 'яндекскарточка': { + leads: { range: 'C43:U44', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C49:P49', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + }, + 'продокторов': { + leads: { range: 'C62:P62', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C67:P67', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + }, + 'напоправку': { revenue: { range: 'C76:P76', colors: ['#E78273', '#FFD666', '#E78273'] } }, + 'сберздоровье': { revenue: { range: 'C85:P85', colors: ['#E78273', '#FFD666', '#57BB8A'] } }, + zoon: { revenue: { range: 'C94:P94', colors: ['#E78273', '#FFD666', '#57BB8A'] } }, + 'докту': { revenue: { range: 'C103:P103', colors: ['#E78273', '#FFD666', '#57BB8A'] } }, + 'тв': { revenue: { range: 'C127:P127', colors: ['#E78273', '#FFD666', '#57BB8A'] } }, + 'инстаграм': { + leads: { range: 'C149:U149', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + revenue: { range: 'C154:P154', colors: ['#E78273', '#FFD666', '#57BB8A'] }, + }, + }, + + // Ordinary cell fills and manually highlighted rows on «Дана неделя». + // These are not colorScale rules: do not invent numeric thresholds for them. + fills: { + google: { revenue: { range: 'C58:U58', color: '#83C380' } }, + zoon: { leads: { range: 'C89:U89', color: '#E67C73' } }, + 'докту': { leads: { range: 'C98:U98', color: '#E98A71' } }, + 'тв': { leads: { range: 'C122:U122', color: '#E98A71' } }, + 'радио': { + leads: { range: 'C131:U131', color: '#E98972' }, + revenue: { range: 'C136:U136', color: '#F4CCCC' }, + }, + 'баннера': { revenue: { range: 'C145:U145', color: '#F4CCCC' } }, + 'маршрутки': { revenue: { range: 'C163:U163', color: '#F4CCCC' } }, + 'вотсапрассылки': { revenue: { range: 'C172:U172', color: '#F4CCCC' } }, + 'полиграфия': { revenue: { range: 'C181:U181', color: '#F4CCCC' } }, + 'итогоинтернет': { conversion: { range: 'C112:U112', color: '#F4CCCC' } }, + }, + + rule(source, metric) { + let key = String(source || '').toLocaleLowerCase('ru-RU').replace(/[\s_-]+/g, ''); + key = ({ '2gis': '2гис', 'яндекс': 'яндекскарточка', 'инстаграмм': 'инстаграм', instagram: 'инстаграм' })[key] || key; + return this.rules[key]?.[metric]; + }, + + fill(source, metric) { + const key = WeeklySourceLayout.key(source); + if (this.fills[key]?.[metric]) return this.fills[key][metric]; + if (key === 'callcenter') return { range: 'C2:U15', color: '#F9CB9C' }; + if (WeeklySourceLayout.sources.some(([name]) => WeeklySourceLayout.key(name) === key)) + return { range: 'C17:U187', color: '#EAE5D8' }; + return null; + }, + + colors(values, rule, reference = values, labels = [], referenceLabels = labels) { + if (!rule) return values.map(() => ''); + const periodKey = (label) => String(label || '').replace(/[–—]/g, '-').replace(/\s/g, ''); + const separate = (label) => Boolean(rule.separatePeriod && periodKey(label) === rule.separatePeriod); + const sorted = (isolated) => reference.filter((v, i) => Number.isFinite(v) && separate(referenceLabels[i]) === isolated).sort((a, b) => a - b); + const groups = [sorted(false), sorted(true)]; + const rgb = rule.colors.map((color) => [1, 3, 5].map((offset) => parseInt(color.slice(offset, offset + 2), 16))); + return values.map((value, i) => { + if (!Number.isFinite(value)) return ''; + const nums = groups[Number(separate(labels[i]))]; + if (!nums.length) return ''; + const min = nums[0], max = nums[nums.length - 1]; + // PERCENTILE.INC(range, .5): the middle value, or the mean of the two middle values. + const median = (nums[Math.floor((nums.length - 1) / 2)] + nums[Math.ceil((nums.length - 1) / 2)]) / 2; + // Excel uses the upper colour for a singleton/equal-value scale (e.g. C35). + if (min === max || value >= max) return rule.colors[2]; + if (value <= min) return rule.colors[0]; + if (value === median) return rule.colors[1]; + const lowerHalf = value < median; + const lower = lowerHalf ? min : median, upper = lowerHalf ? median : max; + const from = rgb[lowerHalf ? 0 : 1], to = rgb[lowerHalf ? 1 : 2]; + const fraction = (value - lower) / (upper - lower); + return `rgb(${from.map((start, component) => Math.round(start + (to[component] - start) * fraction)).join(', ')})`; + }); + }, +}; + +const WeeklySourceLayout = { + // Source order from the reference, with «Медийка» excluded at the user's request. + sources: [ + ['сайт', 'Сайт'], ['2гис', '2Гис'], + ['яндекскарточка', 'Яндекс карточка'], ['google', 'Google карточка'], + ['продокторов', 'Продокторов'], ['напоправку', 'НаПоправку'], + ['сберздоровье', 'Сберздоровье'], ['zoon', 'ZOON'], ['докту', 'Докту'], + ['итого-интернет', 'ИТОГО интернет'], ['тв', 'ТВ'], ['радио', 'Радио'], + ['баннера', 'Баннера'], ['инстаграм', 'Инстаграм'], ['маршрутки', 'Маршрутки'], + ['вотсапрассылки', 'Вотсап рассылки'], ['полиграфия', 'Полиграфия'], + ], + + key(value) { + const key = String(value || '').toLocaleLowerCase('ru-RU').replace(/[\s_-]+/g, ''); + return ({ + '2gis': '2гис', 'яндекс': 'яндекскарточка', googleкарточка: 'google', + 'баннер': 'баннера', 'инстаграмм': 'инстаграм', instagram: 'инстаграм', + whatsapp: 'вотсапрассылки', whatsappрассылки: 'вотсапрассылки', + 'вотсап': 'вотсапрассылки', 'ватсап': 'вотсапрассылки', 'ватсапрассылки': 'вотсапрассылки', + })[key] || key; + }, + + arrange(reports) { + const available = []; + const seen = new Set(); + for (const report of reports) { + for (const channel of report.channels) { + // Hide this source from the dashboard, including previously saved reports. + // The saved channel and its calculations remain untouched. + if (this.key(channel.key) === 'медийка' || this.key(channel.title) === 'медийка') continue; + if (this.key(channel.key) === 'всеномера' || seen.has(channel.key)) continue; + seen.add(channel.key); + available.push(channel); + } + } + const result = []; + const used = new Set(); + for (const [key, title] of this.sources) { + const canonical = this.key(key); + const matches = available.filter((channel) => !used.has(channel.key) + && (this.key(channel.key) === canonical || this.key(channel.title) === canonical)); + if (matches.length) { + for (const channel of matches) { + result.push({ key: channel.key, title }); + used.add(channel.key); + } + } else { + result.push({ key, title }); + } + } + // Keep additional real channels; never discard data absent from the template. + return result.concat(available.filter((channel) => !used.has(channel.key)).map(({ key, title }) => ({ key, title }))); + }, +}; + +const WeeklyDashboard = { + list: [], + // Страница по ссылке для маркетолога (share.html): только просмотр — без удаления периодов и правки бюджета. + readOnly: false, + loading: false, + error: '', + requestId: 0, + mode: 'weeks', + + dateKey(value) { + const formatter = new Intl.DateTimeFormat('en-CA', { timeZone: 'Europe/Moscow', year: 'numeric', month: '2-digit', day: '2-digit' }); + const parts = Object.fromEntries(formatter.formatToParts(new Date(value)).map(({ type, value }) => [type, value])); + return `${parts.year}-${parts.month}-${parts.day}`; + }, + + isMonth(report) { + const from = this.dateKey(report.period_from), to = this.dateKey(report.period_to); + const [year, month, day] = from.split('-').map(Number); + return day === 1 && to === new Date(Date.UTC(year, month, 1)).toISOString().slice(0, 10); + }, + + periods() { + return this.list.filter((report) => this.isMonth(report) === (this.mode === 'months')); + }, + + setMode(mode) { + if (!['weeks', 'months'].includes(mode) || this.mode === mode) return; + this.mode = mode; + this.render(); + }, + + periodLabel(report) { + if (this.mode !== 'months') return report.label.replace(/[–—]/g, '-'); + const label = new Intl.DateTimeFormat('ru-RU', { timeZone: 'Europe/Moscow', month: 'long', year: 'numeric' }).format(new Date(report.period_from)).replace(/\s*г\.$/, ''); + return label[0].toLocaleUpperCase('ru-RU') + label.slice(1); + }, + + async open() { + await WeeklyDashboard.load(); + }, + + async load() { + const requestId = ++WeeklyDashboard.requestId; + WeeklyDashboard.loading = true; + WeeklyDashboard.error = ''; + WeeklyDashboard.render(); + try { + const r = await api(WeeklyDashboard.readOnly ? '/share/weekly-reports' : ReportClinic.q('/admin/weekly-reports')); + if (requestId !== WeeklyDashboard.requestId) return; + WeeklyDashboard.list = Array.isArray(r.data) ? r.data : []; + } catch (e) { + if (requestId !== WeeklyDashboard.requestId) return; + WeeklyDashboard.error = e.message || 'Не удалось загрузить отчёты'; + toast(WeeklyDashboard.error, true); + } finally { + if (requestId === WeeklyDashboard.requestId) { + WeeklyDashboard.loading = false; + WeeklyDashboard.render(); + } + } + }, + + async setBudget(reportId, channel, raw) { + const amount = Number(String(raw).trim().replace(',', '.')); + if (!Number.isFinite(amount) || amount < 0) { + toast('Бюджет должен быть неотрицательным числом', true); + WeeklyDashboard.render(); + return false; + } + try { + await api(`/admin/weekly-reports/${reportId}/budget`, { method: 'PUT', body: JSON.stringify({ channel, amount }) }); + const saved = WeeklyDashboard.list.find((r) => r.id === reportId)?.channels.find((c) => c.key === channel); + if (saved) saved.budget = amount; + WeeklyDashboard.render(); + return true; + } catch (e) { + toast(e.message, true); + WeeklyDashboard.render(); + return false; + } + }, + + async remove(id) { + if (!confirm('Удалить этот период из дашборда?')) return; + try { + await api('/admin/weekly-reports/' + id, { method: 'DELETE' }); + toast('Период удалён'); + WeeklyDashboard.load(); + } catch (e) { toast(e.message, true); } + }, + + /* ---------- фильтр по датам ---------- */ + filtered() { + const from = $('#wk-filter-from').value, to = $('#wk-filter-to').value; + const periods = WeeklyDashboard.periods(); + if (!from && !to) return periods; + return periods.filter((r) => { + const pf = WeeklyDashboard.dateKey(r.period_from), pt = WeeklyDashboard.dateKey(r.period_to); + // Storage uses Moscow midnight and an exclusive period_to (next day). + if (from && pt <= from) return false; + if (to && pf > to) return false; + return true; + }); + }, + + /* ---------- таблица ---------- */ + render() { + const all = WeeklyDashboard.periods(); + const list = WeeklyDashboard.filtered(); + $('#wk-title').textContent = all.length + ? `Периодов в дашборде: ${all.length}` + (list.length !== all.length ? ` · показано: ${list.length}` : '') + : 'Периодов пока нет'; + $('#wk-sub').textContent = all.length + ? `С ${all[0].label} по ${all[all.length - 1].label}` + : 'Периоды появляются сами: загрузите выгрузку 1С на вкладке «Новый отчёт» — если в ней есть строка «Период ДД.ММ.ГГГГ - ДД.ММ.ГГГГ», результат попадёт и сюда.'; + $('#wk-filter').hidden = all.length < 2; + // Keep source names/icons visible even if this mode/filter has no periods. + $('#wk-board').hidden = false; + + const sources = WeeklySourceLayout.arrange(WeeklyDashboard.list); + const order = sources.map((source) => source.key); + const titleByKey = Object.fromEntries(sources.map((source) => [source.key, source.title])); + + const fmtN = (v) => Math.round(v || 0).toLocaleString('ru-RU'); + const fmtRub = (v) => '₽ ' + (v || 0).toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); + const fmtPct = (v) => (v || 0).toFixed(1).replace('.', ',') + '%'; + const fmtF = (v) => (v || 0).toFixed(2).replace('.', ','); + const fmtTime = (sec) => { sec = sec || 0; const m = Math.floor(sec / 60), s = sec % 60; return m + ':' + String(s).padStart(2, '0'); }; + + const table = $('#wk-table'); + table.textContent = ''; + const cols = el('colgroup'); + for (const name of ['channel', 'metric', ...list.map(() => 'period')]) cols.append(el('col', { className: 'wk-col--' + name })); + table.append(cols); + const thead = el('thead'); + const hr = el('tr'); + const brand = el('th', { className: 'wk-brand', colSpan: '2' }); + brand.append(el('span', { textContent: ReportClinic.brand }), el('small', { textContent: 'Маркетинговая аналитика' })); + hr.append(brand); + for (const rep of list) { + const th = el('th', { className: 'wk-period' }); + th.append(el('span', { textContent: WeeklyDashboard.periodLabel(rep), title: rep.label })); + if (!WeeklyDashboard.readOnly) { + const del = el('button', { type: 'button', className: 'wk-del', title: 'Удалить период', ariaLabel: `Удалить период ${rep.label}`, textContent: '×' }); + del.onclick = () => WeeklyDashboard.remove(rep.id); + th.append(del); + } + hr.append(th); + } + thead.append(hr); + table.append(thead); + + const tbody = el('tbody'); + // Original service icons are served locally, without redrawing or recolouring. + const sourceMarks = { + 'сайт': ['#83b8a2', 'website', 'svg'], + '2гис': ['#17a623', '2gis'], '2gis': ['#17a623', '2gis'], + 'яндекс': ['#ff4025', 'yandex'], 'яндекскарточка': ['#ff4025', 'yandex'], + 'продокторов': ['#3581fa', 'prodoctorov'], + 'напоправку': ['#ffb900', 'napopravku'], + 'сберздоровье': ['#20b7c5', 'sber'], + zoon: ['#6934ff', 'zoon'], 'докту': ['#12a344', 'doctu', 'svg'], + 'инстаграм': ['#e4409b', 'instagram'], 'инстаграмм': ['#e4409b', 'instagram'], instagram: ['#e4409b', 'instagram'], + 'вк': ['#0077ff', 'vk'], 'вконтакте': ['#0077ff', 'vk'], vk: ['#0077ff', 'vk'], + telegram: ['#26a5e4', 'telegram'], 'телеграм': ['#26a5e4', 'telegram'], + google: ['#4285f4', 'google'], max: ['#7354ff', 'max'], + }; + const utilityMarks = { + phone: '', + website: '', + internet: '', + banner: '', + tv: '', + route: '', + radio: '', + print: '', + video: '', + chat: '', + generic: '', + }; + const utilityKeys = { + 'сайт': 'website', 'итогоинтернет': 'internet', 'баннер': 'banner', 'баннера': 'banner', 'баннераимаршрутки': 'banner', + 'тв': 'tv', 'маршрутки': 'route', 'радио': 'radio', 'полиграфия': 'print', 'ролик': 'video', + 'медийка': 'video', 'вотсапрассылки': 'chat', + }; + const utilityColors = { phone: '#93bfa7', website: '#83b8a2', internet: '#6d9de0', banner: '#e89b3c', tv: '#9a83cb', route: '#6b9dbc', radio: '#d8a47d', print: '#8598a9', video: '#719dcc', chat: '#5daf83', generic: '#6d9de0' }; + const sourceTint = (color) => { + const match = /^#([\da-f]{6})$/i.exec(color || ''); + if (!match) return ''; + const rgb = [0, 2, 4].map((offset) => parseInt(match[1].slice(offset, offset + 2), 16)); + return `#${rgb.map((value) => Math.round(value + (255 - value) * .955).toString(16).padStart(2, '0')).join('')}`; + }; + // Номера источников — как в таблице «Показатели Дана», лист своей клиники (подпись под названием канала). + const ochevidnoNumbers = { + 'сайт': 'Звонок +79285300063\nВотсап +79285300063', + '2гис': 'Звонок +79285140077\nВотсап +79285140077', + 'яндекскарточка': 'Звонок +79285300213\nВотсап +79285300213', + 'продокторов': '+74993254182', + 'напоправку': '+74993254273', + 'сберздоровье': '+74993254221', + 'zoon': '+74993254228', + 'тв': '+79285300068', + 'баннераимаршрутки': '+79285300066', + 'инстаграм': 'Instagram Direct\nВотсап +79285300077', + }; + const danaNumbers = { + 'сайт': 'Звонок +79289620094\nВотсап +79289620094\nЗаявки с сайта', + '2гис': 'Звонок 89285440094\nВотсап 89285440094', + 'яндекскарточка': 'Звонок 89285088306\nВотсап 89285088306', + 'продокторов': '+74993256643', + 'напоправку': '+74993256599', + 'сберздоровье': '+74993256736', + 'zoon': '+74993256585', + 'докту': '+74993256742', + 'тв': '+7 499 325-72-04', + 'радио': '+7 499 325-49-40', + 'баннера': '89282970055', + 'инстаграм': 'Звонок\n+74993253118 — Эстетик\n+74993253092 — Гинекология\n+74993253084 — Остео\n+74993253079 — Дети\n+74993253016 — Дана медикал\nВотсап\n89280470055', + 'маршрутки': '+7 499 325-69-18', + 'полиграфия': '+7 499 325-70-82', + }; + const sourceNumbers = ReportClinic.key === 'ochevidno' ? ochevidnoNumbers : danaNumbers; + const channelCell = (title, kind, rows, sourceKey) => { + const normalizedKey = WeeklySourceLayout.key(sourceKey); + const titleKey = WeeklySourceLayout.key(title); + let brand = sourceMarks[normalizedKey] || sourceMarks[titleKey]; + // канал «Сайт» — значок сайта своей клиники (у «Очевидно» — с ochevidno.ru); нет значка — общая иконка + const siteLogo = { dana: '/favicon.ico?v=20261005-dana-site-icon', ochevidno: 'assets/channels/ochevidno-site.png?v=20261014' }[ReportClinic.key]; + if (brand && brand[1] === 'website') brand = siteLogo ? (ReportClinic.key === 'ochevidno' ? ['#26365a', 'website'] : brand) : null; + const td = el('td', { className: `wk-channel wk-channel--${kind}`, rowSpan: String(rows) }); + // The reference uses cream for 2GIS and a barely tinted blush for Yandex. + if (brand && kind !== 'pin' && kind !== 'ya') td.style.setProperty('--wk-tint', sourceTint(brand[0])); + const icon = el('span', { className: `wk-channel__icon wk-channel__icon--${brand ? 'brand' : 'utility'}` }); + icon.setAttribute('aria-hidden', 'true'); + if (brand) { + icon.style.setProperty('--wk-source-color', brand[0]); + const mark = el('img', { + src: brand[1] === 'website' ? siteLogo : `assets/channels/${brand[1]}.${brand[2] || 'png'}?v=20261002-original-icons`, + alt: '', width: 32, height: 32, decoding: 'async', + }); + if (brand[1] === 'doctu') { + icon.style.width = '84px'; + mark.style.width = '84px'; + mark.width = 84; + } + icon.append(mark); + } else { + const name = utilityKeys[normalizedKey] || utilityKeys[titleKey] || (kind === 'web' ? 'website' : kind === 'phone' ? 'phone' : 'generic'); + td.style.setProperty('--wk-accent', utilityColors[name]); + if (kind !== 'phone' && kind !== 'web') td.style.setProperty('--wk-tint', sourceTint(utilityColors[name])); + icon.dataset.mark = name; + if (name === 'banner') { + icon.classList.add('wk-channel__icon--banner'); + td.style.setProperty('--wk-accent', '#e89b3c'); + td.style.setProperty('--wk-tint', sourceTint('#e89b3c')); + } + icon.innerHTML = ``; + } + td.append(icon, el('strong', { className: 'wk-channel__title', textContent: title })); + // У колл-центра подписи нет; у источников — их номера (если известны), иначе общая надпись. + if (kind !== 'phone') td.append(el('small', { className: 'wk-channel__hint', textContent: sourceNumbers[normalizedKey] || sourceNumbers[titleKey] || 'Канал обращений' })); + return td; + }; + const metricRow = (label, values, channel, channelKind, channelRows, sourceKey, heat, rule, reference, metric) => { + const tr = el('tr'); + if (channel) tr.append(channelCell(channel, channelKind, channelRows, sourceKey)); + tr.append(el('td', { className: 'wk-metric', textContent: label })); + const colors = heat ? WeeklyExcelFormatting.colors(heat, rule, reference, list.map((r) => r.label), all.map((r) => r.label)) : []; + const fill = WeeklyExcelFormatting.fill(sourceKey, metric); + for (const [i, v] of values.entries()) { + const td = el('td', { className: `wk-value${colors[i] ? ' wk-heat' : ''}`, textContent: v }); + if (colors[i]) { + td.style.setProperty('--wk-heat', colors[i]); + td.title = `Как в Excel: минимум → медиана (50-й процентиль) → максимум. Диапазон: вся история этого показателя и канала. Правило: ${rule.range}`; + } else if (fill && v !== '—' && v !== '') { + td.classList.add('wk-fill'); + td.style.background = fill.color; + td.title = `Заливка строки из Excel: ${fill.range}. Без градиента по значениям.`; + } + tr.append(td); + } + return tr; + }; + const appendGap = () => { + const gap = el('tr', { className: 'wk-gap', ariaHidden: 'true' }); + gap.append(el('td', { colSpan: String(list.length + 2) })); + tbody.append(gap); + }; + // У недель, добавленных из внешней таблицы, колл-центра нет (в базе пустой объект) — показываем «—», а не нули. + const hasCC = (r) => ['total', 'incoming', 'outgoing', 'unique', 'patients', 'revenue'].some((k) => (r.callcenter && r.callcenter[k]) > 0); + const cc = (format) => (r) => hasCC(r) ? format(r) : '—'; + const ccHeat = (pick) => (r) => hasCC(r) ? pick(r) : NaN; + const ccRows = [ + ['Всего звонков', cc((r) => fmtN(r.callcenter.total))], + ['Входящих', cc((r) => fmtN(r.callcenter.incoming))], + ['Исходящих', cc((r) => fmtN(r.callcenter.outgoing))], + ['Не принятых', cc((r) => fmtN(r.callcenter.missed))], + ['Доля пропущенных', cc((r) => fmtPct(r.callcenter.missed_pct))], + ['Уникальных позвонивших', cc((r) => fmtN(r.callcenter.unique)), ccHeat((r) => r.callcenter.unique), 'unique'], + ['Количество пациентов', cc((r) => fmtN(r.callcenter.patients))], + ['Реализованных услуг', cc((r) => fmtN(r.callcenter.services))], + ['Выручка', cc((r) => fmtRub(r.callcenter.revenue)), ccHeat((r) => r.callcenter.revenue), 'revenue'], + ['Средний чек', cc((r) => fmtRub(r.callcenter.avg_check))], + ['Среднее время разговора', cc((r) => fmtTime(r.callcenter.talk_seconds))], + ['Среднее ожидание ответа', cc((r) => fmtTime(r.callcenter.wait_seconds))], + ]; + ccRows.forEach(([label, format, heat, metric], i) => tbody.append(metricRow(label, list.map(format), i === 0 ? 'Колл-центр' : null, 'phone', ccRows.length, 'callcenter', heat ? list.map(heat) : null, WeeklyExcelFormatting.rule('callcenter', metric), heat ? all.map(heat) : null, metric))); + + if (order.length) appendGap(); + + for (const key of order) { + const byRep = list.map((rep) => rep.channels.find((c) => c.key === key)); + const allByRep = all.map((rep) => rep.channels.find((c) => c.key === key)); + const rows = 8; + const kind = /все номера|колл/i.test(titleByKey[key]) ? 'phone' + : /2гис/i.test(titleByKey[key]) ? 'pin' : /яндекс/i.test(titleByKey[key]) ? 'ya' + : /сайт/i.test(titleByKey[key]) ? 'web' : 'source'; + const rowsData = [ + ['Лиды', (c) => c ? fmtN(c.leads) : '—', (c) => c ? c.leads : NaN, 'leads'], + ['Количество пациентов', (c) => c ? fmtN(c.patients) : '—'], + ['Конверсия звонка в пациента', (c) => c && c.leads ? fmtPct((c.patients / c.leads) * 100) : '—', null, 'conversion'], + ['Количество реализованных услуг', (c) => c ? fmtN(c.services) : '—'], + ['Среднее кол. услуг/пациент', (c) => c && c.patients ? fmtF(c.services / c.patients) : '—'], + ['Выручка', (c) => c ? fmtRub(c.revenue) : '—', (c) => c ? c.revenue : NaN, 'revenue'], + ['Средний чек', (c) => c ? fmtRub(c.avg_check) : '—'], + ]; + rowsData.forEach(([label, format, heat, metric], i) => tbody.append(metricRow(label, byRep.map(format), i === 0 ? titleByKey[key] : null, kind, rows, key, heat ? byRep.map(heat) : null, WeeklyExcelFormatting.rule(key, metric), heat ? allByRep.map(heat) : null, metric))); + const budgetRow = el('tr'); + budgetRow.append(el('td', { className: 'wk-metric', textContent: 'Бюджет' })); + byRep.forEach((c, i) => { + const td = el('td', { className: 'wk-value' }); + if (c && key !== 'итого-интернет' && WeeklyDashboard.readOnly) { + const fill = WeeklyExcelFormatting.fill(key, 'budget'); + if (fill) td.style.background = fill.color; + td.textContent = c.budget ? fmtRub(c.budget) : '—'; + } else if (c && key !== 'итого-интернет') { + const fill = WeeklyExcelFormatting.fill(key, 'budget'); + if (fill) td.style.background = fill.color; + const inp = el('input', { type: 'number', min: '0', step: '0.01', value: c.budget ?? '', placeholder: '—', className: 'wk-budget', ariaLabel: `Бюджет: ${titleByKey[key]}, ${list[i].label}` }); + inp.onchange = async () => { + inp.disabled = true; + await WeeklyDashboard.setBudget(list[i].id, key, inp.value); + }; + td.append(inp); + } else { + td.textContent = '—'; + } + budgetRow.append(td); + }); + tbody.append(budgetRow); + if (key !== order.at(-1)) { + appendGap(); + } + } + table.append(tbody); + if (typeof WeeklyFullscreen !== 'undefined') WeeklyFullscreen.sync(); + }, +}; + +$('#wk-filter-from').onchange = () => WeeklyDashboard.render(); +$('#wk-filter-to').onchange = () => WeeklyDashboard.render(); +$('#wk-filter-reset').onclick = () => { + $('#wk-filter-from').value = ''; + $('#wk-filter-to').value = ''; + WeeklyDashboard.render(); +}; diff --git a/internal/api/api.go b/internal/api/api.go new file mode 100644 index 0000000..a3474f3 --- /dev/null +++ b/internal/api/api.go @@ -0,0 +1,327 @@ +package api + +import ( + "encoding/json" + "errors" + "log/slog" + "net/http" + "reflect" + "strings" + "time" + + "dana/internal/bitrix" + "dana/internal/megafon" + "dana/internal/notify" + "dana/internal/onec" + "dana/internal/prodoctorov" + "dana/internal/sberhealth" + "dana/internal/storage" +) + +type API struct { + store *storage.Storage + mediaDir string + notifier notify.Notifier + website *website + pbx *megafon.Client + pbxClinics map[string]*megafon.Client // АТС других клиник (clinics.go) + onec *onec.Client + onecHook string + onecEvery time.Duration + onecLongPoll bool + bitrix *bitrix.Client + smscLogin string + smscPassword string + smscCallbackToken string + prodoctorov *prodoctorov.Client + sberhealth *sberhealth.Client + limiter *rateLimiter + confirmKick chan struct{} // будит подтверждение записей (booking_confirm.go) +} + +// WithBitrix включает разовую выгрузку номеров из Битрикс24 для «Отчёта по каналам». +// Без вебхука кнопка в отчёте просто не предлагается. +func (a *API) WithBitrix(c *bitrix.Client) { a.bitrix = c } + +func (a *API) WithSMSC(login, password string) { a.smscLogin = login; a.smscPassword = password } + +// WithSMSCCallbackToken включает приём обратных вызовов SMSC: без токена в адресе они отклоняются. +func (a *API) WithSMSCCallbackToken(t string) { a.smscCallbackToken = strings.TrimSpace(t) } + +func New(store *storage.Storage, mediaDir string) *API { + return &API{store: store, mediaDir: mediaDir, limiter: newRateLimiter(), confirmKick: make(chan struct{}, 1)} +} + +func (a *API) Routes() http.Handler { + mux := http.NewServeMux() + if a.website != nil { + mux.HandleFunc("GET /robots.txt", a.robots) + mux.HandleFunc("GET /sitemap.xml", a.sitemap) + mux.HandleFunc("GET /", a.websitePage) + } + + mux.HandleFunc("GET /api/v1/health", a.health) + mux.HandleFunc("GET /api/v1/services", a.services) + mux.HandleFunc("GET /api/v1/services/{path...}", a.service) + mux.HandleFunc("GET /api/v1/specialties", a.specialties) + mux.HandleFunc("GET /api/v1/doctors", a.doctors) + mux.HandleFunc("GET /api/v1/doctors/{slug}", a.doctor) + mux.HandleFunc("GET /api/v1/doctors/{slug}/slots", a.doctorSlots) + mux.HandleFunc("GET /api/v1/prices", a.prices) + mux.HandleFunc("GET /api/v1/price-sections", a.priceSections) + + mux.HandleFunc("GET /api/v1/checkups", a.checkups) + mux.HandleFunc("GET /api/v1/checkups/{slug}", a.checkup) + + mux.HandleFunc("GET /api/v1/news", a.posts("news")) + mux.HandleFunc("GET /api/v1/news/{slug}", a.post("news")) + mux.HandleFunc("GET /api/v1/articles", a.posts("article")) + mux.HandleFunc("GET /api/v1/articles/{slug}", a.post("article")) + mux.HandleFunc("GET /api/v1/promos", a.posts("promo")) + mux.HandleFunc("GET /api/v1/promos/{slug}", a.post("promo")) + + mux.HandleFunc("GET /api/v1/reviews", a.reviews) + mux.HandleFunc("GET /api/v1/faq", a.faq) + mux.HandleFunc("GET /api/v1/documents", a.documents) + mux.HandleFunc("GET /api/v1/partners", a.partners) + mux.HandleFunc("GET /api/v1/vacancies", a.vacancies) + mux.HandleFunc("GET /api/v1/branches", a.branches) + mux.HandleFunc("GET /api/v1/gallery", a.gallery) + mux.HandleFunc("GET /api/v1/settings", a.settings) + mux.HandleFunc("GET /api/v1/pages/{slug}", a.page) + mux.HandleFunc("GET /api/v1/banners", a.banners) + mux.HandleFunc("GET /api/v1/search", a.search) + + // Формы и заявки с сайта. + mux.HandleFunc("GET /api/v1/forms", a.forms) + mux.HandleFunc("POST /api/v1/leads", a.createLead) + mux.HandleFunc("POST /api/v1/appointments", a.createAppointment) + mux.HandleFunc("POST /api/v1/auth/wait-call/init", a.waitCallInit) + mux.HandleFunc("GET /api/v1/auth/wait-call/status", a.waitCallStatus) + mux.HandleFunc("POST /api/v1/auth/smsc-callback", a.smscCallback) + mux.HandleFunc("GET /api/v1/auth/smsc-callback", a.smscCallback) + + // --- админка --- + mux.HandleFunc("POST /api/v1/admin/login", a.adminLogin) + mux.HandleFunc("POST /api/v1/admin/logout", a.adminLogout) + mux.HandleFunc("GET /api/v1/admin/me", a.requireAuth(a.adminMe)) + + mux.HandleFunc("GET /api/v1/admin/services", a.requireAuth(a.adminServices)) + mux.HandleFunc("POST /api/v1/admin/services", a.requireAuth(a.adminCreateService)) + mux.HandleFunc("PUT /api/v1/admin/services/{id}", a.requireAuth(a.adminUpdateService)) + + mux.HandleFunc("POST /api/v1/onec/events", a.onecEvents) + mux.HandleFunc("GET /api/v1/admin/onec", a.requireAuth(a.adminOnec)) + mux.HandleFunc("POST /api/v1/admin/onec/sync", a.requireAuth(a.adminOnecSync)) + mux.HandleFunc("PUT /api/v1/admin/onec/doctors/{onec_id}/link", a.requireAuth(a.adminOnecLink)) + mux.HandleFunc("PUT /api/v1/admin/onec/doctors/{onec_id}/booking", a.requireAuth(a.adminOnecBooking)) + mux.HandleFunc("GET /api/v1/admin/onec/grid", a.requireAuth(a.adminOnecGrid)) + mux.HandleFunc("GET /api/v1/admin/onec/appointments", a.requireAuth(a.adminOnecAppointments)) + mux.HandleFunc("GET /api/v1/admin/booking-settings", a.requireAuth(a.adminBookingSettings)) + mux.HandleFunc("PUT /api/v1/admin/booking-settings", a.requireAuth(a.adminUpdateBookingSettings)) + + mux.HandleFunc("GET /api/v1/admin/leads", a.requireAuth(a.adminLeads)) + mux.HandleFunc("PUT /api/v1/admin/leads/{id}", a.requireAuth(a.adminUpdateLead)) + + mux.HandleFunc("GET /api/v1/admin/reviews", a.requireAuth(a.adminReviews)) + mux.HandleFunc("POST /api/v1/admin/reviews", a.requireAuth(a.adminCreateReview)) + mux.HandleFunc("PUT /api/v1/admin/reviews/{id}", a.requireAuth(a.adminUpdateReview)) + mux.HandleFunc("DELETE /api/v1/admin/reviews/{id}", a.requireAuth(a.adminDeleteReview)) + + mux.HandleFunc("GET /api/v1/admin/banners", a.requireAuth(a.adminBanners)) + mux.HandleFunc("POST /api/v1/admin/banners", a.requireAuth(a.adminCreateBanner)) + mux.HandleFunc("PUT /api/v1/admin/banners/{id}", a.requireAuth(a.adminUpdateBanner)) + mux.HandleFunc("DELETE /api/v1/admin/banners/{id}", a.requireAuth(a.adminDeleteBanner)) + mux.HandleFunc("POST /api/v1/admin/media", a.requireAuth(a.adminUploadMedia)) + + mux.HandleFunc("GET /api/v1/admin/doctors", a.requireAuth(a.adminDoctors)) + mux.HandleFunc("GET /api/v1/admin/doctors-quality", a.requireAuth(a.adminDoctorQuality)) + mux.HandleFunc("POST /api/v1/admin/doctors", a.requireAuth(a.adminCreateDoctor)) + mux.HandleFunc("PUT /api/v1/admin/doctors/{id}", a.requireAuth(a.adminUpdateDoctor)) + mux.HandleFunc("GET /api/v1/admin/specialties", a.requireAuth(a.adminSpecialties)) + mux.HandleFunc("POST /api/v1/admin/specialties", a.requireAuth(a.adminCreateSpecialty)) + mux.HandleFunc("PUT /api/v1/admin/specialties/{id}", a.requireAuth(a.adminUpdateSpecialty)) + mux.HandleFunc("DELETE /api/v1/admin/specialties/{id}", a.requireAuth(a.adminDeleteSpecialty)) + mux.HandleFunc("PUT /api/v1/admin/doctors/{id}/links", a.requireAuth(a.adminSetDoctorLinks)) + mux.HandleFunc("GET /api/v1/admin/doctors/{id}/bio", a.requireAuth(a.adminBioEntries)) + mux.HandleFunc("POST /api/v1/admin/bio-entries", a.requireAuth(a.adminCreateBioEntry)) + mux.HandleFunc("PUT /api/v1/admin/bio-entries/{id}", a.requireAuth(a.adminUpdateBioEntry)) + mux.HandleFunc("DELETE /api/v1/admin/bio-entries/{id}", a.requireAuth(a.adminDeleteBioEntry)) + + mux.HandleFunc("GET /api/v1/admin/price-sections", a.requireAuth(a.adminPriceSections)) + mux.HandleFunc("GET /api/v1/admin/price-items", a.requireAuth(a.adminPriceItems)) + mux.HandleFunc("POST /api/v1/admin/price-items", a.requireAuth(a.adminCreatePriceItem)) + mux.HandleFunc("PUT /api/v1/admin/price-items/{id}", a.requireAuth(a.adminUpdatePriceItem)) + mux.HandleFunc("DELETE /api/v1/admin/price-items/{id}", a.requireAuth(a.adminDeletePriceItem)) + + mux.HandleFunc("GET /api/v1/admin/promos", a.requireAuth(a.adminPromos)) + mux.HandleFunc("POST /api/v1/admin/promos", a.requireAuth(a.adminCreatePromo)) + mux.HandleFunc("PUT /api/v1/admin/promos/{id}", a.requireAuth(a.adminUpdatePromo)) + mux.HandleFunc("DELETE /api/v1/admin/promos/{id}", a.requireAuth(a.adminDeletePromo)) + + mux.HandleFunc("GET /api/v1/admin/checkups", a.requireAuth(a.adminCheckups)) + mux.HandleFunc("POST /api/v1/admin/checkups", a.requireAuth(a.adminCreateCheckup)) + mux.HandleFunc("PUT /api/v1/admin/checkups/{id}", a.requireAuth(a.adminUpdateCheckup)) + + // Новости (/admin/news) и статьи (/admin/articles) — одинаковые обработчики с разным видом записи. + for section, kind := range postKinds { + base := "/api/v1/admin/" + section + mux.HandleFunc("GET "+base, a.requireAuth(a.adminPosts(kind))) + mux.HandleFunc("POST "+base, a.requireAuth(a.adminCreatePost(kind))) + mux.HandleFunc("PUT "+base+"/{id}", a.requireAuth(a.adminUpdatePost(kind))) + mux.HandleFunc("DELETE "+base+"/{id}", a.requireAuth(a.adminDeletePost(kind))) + } + + mux.HandleFunc("GET /api/v1/admin/vacancies", a.requireAuth(a.adminVacancies)) + mux.HandleFunc("POST /api/v1/admin/vacancies", a.requireAuth(a.adminCreateVacancy)) + mux.HandleFunc("PUT /api/v1/admin/vacancies/{id}", a.requireAuth(a.adminUpdateVacancy)) + mux.HandleFunc("DELETE /api/v1/admin/vacancies/{id}", a.requireAuth(a.adminDeleteVacancy)) + + mux.HandleFunc("GET /api/v1/admin/documents", a.requireAuth(a.adminDocuments)) + mux.HandleFunc("POST /api/v1/admin/documents", a.requireAuth(a.adminCreateDocument)) + mux.HandleFunc("PUT /api/v1/admin/documents/{id}", a.requireAuth(a.adminUpdateDocument)) + mux.HandleFunc("DELETE /api/v1/admin/documents/{id}", a.requireAuth(a.adminDeleteDocument)) + + mux.HandleFunc("GET /api/v1/admin/partners", a.requireAuth(a.adminPartners)) + mux.HandleFunc("POST /api/v1/admin/partners", a.requireAuth(a.adminCreatePartner)) + mux.HandleFunc("PUT /api/v1/admin/partners/{id}", a.requireAuth(a.adminUpdatePartner)) + mux.HandleFunc("DELETE /api/v1/admin/partners/{id}", a.requireAuth(a.adminDeletePartner)) + + mux.HandleFunc("GET /api/v1/admin/dashboard", a.requireAuth(a.adminDashboard)) + mux.HandleFunc("GET /api/v1/admin/stats", a.requireAuth(a.adminStats)) + mux.HandleFunc("GET /api/v1/admin/clinics", a.requireAuth(a.adminClinics)) + mux.HandleFunc("GET /api/v1/admin/share-link", a.requireAuth(a.adminShareLink)) + mux.HandleFunc("POST /api/v1/admin/share-link", a.requireAuth(a.adminCreateShareLink)) + mux.HandleFunc("DELETE /api/v1/admin/share-link", a.requireAuth(a.adminDeleteShareLink)) + mux.HandleFunc("GET /api/v1/share/weekly-reports", a.shareWeeklyReports) + mux.HandleFunc("GET /api/v1/admin/pbx/status", a.requireAuth(a.adminPBXStatus)) + mux.HandleFunc("POST /api/v1/admin/pbx/check", a.requireAuth(a.adminPBXCheck)) + mux.HandleFunc("POST /api/v1/admin/pbx/sync", a.requireAuth(a.adminPBXSync)) + mux.HandleFunc("GET /api/v1/admin/pbx/lines", a.requireAuth(a.adminPBXLines)) + mux.HandleFunc("PUT /api/v1/admin/pbx/lines", a.requireAuth(a.adminSetPBXLine)) + mux.HandleFunc("GET /api/v1/admin/pbx/report", a.requireAuth(a.adminPBXReport)) + mux.HandleFunc("GET /api/v1/admin/reports", a.requireAuth(a.adminReports)) + mux.HandleFunc("POST /api/v1/admin/reports", a.requireAuth(a.adminCreateReport)) + mux.HandleFunc("POST /api/v1/admin/reports/period", a.requireAuth(a.adminReportPeriod)) + mux.HandleFunc("GET /api/v1/admin/reports/{id}", a.requireAuth(a.adminReport)) + mux.HandleFunc("POST /api/v1/admin/reports/{id}/cancel", a.requireAuth(a.adminCancelReport)) + mux.HandleFunc("DELETE /api/v1/admin/reports/{id}", a.requireAuth(a.adminDeleteReport)) + mux.HandleFunc("GET /api/v1/admin/reports/{id}/download", a.requireAuth(a.adminDownloadReport)) + mux.HandleFunc("GET /api/v1/admin/weekly-reports", a.requireAuth(a.adminWeeklyReports)) + mux.HandleFunc("DELETE /api/v1/admin/weekly-reports/{id}", a.requireAuth(a.adminDeleteWeeklyReport)) + mux.HandleFunc("PUT /api/v1/admin/weekly-reports/{id}/budget", a.requireAuth(a.adminSetWeeklyBudget)) + mux.HandleFunc("GET /api/v1/admin/redirects", a.requireAuth(a.adminRedirects)) + mux.HandleFunc("POST /api/v1/admin/redirects", a.requireAuth(a.adminCreateRedirect)) + mux.HandleFunc("DELETE /api/v1/admin/redirects/{id}", a.requireAuth(a.adminDeleteRedirect)) + mux.HandleFunc("DELETE /api/v1/admin/not-found", a.requireAuth(a.adminDeleteNotFound)) + + mux.HandleFunc("GET /api/v1/admin/pages", a.requireAuth(a.adminPages)) + mux.HandleFunc("PUT /api/v1/admin/pages/{id}", a.requireAuth(a.adminUpdatePage)) + + mux.HandleFunc("GET /api/v1/admin/settings", a.requireAuth(a.adminSettings)) + mux.HandleFunc("PUT /api/v1/admin/settings", a.requireAuth(a.adminUpdateSettings)) + + mux.HandleFunc("GET /api/v1/admin/branches", a.requireAuth(a.adminBranches)) + mux.HandleFunc("POST /api/v1/admin/branches", a.requireAuth(a.adminCreateBranch)) + mux.HandleFunc("PUT /api/v1/admin/branches/{id}", a.requireAuth(a.adminUpdateBranch)) + + mux.HandleFunc("GET /api/v1/admin/faq", a.requireAuth(a.adminFAQ)) + mux.HandleFunc("POST /api/v1/admin/faq", a.requireAuth(a.adminCreateFAQ)) + mux.HandleFunc("PUT /api/v1/admin/faq/{id}", a.requireAuth(a.adminUpdateFAQ)) + mux.HandleFunc("DELETE /api/v1/admin/faq/{id}", a.requireAuth(a.adminDeleteFAQ)) + + // Интерфейс админки (встроен в бинарник). + mux.Handle("GET /admin/", http.StripPrefix("/admin/", http.FileServer(http.FS(adminFS())))) + mux.HandleFunc("GET /admin", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/admin/", http.StatusFound) + }) + + // Раздача загруженных картинок: /media/... -> файлы из mediaDir. + if a.mediaDir != "" { + mux.Handle("GET /media/", mediaHandler(a.mediaDir)) + } + + return logging(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h := w.Header() + // Заголовки безопасности на все ответы. CSP только frame-ancestors — + // защита от кликджекинга без риска сломать SPA (скрипты/стили не трогаем). + h.Set("X-Content-Type-Options", "nosniff") + h.Set("X-Frame-Options", "SAMEORIGIN") + // Безопасные директивы, не ломающие SPA: запрет плагинов, чужого , + // отправки форм на сторонние адреса и встраивания сайта в чужие фреймы. + // Полный script-src/style-src добавляется отдельно (сначала в Report-Only). + h.Set("Content-Security-Policy", "frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'") + h.Set("Referrer-Policy", "strict-origin-when-cross-origin") + h.Set("Strict-Transport-Security", "max-age=31536000") // браузер учитывает только по HTTPS + if strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/admin" || strings.HasPrefix(r.URL.Path, "/admin/") { + h.Set("X-Robots-Tag", "noindex, nofollow") + } + mux.ServeHTTP(w, r) + })) +} + +type envelope struct { + Data any `json:"data"` + Meta map[string]any `json:"meta,omitempty"` +} + +func writeJSON(w http.ResponseWriter, status int, payload any) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(status) + if err := json.NewEncoder(w).Encode(payload); err != nil { + slog.Error("encode response", "err", err) + } +} + +func ok(w http.ResponseWriter, data any, meta map[string]any) { + writeJSON(w, http.StatusOK, envelope{Data: emptyIfNil(data), Meta: meta}) +} + +// emptyIfNil превращает nil-срез в пустой: Go кодирует его как null, и фронт +// падал бы на пустой выборке (поиск без результатов, врачи без отзывов). +func emptyIfNil(data any) any { + v := reflect.ValueOf(data) + if v.Kind() == reflect.Slice && v.IsNil() { + return reflect.MakeSlice(v.Type(), 0, 0).Interface() + } + return data +} + +type apiError struct { + Error struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error"` +} + +func fail(w http.ResponseWriter, status int, code, message string) { + var e apiError + e.Error.Code = code + e.Error.Message = message + writeJSON(w, status, e) +} + +// handleErr переводит ошибки хранилища в ответ API. +func handleErr(w http.ResponseWriter, err error) { + if errors.Is(err, storage.ErrNotFound) { + fail(w, http.StatusNotFound, "not_found", "Не найдено") + return + } + if errors.Is(err, storage.ErrBadReference) { + fail(w, http.StatusUnprocessableEntity, "bad_reference", "Связанная запись не найдена, обновите страницу") + return + } + if errors.Is(err, storage.ErrConflict) { + fail(w, http.StatusConflict, "conflict", "Такой адрес уже занят другой записью") + return + } + slog.Error("request failed", "err", err) + fail(w, http.StatusInternalServerError, "internal", "Внутренняя ошибка") +} + +func logging(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + slog.Debug("request", "method", r.Method, "path", r.URL.Path) + next.ServeHTTP(w, r) + }) +} diff --git a/internal/api/booking_confirm.go b/internal/api/booking_confirm.go new file mode 100644 index 0000000..49e785a --- /dev/null +++ b/internal/api/booking_confirm.go @@ -0,0 +1,405 @@ +package api + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "html" + "log/slog" + "net/http" + "strings" + "time" + + "dana/internal/bitrix" + "dana/internal/onec" + "dana/internal/storage" +) + +// Подтверждение онлайн-записи оператором. Запись сразу занимает окно в 1С, сайт заводит лид +// в Битрикс24 и раз в минуту смотрит его стадию: +// - успех («Клиент записался») — запись подтверждена; +// - провал (любая провальная стадия) — отменяем запись в 1С, окно освобождается; +// - решения нет к сроку (24 ч после записи или 2 ч до приёма, что раньше) — отменяем сами +// и переводим лид в «Клиент не записался». +// Работает, когда настроены и 1С, и вебхук Битрикса; иначе запись ведёт себя как раньше. + +const ( + confirmEvery = time.Minute + // confirmMinGrace — меньше этого оператору на звонок не даём, даже если до приёма совсем мало. + confirmMinGrace = 30 * time.Minute + // bitrixSiteSourceID — источник «Сайт danamedical.ru» в CRM клиники: отчёт по каналам относит его к «Сайту». + bitrixSiteSourceID = "112" +) + +type confirmAction int + +const ( + confirmWait confirmAction = iota // ждём оператора + confirmOK // лид в успешной стадии + confirmCancel // лид в провальной стадии — отменить в 1С + confirmExpire // срок вышел — отменить в 1С самим + confirmMissed // приём уже наступил — ничего не отменяем, просто перестаём ждать +) + +// errBookingLimit — подозрительная активность при выключенном SMSC: окно в 1С не занимаем, +// передаём заявку оператору (со SMSC в этом случае просим проверочный звонок). +var errBookingLimit = errors.New("лимит онлайн-записей (защита расписания от ботов)") + +// bookingRiskReason — почему запись выглядит подозрительной ("" — всё обычно). Срабатывание не отказ: +// сайт просит проверочный звонок, который настоящий пациент делает за минуту, а бот — нет. +func (a *API) bookingRiskReason(ctx context.Context, cfg storage.BookingSettings, doctorOnecID, phone string, start time.Time) (string, error) { + if cfg.MaxPerHour > 0 { + n, err := a.store.SiteBookingsSince(ctx, time.Now().Add(-time.Hour)) + if err != nil { + return "", err + } + if n >= cfg.MaxPerHour { + return fmt.Sprintf("за час %d онлайн-записей при лимите %d", n, cfg.MaxPerHour), nil + } + } + if cfg.MaxPerDoctorDay > 0 && a.bookingConfirmEnabled() { + d := start.In(moscow) + from := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, moscow) + n, err := a.store.WaitingForDoctor(ctx, doctorOnecID, from, from.AddDate(0, 0, 1)) + if err != nil { + return "", err + } + if n >= cfg.MaxPerDoctorDay { + return fmt.Sprintf("у врача на %s уже %d неподтверждённых при лимите %d", from.Format("02.01"), n, cfg.MaxPerDoctorDay), nil + } + } + if cfg.MaxActivePerPhone > 0 { + n, err := a.store.ActiveBookingsForPhone(ctx, lastDigits(phone, 10)) + if err != nil { + return "", err + } + if n >= cfg.MaxActivePerPhone { + return fmt.Sprintf("на номер уже %d будущих записей при лимите %d", n, cfg.MaxActivePerPhone), nil + } + } + return "", nil +} + +func (a *API) bookingConfirmEnabled() bool { + return a.bitrix.Configured() && a.onec.Configured() +} + +// kickConfirm будит фоновую проверку сразу после новой записи, чтобы лид появился без ожидания минуты. +func (a *API) kickConfirm() { + select { + case a.confirmKick <- struct{}{}: + default: + } +} + +// confirmDeadline — когда отменить неподтверждённую запись. ok=false — автоотмена выключена. +func confirmDeadline(created, start time.Time, cfg storage.BookingSettings) (deadline time.Time, ok bool) { + if cfg.AutoCancelHours <= 0 && cfg.CancelBeforeHours <= 0 { + return time.Time{}, false + } + if cfg.AutoCancelHours > 0 { + deadline = created.Add(time.Duration(cfg.AutoCancelHours) * time.Hour) + } + if cfg.CancelBeforeHours > 0 { + before := start.Add(-time.Duration(cfg.CancelBeforeHours) * time.Hour) + if deadline.IsZero() || before.Before(deadline) { + deadline = before + } + } + if earliest := created.Add(confirmMinGrace); deadline.Before(earliest) { + deadline = earliest + } + return deadline, true +} + +// decideConfirm — что делать с записью по стадии лида. semantic пустой — лид ещё в работе или удалён в CRM. +func decideConfirm(semantic string, created, start, now time.Time, cfg storage.BookingSettings) confirmAction { + if semantic == bitrix.SemanticSuccess { + return confirmOK + } + // после начала приёма запись в 1С не трогаем: её судьбу решает регистратура + if !now.Before(start) { + return confirmMissed + } + if semantic == bitrix.SemanticFailure { + return confirmCancel + } + if d, ok := confirmDeadline(created, start, cfg); ok && !now.Before(d) { + return confirmExpire + } + return confirmWait +} + +// RunBookingConfirm — фоновый цикл: заводит лиды и следит за их стадиями. +func (a *API) RunBookingConfirm(ctx context.Context) { + if !a.bookingConfirmEnabled() { + slog.Info("booking confirm disabled: нужны и 1С, и BITRIX_WEBHOOK_URL") + return + } + slog.Info("booking confirm enabled: онлайн-записи подтверждает оператор в Битрикс24") + t := time.NewTicker(confirmEvery) + defer t.Stop() + for { + c, cancel := context.WithTimeout(ctx, 2*time.Minute) + if err := a.processConfirmQueue(c, time.Now()); err != nil { + slog.Warn("booking confirm", "err", err) + } + cancel() + select { + case <-ctx.Done(): + return + case <-t.C: + case <-a.confirmKick: + } + } +} + +func (a *API) processConfirmQueue(ctx context.Context, now time.Time) error { + if err := a.sendLeadOnly(ctx); err != nil { + return err + } + cfg, err := a.store.BookingSettings(ctx) + if err != nil { + return fmt.Errorf("настройки записи: %w", err) + } + items, err := a.store.ConfirmQueue(ctx, 100) + if err != nil { + return err + } + if len(items) == 0 { + return nil + } + + // 1. Лиды для новых записей. Не получилось — запись ждёт следующей попытки и автоматически + // не отменяется: оператор её ещё не видел. + var ids []string + for i := range items { + it := &items[i] + if it.BitrixLeadID == "" { + id, err := a.bitrix.AddLead(ctx, leadDraft(*it, cfg)) + if err != nil { + slog.Warn("booking confirm: лид не создан", "appointment", it.ID, "err", err) + _ = a.store.SetConfirmError(ctx, it.ID, "Битрикс: "+err.Error()) + continue + } + if err := a.store.SetBitrixLead(ctx, it.ID, id); err != nil { + return err + } + it.BitrixLeadID = id + slog.Info("booking confirm: лид создан", "appointment", it.ID, "lead", id) + } + ids = append(ids, it.BitrixLeadID) + } + if len(ids) == 0 { + return nil + } + + // 2. Стадии лидов. Без ответа Битрикса ничего не отменяем: вдруг оператор уже подтвердил. + sem, err := a.bitrix.LeadSemantics(ctx, ids) + if err != nil { + return err + } + for _, it := range items { + if it.BitrixLeadID == "" { + continue + } + switch decideConfirm(sem[it.BitrixLeadID], it.CreatedAt, it.Start, now, cfg) { + case confirmOK: + a.finishConfirm(ctx, it, "confirmed") + case confirmMissed: + a.finishConfirm(ctx, it, "missed") + case confirmCancel: + if a.cancelUnconfirmed(ctx, it) { + a.finishConfirm(ctx, it, "cancelled") + a.leadNote(ctx, it, "Сайт отменил запись в 1С: лид переведён в провальную стадию. Окно у врача снова свободно.") + } + case confirmExpire: + if a.cancelUnconfirmed(ctx, it) { + a.finishConfirm(ctx, it, "expired") + if err := a.bitrix.FailLead(ctx, it.BitrixLeadID); err != nil { + slog.Warn("booking confirm: стадия лида не обновлена", "lead", it.BitrixLeadID, "err", err) + } + a.leadNote(ctx, it, "Запись не подтвердили вовремя — сайт отменил её в 1С и перевёл лид в «Клиент не записался». "+ + "Если пациент всё же придёт, запишите его заново.") + } + } + } + return nil +} + +// sendLeadOnly передаёт оператору заявки, которые не удалось записать в 1С (1С недоступна или +// отклонила запись). Подтверждать тут нечего — записи в 1С нет, оператор записывает вручную. +func (a *API) sendLeadOnly(ctx context.Context) error { + items, err := a.store.LeadOnlyQueue(ctx, 50) + if err != nil { + return err + } + for _, it := range items { + id, err := a.bitrix.AddLead(ctx, leadOnlyDraft(it)) + if err != nil { + slog.Warn("booking confirm: лид по заявке не создан", "appointment", it.ID, "err", err) + _ = a.store.SetConfirmError(ctx, it.ID, "Битрикс: "+err.Error()) + continue + } + if err := a.store.LeadOnlySent(ctx, it.ID, id); err != nil { + return err + } + slog.Info("booking confirm: заявка передана в Битрикс", "appointment", it.ID, "lead", id) + } + return nil +} + +func (a *API) finishConfirm(ctx context.Context, it storage.ConfirmItem, state string) { + if err := a.store.FinishBookingConfirm(ctx, it.ID, state); err != nil { + slog.Error("booking confirm: состояние не сохранено", "appointment", it.ID, "state", state, "err", err) + return + } + slog.Info("booking confirm", "appointment", it.ID, "lead", it.BitrixLeadID, "state", state) +} + +// cancelUnconfirmed отменяет запись в 1С. false — 1С не ответила, попробуем на следующем круге. +func (a *API) cancelUnconfirmed(ctx context.Context, it storage.ConfirmItem) bool { + if it.OnecID != "" { + err := a.onec.Cancel(ctx, it.OnecID) + // 404 1С возвращает как «не найден»: записи там уже нет — значит, и отменять нечего + if err != nil && !errors.Is(err, onec.ErrDoctorNotFound) { + slog.Warn("booking confirm: 1С не отменила запись", "appointment", it.ID, "err", err) + _ = a.store.SetConfirmError(ctx, it.ID, "1С: "+onecErrText(err)) + return false + } + } + if err := a.resyncOnecDoctor(ctx, it.DoctorOnec); err != nil { + slog.Warn("booking confirm: окна врача не обновились", "doctor", it.DoctorOnec, "err", err) + } + return true +} + +func (a *API) leadNote(ctx context.Context, it storage.ConfirmItem, text string) { + if err := a.bitrix.AddLeadComment(ctx, it.BitrixLeadID, text); err != nil { + slog.Warn("booking confirm: комментарий в лид не добавлен", "lead", it.BitrixLeadID, "err", err) + } +} + +// leadDraft — лид для оператора: всё, что нужно для звонка, и что будет, если не подтвердить. +// Поля пациента экранируются: в COMMENTS Битрикс показывает HTML. +func leadDraft(it storage.ConfirmItem, cfg storage.BookingSettings) bitrix.LeadDraft { + when := it.Start.In(moscow).Format("02.01 15:04") + phone := it.Phone + if n := normalizeRuPhone(it.Phone); n != "" { + phone = "+" + n + } + lines := []string{ + "Пациент записался онлайн, окно в 1С уже занято за ним.", + "Врач: " + html.EscapeString(it.DoctorName), + "Приём: " + formatSlotTime(it.Start), + "Филиал: " + html.EscapeString(branchOrDash(it.Branch)), + } + if it.ServiceName != "" { + lines = append(lines, "Услуга: "+html.EscapeString(it.ServiceName)) + } + if it.OnecID != "" { + lines = append(lines, "№ записи в 1С: "+html.EscapeString(it.OnecID)) + } + lines = append(lines, "", + "Позвоните пациенту. Подтвердил — переведите лид в «Клиент записался». "+ + "Любая провальная стадия отменит запись в 1С.") + if d, ok := confirmDeadline(it.CreatedAt, it.Start, cfg); ok { + lines = append(lines, "Без подтверждения запись отменится автоматически "+d.In(moscow).Format("02.01 в 15:04")+".") + } + return bitrix.LeadDraft{ + Title: "Онлайн-запись: " + it.DoctorName + " — " + when, + Name: it.Patient, + Phone: phone, + SourceID: bitrixSiteSourceID, + SourceDescr: "Онлайн-запись на сайте", + Comments: strings.Join(lines, "
"), + } +} + +// leadOnlyDraft — лид для заявки без записи в 1С. Причину сбоя 1С не пишем: в ней внутренние адреса. +func leadOnlyDraft(it storage.ConfirmItem) bitrix.LeadDraft { + phone := it.Phone + if n := normalizeRuPhone(it.Phone); n != "" { + phone = "+" + n + } + reason := "Пациент хотел записаться онлайн, но запись в 1С не прошла: 1С была недоступна или отклонила запись." + if strings.HasPrefix(it.Error, errBookingLimit.Error()) { + reason = "Пациент хотел записаться онлайн, но сработала защита от ботов: слишком много неподтверждённых онлайн-записей. " + + "Если пациент настоящий — запишите его." + } + lines := []string{ + reason, + "Окно за пациентом НЕ закреплено.", + "Врач: " + html.EscapeString(it.DoctorName), + "Выбранное время: " + formatSlotTime(it.Start), + "Филиал: " + html.EscapeString(branchOrDash(it.Branch)), + } + if it.ServiceName != "" { + lines = append(lines, "Услуга: "+html.EscapeString(it.ServiceName)) + } + lines = append(lines, "", "Позвоните пациенту и запишите его в 1С вручную.") + return bitrix.LeadDraft{ + Title: "Заявка на запись: " + it.DoctorName + " — " + it.Start.In(moscow).Format("02.01 15:04"), + Name: it.Patient, + Phone: phone, + SourceID: bitrixSiteSourceID, + SourceDescr: "Онлайн-запись на сайте (1С не записала)", + Comments: strings.Join(lines, "
"), + } +} + +// ---------- Настройки в админке ---------- + +func (a *API) adminBookingSettings(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + cfg, err := a.store.BookingSettings(r.Context()) + if err != nil { + handleErr(w, err) + return + } + ok(w, map[string]any{ + "settings": cfg, + "confirm_enabled": a.bookingConfirmEnabled(), + "bitrix_configured": a.bitrix.Configured(), + "smsc_configured": a.smscLogin != "", + }, nil) +} + +func (a *API) adminUpdateBookingSettings(w http.ResponseWriter, r *http.Request, u *storage.AdminUser) { + var in storage.BookingSettings + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4<<10)).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_request", "Некорректный запрос") + return + } + if in.AutoCancelHours < 0 || in.AutoCancelHours > 168 { + fail(w, http.StatusUnprocessableEntity, "validation", "Автоотмена — от 0 до 168 часов после записи (0 — не отменять)") + return + } + if in.CancelBeforeHours < 0 || in.CancelBeforeHours > 48 { + fail(w, http.StatusUnprocessableEntity, "validation", "Отмена до приёма — от 0 до 48 часов (0 — не отменять)") + return + } + for _, c := range []struct { + v, max int + msg string + }{ + {in.MaxPerDoctorDay, 50, "Неподтверждённых к врачу в день — от 0 до 50 (0 — без лимита)"}, + {in.MaxPerHour, 1000, "Онлайн-записей за час — от 0 до 1000 (0 — без лимита)"}, + {in.MaxActivePerPhone, 20, "Будущих записей на номер — от 0 до 20 (0 — без лимита)"}, + } { + if c.v < 0 || c.v > c.max { + fail(w, http.StatusUnprocessableEntity, "validation", c.msg) + return + } + } + if in.PhoneCheck && a.smscLogin == "" { + fail(w, http.StatusUnprocessableEntity, "validation", "Проверочный звонок недоступен: на сервере не настроен SMSC") + return + } + if err := a.store.SetBookingSettings(r.Context(), in); err != nil { + handleErr(w, err) + return + } + a.store.Audit(r.Context(), u.ID, "update", "booking_settings", 0, clientIP(r)) + a.adminBookingSettings(w, r, u) +} diff --git a/internal/api/booking_confirm_test.go b/internal/api/booking_confirm_test.go new file mode 100644 index 0000000..634ac71 --- /dev/null +++ b/internal/api/booking_confirm_test.go @@ -0,0 +1,106 @@ +package api + +import ( + "strings" + "testing" + "time" + + "dana/internal/bitrix" + "dana/internal/storage" +) + +func TestConfirmDeadline(t *testing.T) { + cfg := storage.DefaultBookingSettings // 24 ч после записи или 2 ч до приёма + created := time.Date(2026, 10, 9, 22, 0, 0, 0, moscow) + cases := []struct { + name string + start time.Time + want time.Time + }{ + {"приём через три дня — 24 ч после записи", created.Add(72 * time.Hour), created.Add(24 * time.Hour)}, + {"приём завтра утром — за 2 ч до приёма", created.Add(12 * time.Hour), created.Add(10 * time.Hour)}, + {"приём через час — не раньше 30 минут после записи", created.Add(time.Hour), created.Add(30 * time.Minute)}, + } + for _, c := range cases { + got, ok := confirmDeadline(created, c.start, cfg) + if !ok || !got.Equal(c.want) { + t.Errorf("%s: срок %v, ждали %v", c.name, got, c.want) + } + } + if _, ok := confirmDeadline(created, created.Add(72*time.Hour), storage.BookingSettings{}); ok { + t.Error("оба правила выключены — автоотмены быть не должно") + } +} + +func TestDecideConfirm(t *testing.T) { + cfg := storage.DefaultBookingSettings + created := time.Date(2026, 10, 9, 10, 0, 0, 0, moscow) + start := created.Add(48 * time.Hour) + soon := created.Add(time.Hour) + late := created.Add(25 * time.Hour) // срок (24 ч) прошёл, приём ещё впереди + cases := []struct { + name string + semantic string + now time.Time + want confirmAction + }{ + {"оператор подтвердил", bitrix.SemanticSuccess, soon, confirmOK}, + {"оператор отменил", bitrix.SemanticFailure, soon, confirmCancel}, + {"в работе, срок не вышел", bitrix.SemanticProcess, soon, confirmWait}, + {"лид удалён, срок не вышел", "", soon, confirmWait}, + {"в работе, срок вышел", bitrix.SemanticProcess, late, confirmExpire}, + {"подтвердили даже после срока — подтверждаем", bitrix.SemanticSuccess, late, confirmOK}, + {"провал после начала приёма — в 1С не трогаем", bitrix.SemanticFailure, start.Add(time.Minute), confirmMissed}, + {"никто не ответил до приёма — в 1С не трогаем", bitrix.SemanticProcess, start, confirmMissed}, + } + for _, c := range cases { + if got := decideConfirm(c.semantic, created, start, c.now, cfg); got != c.want { + t.Errorf("%s: %v, ждали %v", c.name, got, c.want) + } + } + if got := decideConfirm(bitrix.SemanticProcess, created, start, late, storage.BookingSettings{}); got != confirmWait { + t.Errorf("автоотмена выключена: %v, ждали ожидание", got) + } +} + +func TestLeadOnlyDraftAsksToBookManually(t *testing.T) { + it := storage.ConfirmItem{ + Start: time.Date(2026, 10, 12, 15, 30, 0, 0, moscow), Patient: "Анна", Phone: "+7 928 000 00 00", + DoctorName: "Петрова `); diff --git a/internal/api/video.go b/internal/api/video.go new file mode 100644 index 0000000..2478a47 --- /dev/null +++ b/internal/api/video.go @@ -0,0 +1,61 @@ +package api + +import ( + "errors" + "net/url" + "regexp" + "strings" +) + +var ( + youtubeIDRE = regexp.MustCompile(`^[A-Za-z0-9_-]{11}$`) + rutubeIDRE = regexp.MustCompile(`^[a-f0-9]{32}$`) + errVideoURL = errors.New("video url") +) + +// embedVideoURL приводит ссылку на видеоотзыв к адресу для встраивания. +// Адрес попадает в src у iframe, поэтому пропускаем только YouTube и Rutube: +// произвольная ссылка (например, javascript:) выполнилась бы на странице сайта. +func embedVideoURL(raw string) (string, error) { + raw = strings.TrimSpace(raw) + if raw == "" { + return "", nil + } + u, err := url.Parse(raw) + if err != nil || (u.Scheme != "https" && u.Scheme != "http") { + return "", errVideoURL + } + host := strings.TrimPrefix(strings.ToLower(u.Hostname()), "www.") + host = strings.TrimPrefix(host, "m.") + parts := strings.Split(strings.Trim(u.Path, "/"), "/") + + youtube := func(id string) (string, error) { + if !youtubeIDRE.MatchString(id) { + return "", errVideoURL + } + return "https://www.youtube.com/embed/" + id, nil + } + switch host { + case "youtu.be": + return youtube(parts[0]) + case "youtube.com", "youtube-nocookie.com": + switch { + case len(parts) == 1 && parts[0] == "watch": + return youtube(u.Query().Get("v")) + case len(parts) == 2 && (parts[0] == "embed" || parts[0] == "shorts" || parts[0] == "live"): + return youtube(parts[1]) + } + case "rutube.ru": + // rutube.ru/video// и rutube.ru/play/embed/ + id := "" + if len(parts) == 2 && parts[0] == "video" { + id = parts[1] + } else if len(parts) == 3 && parts[0] == "play" && parts[1] == "embed" { + id = parts[2] + } + if rutubeIDRE.MatchString(id) { + return "https://rutube.ru/play/embed/" + id, nil + } + } + return "", errVideoURL +} diff --git a/internal/api/video_test.go b/internal/api/video_test.go new file mode 100644 index 0000000..38d5a9d --- /dev/null +++ b/internal/api/video_test.go @@ -0,0 +1,34 @@ +package api + +import "testing" + +func TestEmbedVideoURL(t *testing.T) { + good := map[string]string{ + "": "", + "https://www.youtube.com/embed/iZ-PIqFOY0M": "https://www.youtube.com/embed/iZ-PIqFOY0M", + "https://youtu.be/V1w8DhkWARc?si=abc": "https://www.youtube.com/embed/V1w8DhkWARc", + "https://www.youtube.com/watch?v=-oaWiVEUSx8": "https://www.youtube.com/embed/-oaWiVEUSx8", + "https://m.youtube.com/shorts/V1w8DhkWARc": "https://www.youtube.com/embed/V1w8DhkWARc", + " https://rutube.ru/video/0123456789abcdef0123456789abcdef/ ": "https://rutube.ru/play/embed/0123456789abcdef0123456789abcdef", + "https://rutube.ru/play/embed/0123456789abcdef0123456789abcdef": "https://rutube.ru/play/embed/0123456789abcdef0123456789abcdef", + } + for in, want := range good { + got, err := embedVideoURL(in) + if err != nil || got != want { + t.Errorf("embedVideoURL(%q) = %q, %v; want %q", in, got, err, want) + } + } + for _, in := range []string{ + "javascript:alert(1)", + "https://evil.example/embed/iZ-PIqFOY0M", + "https://youtube.com.evil.example/embed/iZ-PIqFOY0M", + "https://www.youtube.com/embed/iZ-PIqFOY0M/../../evil", + "https://www.youtube.com/watch?v=", + "//www.youtube.com/embed/iZ-PIqFOY0M", + } { + if got, err := embedVideoURL(in); err == nil { + t.Errorf("embedVideoURL(%q) accepted as %q", in, got) + } + } +} diff --git a/internal/api/waitcall.go b/internal/api/waitcall.go new file mode 100644 index 0000000..b687a7f --- /dev/null +++ b/internal/api/waitcall.go @@ -0,0 +1,343 @@ +package api + +import ( + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "log/slog" + "net/http" + "net/url" + "strings" + "sync" + "time" +) + +type WaitCallSession struct { + ID string `json:"session_id"` + Phone string `json:"phone"` // e.g. "79896657520" + TargetPhone string `json:"target_phone"` // e.g. "79019605980" + DisplayPhone string `json:"display_phone"` // e.g. "+7 (901) 960-59-80" + Verified bool `json:"verified"` + CreatedAt time.Time `json:"created_at"` + ExpiresAt time.Time `json:"expires_at"` +} + +type waitCallManager struct { + mu sync.RWMutex + sessions map[string]*WaitCallSession + phoneSessions map[string][]*WaitCallSession + lastAnswerPoll time.Time +} + +var wcm = &waitCallManager{ + sessions: make(map[string]*WaitCallSession), + phoneSessions: make(map[string][]*WaitCallSession), +} + +func (m *waitCallManager) set(s *WaitCallSession) { + m.mu.Lock() + defer m.mu.Unlock() + m.sessions[s.ID] = s + m.phoneSessions[s.Phone] = append(m.phoneSessions[s.Phone], s) +} + +func (m *waitCallManager) get(id string) *WaitCallSession { + m.mu.RLock() + defer m.mu.RUnlock() + s, ok := m.sessions[id] + if !ok || time.Now().After(s.ExpiresAt) { + return nil + } + return s +} + +func (m *waitCallManager) isVerified(id, phone string) bool { + m.mu.RLock() + defer m.mu.RUnlock() + s, ok := m.sessions[id] + if !ok || time.Now().After(s.ExpiresAt) { + return false + } + norm := normalizeRuPhone(phone) + return s.Verified && (s.Phone == norm || s.Phone == phone) +} + +// consume гасит подтверждение после успешной записи: одно подтверждение — одна запись. +func (m *waitCallManager) consume(id string) { + m.mu.Lock() + defer m.mu.Unlock() + if s, ok := m.sessions[id]; ok { + s.Verified = false + s.ExpiresAt = time.Now() + } +} + +func (m *waitCallManager) markVerified(phone string) int { + m.mu.Lock() + defer m.mu.Unlock() + norm := normalizeRuPhone(phone) + list, ok := m.phoneSessions[norm] + if !ok { + return 0 + } + count := 0 + now := time.Now() + for _, s := range list { + if now.Before(s.ExpiresAt) { + s.Verified = true + count++ + } + } + return count +} + +func (m *waitCallManager) checkSMSCFallback(login, password, targetPhone string) { + if login == "" || password == "" { + return + } + m.mu.Lock() + if time.Since(m.lastAnswerPoll) < 3*time.Second { + m.mu.Unlock() + return + } + m.lastAnswerPoll = time.Now() + m.mu.Unlock() + + endpoint := fmt.Sprintf( + "https://smsc.ru/sys/get.php?get_answers=1&hour=1&login=%s&psw=%s&fmt=3", + url.QueryEscape(login), + url.QueryEscape(password), + ) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + if err != nil { + return + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + var answers []struct { + ID any `json:"id"` + Phone string `json:"phone"` + Message string `json:"message"` + Text string `json:"text"` + } + if err := json.NewDecoder(resp.Body).Decode(&answers); err != nil { + return + } + + for _, a := range answers { + txt := a.Message + if txt == "" { + txt = a.Text + } + if strings.Contains(txt, "[waitcall]") || strings.Contains(txt, "waitcall") || txt == "" { + norm := normalizeRuPhone(a.Phone) + if norm != "" { + m.markVerified(norm) + } + } + } +} + +// formatRuPhone formats Russian phone number to "+7 (XXX) XXX-XX-XX" +func formatRuPhone(p string) string { + d := onlyDigits(p) + if len(d) == 11 && (d[0] == '7' || d[0] == '8') { + return fmt.Sprintf("+7 (%s) %s-%s-%s", d[1:4], d[4:7], d[7:9], d[9:11]) + } + if len(d) == 10 { + return fmt.Sprintf("+7 (%s) %s-%s-%s", d[0:3], d[3:6], d[6:8], d[8:10]) + } + return "+" + d +} + +// normalizeRuPhone converts various formats to "7XXXXXXXXXX" +func normalizeRuPhone(p string) string { + d := onlyDigits(p) + if len(d) == 10 { + return "7" + d + } + if len(d) == 11 && (d[0] == '8' || d[0] == '7') { + return "7" + d[1:] + } + return d +} + +func (a *API) waitCallInit(w http.ResponseWriter, r *http.Request) { + if a.tooMany(w, "wc_init:"+clientIP(r), 10, 5*time.Minute) { + return + } + if a.smscLogin == "" || a.smscPassword == "" { + fail(w, http.StatusServiceUnavailable, "verification_disabled", "Подтверждение номера сейчас недоступно") + return + } + var in struct { + Phone string `json:"phone"` + } + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1024)).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_json", "Некорректный запрос") + return + } + phone := normalizeRuPhone(in.Phone) + if len(phone) != 11 || phone[0] != '7' { + fail(w, http.StatusBadRequest, "invalid_phone", "Укажите корректный российский номер телефона") + return + } + if a.tooMany(w, "wc_phone:"+phone, 4, 5*time.Minute) { + return + } + + endpoint := fmt.Sprintf( + "https://smsc.ru/sys/wait_call.php?login=%s&psw=%s&phone=%s&fmt=3", + url.QueryEscape(a.smscLogin), + url.QueryEscape(a.smscPassword), + url.QueryEscape(phone), + ) + + ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + if err != nil { + handleErr(w, err) + return + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + // в адресе логин и пароль SMSC — в лог пишем ошибку без него + slog.Error("smsc wait_call request failed", "err", redactURL(err)) + fail(w, http.StatusBadGateway, "smsc_error", "Сервис проверки номеров временно недоступен") + return + } + defer resp.Body.Close() + + var smscResp struct { + Phone string `json:"phone"` + AllPhones []string `json:"all_phones"` + Error string `json:"error"` + ErrorCode int `json:"error_code"` + } + if err := json.NewDecoder(resp.Body).Decode(&smscResp); err != nil { + slog.Error("smsc wait_call decode failed", "err", err) + fail(w, http.StatusBadGateway, "smsc_error", "Ошибка ответа сервиса проверки") + return + } + if smscResp.ErrorCode != 0 { + slog.Warn("smsc wait_call error", "code", smscResp.ErrorCode, "err", smscResp.Error) + msg := "Не удалось получить номер для проверки" + if smscResp.ErrorCode == 9 { + msg = "Слишком много запросов. Пожалуйста, подождите 1 минуту." + } + fail(w, http.StatusBadRequest, "smsc_error", msg) + return + } + + targetPhone := smscResp.Phone + if targetPhone == "" && len(smscResp.AllPhones) > 0 { + targetPhone = smscResp.AllPhones[0] + } + if targetPhone == "" { + fail(w, http.StatusBadGateway, "smsc_error", "Не получен номер для звонка") + return + } + + b := make([]byte, 16) + _, _ = rand.Read(b) + sessionID := hex.EncodeToString(b) + + sess := &WaitCallSession{ + ID: sessionID, + Phone: phone, + TargetPhone: targetPhone, + DisplayPhone: formatRuPhone(targetPhone), + Verified: false, + CreatedAt: time.Now(), + ExpiresAt: time.Now().Add(10 * time.Minute), + } + wcm.set(sess) + + ok(w, map[string]any{ + "session_id": sess.ID, + "target_phone": sess.TargetPhone, + "display_phone": sess.DisplayPhone, + "expires_in": 600, + }, nil) +} + +func (a *API) waitCallStatus(w http.ResponseWriter, r *http.Request) { + sessionID := r.URL.Query().Get("session_id") + if sessionID == "" { + fail(w, http.StatusBadRequest, "missing_session", "Укажите session_id") + return + } + sess := wcm.get(sessionID) + if sess == nil { + fail(w, http.StatusNotFound, "not_found", "Сессия не найдена или истекла") + return + } + + if !sess.Verified { + wcm.checkSMSCFallback(a.smscLogin, a.smscPassword, sess.Phone) + } + + ok(w, map[string]any{ + "session_id": sess.ID, + "phone": sess.Phone, + "target_phone": sess.TargetPhone, + "display_phone": sess.DisplayPhone, + "verified": sess.Verified, + }, nil) +} + +func (a *API) smscCallback(w http.ResponseWriter, r *http.Request) { + // Обратный вызов приходит из интернета, поэтому без секрета в адресе его не принимаем: + // подтверждение номера всё равно придёт через опрос ответов SMSC (waitCallStatus). + if a.smscCallbackToken == "" || !sameKey(r.URL.Query().Get("token"), a.smscCallbackToken) { + slog.Warn("smsc: callback rejected (нет или неверный token)", "ip", clientIP(r)) + fail(w, http.StatusForbidden, "forbidden", "Запрещено") + return + } + _ = r.ParseForm() + phone := r.FormValue("phone") + if phone == "" { + phone = r.URL.Query().Get("phone") + } + waitcall := r.FormValue("waitcall") + if waitcall == "" { + waitcall = r.URL.Query().Get("waitcall") + } + + if phone == "" && r.Body != nil { + var j struct { + Phone string `json:"phone"` + Waitcall any `json:"waitcall"` + } + _ = json.NewDecoder(r.Body).Decode(&j) + if j.Phone != "" { + phone = j.Phone + } + if j.Waitcall != nil { + waitcall = fmt.Sprint(j.Waitcall) + } + } + + slog.Info("smsc: callback received", "phone", maskPhone(phone), "waitcall", waitcall) + + norm := normalizeRuPhone(phone) + if norm != "" { + verifiedCount := wcm.markVerified(norm) + slog.Info("smsc: sessions marked verified", "phone", maskPhone(norm), "count", verifiedCount) + } + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) +} diff --git a/internal/api/waitcall.go.root-backup b/internal/api/waitcall.go.root-backup new file mode 100644 index 0000000..8f530dd --- /dev/null +++ b/internal/api/waitcall.go.root-backup @@ -0,0 +1,327 @@ +package api + +import ( + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "log/slog" + "net/http" + "net/url" + "strings" + "sync" + "time" +) + +type WaitCallSession struct { + ID string `json:"session_id"` + Phone string `json:"phone"` // e.g. "79896657520" + TargetPhone string `json:"target_phone"` // e.g. "79019605980" + DisplayPhone string `json:"display_phone"` // e.g. "+7 (901) 960-59-80" + Verified bool `json:"verified"` + CreatedAt time.Time `json:"created_at"` + ExpiresAt time.Time `json:"expires_at"` +} + +type waitCallManager struct { + mu sync.RWMutex + sessions map[string]*WaitCallSession + phoneSessions map[string][]*WaitCallSession + lastAnswerPoll time.Time +} + +var wcm = &waitCallManager{ + sessions: make(map[string]*WaitCallSession), + phoneSessions: make(map[string][]*WaitCallSession), +} + +func (m *waitCallManager) set(s *WaitCallSession) { + m.mu.Lock() + defer m.mu.Unlock() + m.sessions[s.ID] = s + m.phoneSessions[s.Phone] = append(m.phoneSessions[s.Phone], s) +} + +func (m *waitCallManager) get(id string) *WaitCallSession { + m.mu.RLock() + defer m.mu.RUnlock() + s, ok := m.sessions[id] + if !ok || time.Now().After(s.ExpiresAt) { + return nil + } + return s +} + +func (m *waitCallManager) isVerified(id, phone string) bool { + m.mu.RLock() + defer m.mu.RUnlock() + s, ok := m.sessions[id] + if !ok || time.Now().After(s.ExpiresAt) { + return false + } + norm := normalizeRuPhone(phone) + return s.Verified && (s.Phone == norm || s.Phone == phone) +} + +func (m *waitCallManager) markVerified(phone string) int { + m.mu.Lock() + defer m.mu.Unlock() + norm := normalizeRuPhone(phone) + list, ok := m.phoneSessions[norm] + if !ok { + return 0 + } + count := 0 + now := time.Now() + for _, s := range list { + if now.Before(s.ExpiresAt) { + s.Verified = true + count++ + } + } + return count +} + +func (m *waitCallManager) checkSMSCFallback(login, password, targetPhone string) { + if login == "" || password == "" { + return + } + m.mu.Lock() + if time.Since(m.lastAnswerPoll) < 3*time.Second { + m.mu.Unlock() + return + } + m.lastAnswerPoll = time.Now() + m.mu.Unlock() + + endpoint := fmt.Sprintf( + "https://smsc.ru/sys/get.php?get_answers=1&hour=1&login=%s&psw=%s&fmt=3", + url.QueryEscape(login), + url.QueryEscape(password), + ) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + if err != nil { + return + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + + var answers []struct { + ID any `json:"id"` + Phone string `json:"phone"` + Message string `json:"message"` + Text string `json:"text"` + } + if err := json.NewDecoder(resp.Body).Decode(&answers); err != nil { + return + } + + for _, a := range answers { + txt := a.Message + if txt == "" { + txt = a.Text + } + if strings.Contains(txt, "[waitcall]") || strings.Contains(txt, "waitcall") || txt == "" { + norm := normalizeRuPhone(a.Phone) + if norm != "" { + m.markVerified(norm) + } + } + } +} + +// formatRuPhone formats Russian phone number to "+7 (XXX) XXX-XX-XX" +func formatRuPhone(p string) string { + d := onlyDigits(p) + if len(d) == 11 && (d[0] == '7' || d[0] == '8') { + return fmt.Sprintf("+7 (%s) %s-%s-%s", d[1:4], d[4:7], d[7:9], d[9:11]) + } + if len(d) == 10 { + return fmt.Sprintf("+7 (%s) %s-%s-%s", d[0:3], d[3:6], d[6:8], d[8:10]) + } + return "+" + d +} + +// normalizeRuPhone converts various formats to "7XXXXXXXXXX" +func normalizeRuPhone(p string) string { + d := onlyDigits(p) + if len(d) == 10 { + return "7" + d + } + if len(d) == 11 && (d[0] == '8' || d[0] == '7') { + return "7" + d[1:] + } + return d +} + +func (a *API) waitCallInit(w http.ResponseWriter, r *http.Request) { + if a.tooMany(w, "wc_init:"+clientIP(r), 10, 5*time.Minute) { + return + } + var in struct { + Phone string `json:"phone"` + } + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1024)).Decode(&in); err != nil { + fail(w, http.StatusBadRequest, "bad_json", "Некорректный запрос") + return + } + phone := normalizeRuPhone(in.Phone) + if len(phone) != 11 || phone[0] != '7' { + fail(w, http.StatusBadRequest, "invalid_phone", "Укажите корректный российский номер телефона") + return + } + if a.tooMany(w, "wc_phone:"+phone, 4, 5*time.Minute) { + fail(w, http.StatusTooManyRequests, "too_many_requests", "Слишком много попыток для этого номера. Подождите пару минут.") + return + } + + endpoint := fmt.Sprintf( + "https://smsc.ru/sys/wait_call.php?login=%s&psw=%s&phone=%s&fmt=3", + url.QueryEscape(a.smscLogin), + url.QueryEscape(a.smscPassword), + url.QueryEscape(phone), + ) + + ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + if err != nil { + handleErr(w, err) + return + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + slog.Error("smsc wait_call request failed", "err", err) + fail(w, http.StatusBadGateway, "smsc_error", "Сервис проверки номеров временно недоступен") + return + } + defer resp.Body.Close() + + var smscResp struct { + Phone string `json:"phone"` + AllPhones []string `json:"all_phones"` + Error string `json:"error"` + ErrorCode int `json:"error_code"` + } + if err := json.NewDecoder(resp.Body).Decode(&smscResp); err != nil { + slog.Error("smsc wait_call decode failed", "err", err) + fail(w, http.StatusBadGateway, "smsc_error", "Ошибка ответа сервиса проверки") + return + } + if smscResp.ErrorCode != 0 { + slog.Warn("smsc wait_call error", "code", smscResp.ErrorCode, "err", smscResp.Error) + msg := "Не удалось получить номер для проверки" + if smscResp.ErrorCode == 9 { + msg = "Слишком много запросов. Пожалуйста, подождите 1 минуту." + } + fail(w, http.StatusBadRequest, "smsc_error", msg) + return + } + + targetPhone := smscResp.Phone + if targetPhone == "" && len(smscResp.AllPhones) > 0 { + targetPhone = smscResp.AllPhones[0] + } + if targetPhone == "" { + fail(w, http.StatusBadGateway, "smsc_error", "Не получен номер для звонка") + return + } + + b := make([]byte, 16) + _, _ = rand.Read(b) + sessionID := hex.EncodeToString(b) + + sess := &WaitCallSession{ + ID: sessionID, + Phone: phone, + TargetPhone: targetPhone, + DisplayPhone: formatRuPhone(targetPhone), + Verified: false, + CreatedAt: time.Now(), + ExpiresAt: time.Now().Add(10 * time.Minute), + } + wcm.set(sess) + + ok(w, map[string]any{ + "session_id": sess.ID, + "target_phone": sess.TargetPhone, + "display_phone": sess.DisplayPhone, + "expires_in": 600, + }, nil) +} + +func (a *API) waitCallStatus(w http.ResponseWriter, r *http.Request) { + sessionID := r.URL.Query().Get("session_id") + if sessionID == "" { + fail(w, http.StatusBadRequest, "missing_session", "Укажите session_id") + return + } + sess := wcm.get(sessionID) + if sess == nil { + fail(w, http.StatusNotFound, "not_found", "Сессия не найдена или истекла") + return + } + + if !sess.Verified { + wcm.checkSMSCFallback(a.smscLogin, a.smscPassword, sess.Phone) + } + + ok(w, map[string]any{ + "session_id": sess.ID, + "phone": sess.Phone, + "target_phone": sess.TargetPhone, + "display_phone": sess.DisplayPhone, + "verified": sess.Verified, + }, nil) +} + +func (a *API) smscCallback(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + phone := r.FormValue("phone") + if phone == "" { + phone = r.URL.Query().Get("phone") + } + waitcall := r.FormValue("waitcall") + if waitcall == "" { + waitcall = r.URL.Query().Get("waitcall") + } + mes := r.FormValue("mes") + + if phone == "" && r.Body != nil { + var j struct { + Phone string `json:"phone"` + Waitcall any `json:"waitcall"` + Mes string `json:"mes"` + } + _ = json.NewDecoder(r.Body).Decode(&j) + if j.Phone != "" { + phone = j.Phone + } + if j.Waitcall != nil { + waitcall = fmt.Sprint(j.Waitcall) + } + if j.Mes != "" { + mes = j.Mes + } + } + + slog.Info("smsc: callback received", "phone", phone, "waitcall", waitcall, "mes", mes) + + norm := normalizeRuPhone(phone) + if norm != "" { + verifiedCount := wcm.markVerified(norm) + slog.Info("smsc: sessions marked verified", "phone", norm, "count", verifiedCount) + } + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) +} diff --git a/internal/api/waitcall_test.go b/internal/api/waitcall_test.go new file mode 100644 index 0000000..9e7b480 --- /dev/null +++ b/internal/api/waitcall_test.go @@ -0,0 +1,87 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func newWaitSession(phone string) *WaitCallSession { + s := &WaitCallSession{ID: "sess-" + phone, Phone: phone, ExpiresAt: time.Now().Add(time.Minute)} + wcm.set(s) + return s +} + +// Обратный вызов SMSC — публичная точка: без правильного секрета номер подтверждаться не должен. +func TestSMSCCallbackNeedsToken(t *testing.T) { + a := &API{smscCallbackToken: "s3cret-token"} + s := newWaitSession("79280000001") + + for name, query := range map[string]string{ + "без токена": "phone=79280000001", + "чужой токен": "phone=79280000001&token=wrong", + "пустой токен": "phone=79280000001&token=", + "токен не в query": "phone=79280000001&TOKEN=s3cret-token", + } { + rec := httptest.NewRecorder() + a.smscCallback(rec, httptest.NewRequest(http.MethodGet, "/api/v1/auth/smsc-callback?"+query, nil)) + if rec.Code != http.StatusForbidden { + t.Errorf("%s: код %d, ожидали 403", name, rec.Code) + } + if s.Verified { + t.Fatalf("%s: номер подтвердился без секрета", name) + } + } + + rec := httptest.NewRecorder() + a.smscCallback(rec, httptest.NewRequest(http.MethodGet, "/api/v1/auth/smsc-callback?phone=79280000001&token=s3cret-token", nil)) + if rec.Code != http.StatusOK || !s.Verified { + t.Errorf("с верным токеном: код %d, подтверждён=%v", rec.Code, s.Verified) + } +} + +// Если токен на сервере не задан, обратный вызов выключен полностью (в том числе с пустым token=). +func TestSMSCCallbackDisabledWithoutServerToken(t *testing.T) { + a := &API{} + s := newWaitSession("79280000002") + rec := httptest.NewRecorder() + a.smscCallback(rec, httptest.NewRequest(http.MethodGet, "/api/v1/auth/smsc-callback?phone=79280000002&token=", nil)) + if rec.Code != http.StatusForbidden || s.Verified { + t.Errorf("код %d, подтверждён=%v: вызов без настроенного токена должен отклоняться", rec.Code, s.Verified) + } +} + +// Одно подтверждение — одна запись: после consume повторное использование не проходит. +func TestVerificationIsOneTime(t *testing.T) { + s := newWaitSession("79280000003") + wcm.markVerified("79280000003") + if !wcm.isVerified(s.ID, "79280000003") { + t.Fatal("подтверждение не сработало") + } + wcm.consume(s.ID) + if wcm.isVerified(s.ID, "79280000003") { + t.Error("подтверждение осталось действительным после записи") + } +} + +func TestLastDigits(t *testing.T) { + for in, want := range map[string]string{ + "8 (928) 047-00-55": "9280470055", + "+7 928 047 00 55": "9280470055", + "89280470055": "9280470055", + "123": "123", + } { + if got := lastDigits(in, 10); got != want { + t.Errorf("lastDigits(%q)=%q, ожидали %q", in, got, want) + } + } +} + +func TestMaskPhone(t *testing.T) { + for in, want := range map[string]string{"79280470055": "***0055", "+7 (928) 047-00-55": "***0055", "12": "***", "": "***"} { + if got := maskPhone(in); got != want { + t.Errorf("maskPhone(%q)=%q, ожидали %q", in, got, want) + } + } +} diff --git a/internal/api/website.go b/internal/api/website.go new file mode 100644 index 0000000..93ae075 --- /dev/null +++ b/internal/api/website.go @@ -0,0 +1,435 @@ +package api + +import ( + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "log/slog" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + + "dana/internal/domain" + "dana/internal/storage" +) + +type website struct { + dir string + siteURL string + services map[string]bool + local bool + renderer *pageRenderer +} + +// WithWebsite serves build-time rendered React HTML through Go. API-only startup +// is allowed before the first web build, so the generator can read the API. +func (a *API) WithWebsite(dir, siteURL string) error { + if dir == "" { + return nil + } + abs, err := filepath.Abs(dir) + if err != nil { + return err + } + if _, err := os.Stat(filepath.Join(abs, "index.html")); errors.Is(err, os.ErrNotExist) { + slog.Warn("web build not found; API-only startup", "dir", abs) + return nil + } else if err != nil { + return err + } + w := &website{dir: abs, services: map[string]bool{}} + var manifest struct { + SiteURL string `json:"siteURL"` + Services []string `json:"services"` + } + data, err := os.ReadFile(filepath.Join(abs, "seo-manifest.json")) + if err == nil { + if err := json.Unmarshal(data, &manifest); err != nil { + return fmt.Errorf("SEO manifest: %w", err) + } + for _, service := range manifest.Services { + w.services[service] = true + } + } else if !errors.Is(err, os.ErrNotExist) { + return err + } + siteURL = strings.TrimRight(siteURL, "/") + if siteURL == "" { + siteURL = manifest.SiteURL + } + if siteURL == "" { + siteURL = "http://127.0.0.1:8080" + } + if manifest.SiteURL != "" && manifest.SiteURL != siteURL { + return fmt.Errorf("SITE_URL differs from the rendered canonical origin; rebuild web with SITE_URL=%s", siteURL) + } + u, err := url.Parse(siteURL) + if err != nil || u.Host == "" || (u.Scheme != "https" && u.Scheme != "http") || u.Path != "" || u.RawQuery != "" || u.Fragment != "" || u.User != nil { + return fmt.Errorf("SITE_URL must be an HTTP(S) origin") + } + w.siteURL = siteURL + w.local = u.Hostname() == "localhost" || u.Hostname() == "127.0.0.1" || u.Hostname() == "::1" + a.website = w + return nil +} + +var rootFiles = map[string]bool{"/favicon.ico": true, "/apple-touch-icon.png": true} + +func canonicalPagePath(path string) string { + if path == "/" { + return path + } + return strings.TrimRight(path, "/") + "/" +} + +func (a *API) websitePage(w http.ResponseWriter, r *http.Request) { + site := a.website + if site.local { + w.Header().Set("X-Robots-Tag", "noindex, nofollow") + } + // Иконки сайта лежат в корне сборки (web/public): браузеры просят их без ссылок из HTML. + if rootFiles[r.URL.Path] { + file := filepath.Join(site.dir, strings.TrimPrefix(r.URL.Path, "/")) + if stat, err := os.Stat(file); err == nil && stat.Mode().IsRegular() { + w.Header().Set("Cache-Control", "public, max-age=604800") + http.ServeFile(w, r, file) + return + } + } + if strings.HasPrefix(r.URL.Path, "/assets/") { + file := filepath.Join(site.dir, filepath.FromSlash(strings.TrimPrefix(r.URL.Path, "/"))) + rel, err := filepath.Rel(site.dir, file) + if err != nil || strings.HasPrefix(rel, "..") { + http.NotFound(w, r) + return + } + if stat, err := os.Stat(file); err != nil || !stat.Mode().IsRegular() { + http.NotFound(w, r) + return + } + w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") + http.ServeFile(w, r, file) + return + } + switch strings.TrimRight(r.URL.Path, "/") { + case "/company/docs", "/company/requisites": + http.Redirect(w, r, "/legaldocs/", http.StatusMovedPermanently) + return + case "/company/history": + http.Redirect(w, r, "/company/", http.StatusMovedPermanently) + return + } + if r.URL.Path == "/privacy" || r.URL.Path == "/privacy/" || r.URL.Path == "/include/licenses_detail.php" { + http.Redirect(w, r, "/legaldocs/privacy/", http.StatusMovedPermanently) + return + } + pagePath := canonicalPagePath(r.URL.Path) + if strings.HasPrefix(pagePath, "/services/") && pagePath != "/services/" { + servicePath := strings.TrimSuffix(strings.TrimPrefix(pagePath, "/services/"), "/") + svc, err := a.store.ServiceByPath(r.Context(), servicePath) + if errors.Is(err, storage.ErrNotFound) { + a.notFound(w, r, pagePath, strings.Split(strings.Trim(pagePath, "/"), "/")) + return + } + if err != nil { + handleErr(w, err) + return + } + if !site.services[servicePath] && site.renderer == nil { + w.Header().Set("X-Robots-Tag", "noindex") + http.Error(w, "Страница ожидает сборки HTML. Выполните npm run build в web.", http.StatusServiceUnavailable) + return + } + if redirectCanonical(w, r, pagePath) { + return + } + if site.renderer != nil { + a.serveSSR(w, r, pagePath, svc) + return + } + file := filepath.Join(site.dir, "services", filepath.FromSlash(servicePath), "index.html") + if _, err := os.Stat(file); err != nil { + http.Error(w, "HTML страницы недоступен", http.StatusServiceUnavailable) + return + } + w.Header().Set("Cache-Control", "no-cache") + http.ServeFile(w, r, file) + return + } + // Only implemented routes get the SPA shell. Unknown routes and unfinished + // legacy sections return a real 404, even though React renders the design. + known := map[string]bool{"/": true, "/services/": true, "/company/staff/": true, "/price/": true, + "/checkup/": true, "/sales/": true, "/contacts/": true, "/legaldocs/": true, + "/legaldocs/privacy/": true, "/legaldocs/consent/": true, "/legaldocs/cookie/": true} + for _, p := range companyPaths { + known[p] = true + } + parts := strings.Split(strings.Trim(pagePath, "/"), "/") + if !known[pagePath] { + valid, err := a.knownContentRoute(r, parts) + if err != nil && !errors.Is(err, storage.ErrNotFound) { + handleErr(w, err) + return + } + if !valid { + a.notFound(w, r, pagePath, parts) + return + } + } + if len(parts) == 4 && parts[0] == "company" && parts[1] == "staff" { + doctor, err := a.store.DoctorBySlug(r.Context(), parts[3]) + if err != nil { + handleErr(w, err) + return + } + canonical := doctorWebPath(*doctor) + if redirectCanonical(w, r, canonical) { + return + } + } + // У вакансии нет своей страницы: старый адрес открывает её в общем списке. + if len(parts) == 3 && parts[0] == "company" && parts[1] == "vacancy" { + http.Redirect(w, r, "/company/vacancy/#"+url.PathEscape(parts[2]), http.StatusMovedPermanently) + return + } + if len(parts) == 2 && parts[0] == "sales" { + http.Redirect(w, r, "/sales/", http.StatusMovedPermanently) + return + } + if redirectCanonical(w, r, pagePath) { + return + } + if site.renderer != nil { + a.serveSSR(w, r, pagePath, nil) + return + } + w.Header().Set("Cache-Control", "no-cache") + http.ServeFile(w, r, filepath.Join(site.dir, "index.html")) +} + +func redirectCanonical(w http.ResponseWriter, r *http.Request, path string) bool { + if r.URL.Path == path { + return false + } + if r.URL.RawQuery != "" { + path += "?" + r.URL.RawQuery + } + http.Redirect(w, r, path, http.StatusMovedPermanently) + return true +} + +// Разделы «Компании» с постоянными адресами старого сайта. +var companyPaths = []string{"/company/", "/news/", "/articles/", "/reviews/", "/company/partners/", + "/company/nadzor/", "/company/vacancy/", "/info/faq/", "/company/licenses/"} + +// postKinds: раздел сайта → вид публикации в базе. +var postKinds = map[string]string{"news": "news", "articles": "article"} + +func (a *API) knownContentRoute(r *http.Request, p []string) (bool, error) { + // «Пациенту» открывается, только когда клиника включила страницу в админке + if len(p) == 1 && p[0] == "patients" { + _, err := a.store.PageBySlug(r.Context(), "patients") + return err == nil, err + } + if len(p) == 2 && postKinds[p[0]] != "" { + _, err := a.store.PostBySlug(r.Context(), postKinds[p[0]], p[1]) + return err == nil, err + } + if len(p) == 3 && p[0] == "company" && p[1] == "vacancy" { + list, err := a.store.Vacancies(r.Context()) + for _, v := range list { + if v.Slug == p[2] { + return true, err + } + } + return false, err + } + if len(p) == 2 && p[0] == "checkup" { + _, err := a.store.CheckupBySlug(r.Context(), p[1]) + return err == nil, err + } + if len(p) == 2 && p[0] == "sales" { + _, err := a.store.PostBySlug(r.Context(), "promo", p[1]) + return err == nil, err + } + if len(p) == 2 && p[0] == "price" { + tree, err := a.store.PriceList(r.Context(), "") + if err != nil { + return false, err + } + var contains func([]*domain.PriceSection) bool + contains = func(list []*domain.PriceSection) bool { + for _, s := range list { + if (s.Slug == p[1] && len(s.Items) > 0) || contains(s.Children) { + return true + } + } + return false + } + return contains(tree), nil + } + if len(p) >= 3 && p[0] == "company" && p[1] == "staff" { + if len(p) == 3 { + list, err := a.store.Specialties(r.Context()) + for _, s := range list { + if s.Slug == p[2] { + return true, err + } + } + return false, err + } + if len(p) == 4 { + doctor, err := a.store.DoctorBySlug(r.Context(), p[3]) + if err != nil { + return false, err + } + if len(doctor.Specialties) == 0 && p[2] == "vrachi" { + return true, nil + } + for _, s := range doctor.Specialties { + if s.Slug == p[2] { + return true, nil + } + } + return false, nil + } + } + return false, nil +} + +func (a *API) website404(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Robots-Tag", "noindex") + w.Header().Set("Content-Type", "text/html; charset=utf-8") + data, err := os.ReadFile(filepath.Join(a.website.dir, "index.html")) + if err != nil { + http.NotFound(w, r) + return + } + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write(data) +} + +func (a *API) robots(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + if a.website.local { + _, _ = fmt.Fprint(w, "User-agent: *\nDisallow: /\n") + return + } + // Query variants can be crawled to see their noindex response and canonical. + _, _ = fmt.Fprintf(w, "User-agent: *\nDisallow: /admin\nDisallow: /api/\nSitemap: %s/sitemap.xml\n", a.website.siteURL) +} + +func (a *API) sitemap(w http.ResponseWriter, r *http.Request) { + tree, err := a.store.ServiceTree(r.Context()) + if err != nil { + handleErr(w, err) + return + } + type location struct { + URL string `xml:"loc"` + } + set := struct { + XMLName xml.Name `xml:"urlset"` + Namespace string `xml:"xmlns,attr"` + URLs []location `xml:"url"` + }{Namespace: "http://www.sitemaps.org/schemas/sitemap/0.9"} + seen := map[string]bool{} + addPath := func(path string) { + if !seen[path] { + seen[path] = true + set.URLs = append(set.URLs, location{a.website.siteURL + path}) + } + } + for _, p := range []string{"/", "/services/", "/company/staff/", "/price/", "/checkup/", "/sales/", "/contacts/"} { + addPath(p) + } + for _, p := range companyPaths { + addPath(p) + } + if _, err := a.store.PageBySlug(r.Context(), "patients"); err == nil { + addPath("/patients/") + } + for _, section := range []string{"news", "articles"} { + posts, err := a.store.Posts(r.Context(), postKinds[section], 100) + if err != nil { + handleErr(w, err) + return + } + for _, p := range posts { + addPath("/" + section + "/" + p.Slug + "/") + } + } + var add func([]*domain.Service) + add = func(list []*domain.Service) { + for _, s := range list { + if a.website.renderer != nil { + addPath("/services/" + s.Path + "/") + } else if a.website.services[s.Path] { + if _, err := os.Stat(filepath.Join(a.website.dir, "services", filepath.FromSlash(s.Path), "index.html")); err == nil { + addPath("/services/" + s.Path + "/") + } + } + add(s.Children) + } + } + add(tree) + if a.website.renderer != nil { + doctors, err := a.store.Doctors(r.Context(), storage.DoctorFilter{}) + if err != nil { + handleErr(w, err) + return + } + for _, d := range doctors { + addPath(doctorWebPath(d)) + for _, s := range d.Specialties { + addPath("/company/staff/" + s.Slug + "/") + } + } + checkups, err := a.store.Checkups(r.Context(), "") + if err != nil { + handleErr(w, err) + return + } + for _, c := range checkups { + addPath("/checkup/" + c.Slug + "/") + } + prices, err := a.store.PriceList(r.Context(), "") + if err != nil { + handleErr(w, err) + return + } + var addPrices func([]*domain.PriceSection) + addPrices = func(list []*domain.PriceSection) { + for _, s := range list { + if len(s.Items) > 0 { + addPath("/price/" + s.Slug + "/") + } + addPrices(s.Children) + } + } + addPrices(prices) + for _, slug := range []string{"legaldocs", "privacy", "consent", "cookie"} { + _, err := a.store.PageBySlug(r.Context(), slug) + if errors.Is(err, storage.ErrNotFound) { + continue + } + if err != nil { + handleErr(w, err) + return + } + path := "/legaldocs/" + if slug != "legaldocs" { + path += slug + "/" + } + addPath(path) + } + } else { + addPath("/legaldocs/") + } + w.Header().Set("Content-Type", "application/xml; charset=utf-8") + _, _ = fmt.Fprint(w, xml.Header) + _ = xml.NewEncoder(w).Encode(set) +} diff --git a/internal/api/website_test.go b/internal/api/website_test.go new file mode 100644 index 0000000..4db87bd --- /dev/null +++ b/internal/api/website_test.go @@ -0,0 +1,101 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" +) + +func testWebsite(t *testing.T, origin string) *API { + t.Helper() + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, "index.html"), []byte("
"), 0600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "favicon.ico"), []byte{0, 0, 1, 0}, 0600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "seo-manifest.json"), []byte(`{"siteURL":"`+origin+`","services":[]}`), 0600); err != nil { + t.Fatal(err) + } + a := New(nil, "") + if err := a.WithWebsite(dir, origin); err != nil { + t.Fatal(err) + } + return a +} + +func TestCanonicalPagePath(t *testing.T) { + for input, expected := range map[string]string{"/": "/", "/contacts": "/contacts/", "/contacts/": "/contacts/", "/services/estetic/rinoplastika": "/services/estetic/rinoplastika/"} { + if got := canonicalPagePath(input); got != expected { + t.Errorf("%s: %s != %s", input, got, expected) + } + } +} + +func TestWebsiteStatusCodes(t *testing.T) { + a := testWebsite(t, "https://example.org") + for _, tc := range []struct { + path string + status int + redirect string + }{ + {"/", 200, ""}, {"/contacts/", 200, ""}, {"/contacts?utm_source=test", 301, "/contacts/?utm_source=test"}, + {"/no-such-page/", 404, ""}, {"/news/", 200, ""}, {"/gallery/", 404, ""}, + {"/company/history/", 301, "/company/"}, {"/company/docs/", 301, "/legaldocs/"}, {"/assets/no-such-file.js", 404, ""}, + {"/seo-manifest.json", 404, ""}, {"/favicon.ico", 200, ""}, {"/privacy", 301, "/legaldocs/privacy/"}, + } { + r := httptest.NewRequest(http.MethodGet, tc.path, nil) + w := httptest.NewRecorder() + a.Routes().ServeHTTP(w, r) + if w.Code != tc.status { + t.Errorf("%s: status %d, want %d", tc.path, w.Code, tc.status) + } + if tc.redirect != "" && w.Header().Get("Location") != tc.redirect { + t.Errorf("redirect %s", w.Header().Get("Location")) + } + if w.Code == 404 && tc.path == "/no-such-page/" && w.Header().Get("X-Robots-Tag") != "noindex" { + t.Error("404 should be noindex") + } + } +} + +func TestRobots(t *testing.T) { + for _, origin := range []string{"https://example.org", "http://127.0.0.1:8080"} { + a := testWebsite(t, origin) + w := httptest.NewRecorder() + a.Routes().ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/robots.txt", nil)) + body := w.Body.String() + if a.website.local { + if body != "User-agent: *\nDisallow: /\n" { + t.Fatal(body) + } + } else { + for _, expected := range []string{"Disallow: /admin", "Disallow: /api/", "Sitemap: https://example.org/sitemap.xml"} { + if !strings.Contains(body, expected) { + t.Errorf("missing %s", expected) + } + } + } + } +} + +func TestWebsiteCanonicalMismatch(t *testing.T) { + a := testWebsite(t, "https://example.org") + if err := a.WithWebsite(a.website.dir, "https://other.example.org"); err == nil { + t.Error("canonical mismatch must fail startup") + } +} + +func TestWebsiteInvalidOrigin(t *testing.T) { + dir := t.TempDir() + _ = os.WriteFile(filepath.Join(dir, "index.html"), []byte("html"), 0600) + for _, origin := range []string{"javascript:alert(1)", "https://example.org/path", "https://user:secret@example.org", "https://example.org?q=test"} { + if err := New(nil, "").WithWebsite(dir, origin); err == nil { + t.Errorf("accepted %s", origin) + } + } +} diff --git a/internal/bitrix/client.go b/internal/bitrix/client.go new file mode 100644 index 0000000..c8037a2 --- /dev/null +++ b/internal/bitrix/client.go @@ -0,0 +1,134 @@ +// Package bitrix — минимальный клиент REST API Битрикс24 через входящий вебхук. +// Используется для выгрузки лидов и контактов в «Отчёт по каналам» и для лидов онлайн-записи, +// которые подтверждает оператор (см. leads.go). +package bitrix + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" +) + +type Client struct { + WebhookURL string + HTTP *http.Client +} + +func New(webhookURL string) *Client { + return &Client{ + WebhookURL: strings.TrimRight(strings.TrimSpace(webhookURL), "/"), + HTTP: &http.Client{Timeout: 30 * time.Second}, + } +} + +func (c *Client) Configured() bool { return c != nil && c.WebhookURL != "" } + +type phoneField struct { + Value string `json:"VALUE"` +} + +type crmItem struct { + ID string `json:"ID"` + SourceID string `json:"SOURCE_ID"` + Phone []phoneField `json:"PHONE"` +} + +type listResponse struct { + Result []crmItem `json:"result"` + Total int `json:"total"` + Error string `json:"error"` + Descr string `json:"error_description"` +} + +// Record — лид или контакт CRM: нужные нам поля, без лишнего. +type Record struct { + ID string + SourceID string // у контактов всегда пусто — поле есть только у лидов + Phones []string +} + +const pageSize = 50 + +// Progress — колбэк хода постраничной выгрузки: сколько записей забрано из скольких всего (по ответу API). +type Progress func(done, total int) + +// list — одна выборка (лиды или контакты) целиком, со всеми страницами. +func (c *Client) list(ctx context.Context, method string, selectFields []string, filter map[string]any, onPage Progress) ([]Record, error) { + var out []Record + start := 0 + for { + body := map[string]any{ + "select": selectFields, + "order": map[string]string{"ID": "ASC"}, + "start": start, + } + if len(filter) > 0 { + body["filter"] = filter + } + b, err := json.Marshal(body) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, + c.WebhookURL+"/"+method+".json", strings.NewReader(string(b))) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := c.HTTP.Do(req) + if err != nil { + return nil, fmt.Errorf("битрикс %s: %w", method, redactURL(err)) + } + var lr listResponse + decErr := json.NewDecoder(resp.Body).Decode(&lr) + resp.Body.Close() + if decErr != nil { + return nil, fmt.Errorf("битрикс %s: не разобрать ответ: %w", method, decErr) + } + if lr.Error != "" { + return nil, fmt.Errorf("битрикс %s: %s (%s)", method, lr.Error, lr.Descr) + } + for _, item := range lr.Result { + r := Record{ID: item.ID, SourceID: item.SourceID} + for _, p := range item.Phone { + if v := strings.TrimSpace(p.Value); v != "" { + r.Phones = append(r.Phones, v) + } + } + out = append(out, r) + } + if onPage != nil { + onPage(len(out), lr.Total) + } + start += pageSize + if start >= lr.Total || len(lr.Result) == 0 { + break + } + } + return out, nil +} + +// dateFilter — ограничение по дате создания записи (DATE_CREATE), чтобы не тянуть всю историю CRM +// за годы. from/to нулевые — фильтр не применяется (выгружается всё, для старых мест вызова). +func dateFilter(from, to time.Time) map[string]any { + if from.IsZero() || to.IsZero() { + return nil + } + return map[string]any{ + ">=DATE_CREATE": from.Format(time.RFC3339), + "= 300 { + return fmt.Errorf("битрикс %s: HTTP %d", method, resp.StatusCode) + } + if out != nil { + if err := json.Unmarshal(r.Result, out); err != nil { + return fmt.Errorf("битрикс %s: неожиданный ответ: %w", method, err) + } + } + return nil +} + +// AddLead заводит лид в стадии «Не обработан» и возвращает его ID. +func (c *Client) AddLead(ctx context.Context, d LeadDraft) (string, error) { + fields := map[string]any{ + "TITLE": d.Title, + "NAME": d.Name, + "STATUS_ID": "NEW", + "OPENED": "Y", + "COMMENTS": d.Comments, + } + if d.Phone != "" { + fields["PHONE"] = []map[string]string{{"VALUE": d.Phone, "VALUE_TYPE": "MOBILE"}} + } + if d.SourceID != "" { + fields["SOURCE_ID"] = d.SourceID + } + if d.SourceDescr != "" { + fields["SOURCE_DESCRIPTION"] = d.SourceDescr + } + // result — число, но на всякий случай принимаем и строку + var id json.Number + if err := c.call(ctx, "crm.lead.add", map[string]any{ + "fields": fields, + "params": map[string]string{"REGISTER_SONET_EVENT": "Y"}, + }, &id); err != nil { + return "", err + } + s := strings.Trim(id.String(), `"`) + if s == "" || s == "0" { + return "", fmt.Errorf("битрикс crm.lead.add: пустой ID лида") + } + return s, nil +} + +// LeadSemantics — семантика стадии (P/S/F) для каждого найденного лида. Удалённых лидов в ответе нет. +func (c *Client) LeadSemantics(ctx context.Context, ids []string) (map[string]string, error) { + out := map[string]string{} + for len(ids) > 0 { + n := min(len(ids), pageSize) + chunk := ids[:n] + ids = ids[n:] + var items []struct { + ID string `json:"ID"` + Semantic string `json:"STATUS_SEMANTIC_ID"` + } + if err := c.call(ctx, "crm.lead.list", map[string]any{ + "select": []string{"ID", "STATUS_SEMANTIC_ID"}, + "filter": map[string]any{"@ID": chunk}, + }, &items); err != nil { + return nil, err + } + for _, it := range items { + out[it.ID] = it.Semantic + } + } + return out, nil +} + +// AddLeadComment оставляет комментарий в ленте лида — оператор видит, что сделал сайт. +func (c *Client) AddLeadComment(ctx context.Context, leadID, text string) error { + return c.call(ctx, "crm.timeline.comment.add", map[string]any{ + "fields": map[string]any{"ENTITY_ID": leadID, "ENTITY_TYPE": "lead", "COMMENT": text}, + }, nil) +} + +// FailLead переводит лид в «Клиент не записался» — после автоматической отмены записи. +func (c *Client) FailLead(ctx context.Context, leadID string) error { + return c.call(ctx, "crm.lead.update", map[string]any{ + "id": leadID, + "fields": map[string]any{"STATUS_ID": FailedStatusID}, + }, nil) +} + +// redactURL убирает адрес запроса из сетевой ошибки: в адресе вебхука секретный ключ, +// а текст ошибки попадает в лог, в базу и в админку. +func redactURL(err error) error { + var ue *url.Error + if errors.As(err, &ue) { + return fmt.Errorf("%s: %w", ue.Op, ue.Err) + } + return err +} diff --git a/internal/bitrix/leads_test.go b/internal/bitrix/leads_test.go new file mode 100644 index 0000000..ca4404b --- /dev/null +++ b/internal/bitrix/leads_test.go @@ -0,0 +1,96 @@ +package bitrix + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +// fakeBitrix отвечает на методы REST так, как отвечает Битрикс24, и запоминает тела запросов. +func fakeBitrix(t *testing.T, answers map[string]string) (*Client, map[string]map[string]any) { + t.Helper() + got := map[string]map[string]any{} + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + method := strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, "/rest/1/key/"), ".json") + var body map[string]any + _ = json.NewDecoder(r.Body).Decode(&body) + got[method] = body + ans, ok := answers[method] + if !ok { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"error":"ERROR_METHOD_NOT_FOUND","error_description":"нет такого метода"}`)) + return + } + _, _ = w.Write([]byte(ans)) + })) + t.Cleanup(srv.Close) + return New(srv.URL + "/rest/1/key/"), got +} + +func TestAddLeadSendsFieldsAndReturnsID(t *testing.T) { + c, got := fakeBitrix(t, map[string]string{"crm.lead.add": `{"result":683100,"time":{}}`}) + id, err := c.AddLead(context.Background(), LeadDraft{ + Title: "Онлайн-запись: Иванов — 10.10 15:00", Name: "Анна", Phone: "+79280000000", + SourceID: "112", SourceDescr: "Онлайн-запись на сайте", Comments: "строка
строка", + }) + if err != nil || id != "683100" { + t.Fatalf("id=%q err=%v", id, err) + } + f, _ := got["crm.lead.add"]["fields"].(map[string]any) + if f["STATUS_ID"] != "NEW" || f["SOURCE_ID"] != "112" || f["NAME"] != "Анна" || f["TITLE"] == nil { + t.Fatalf("поля лида: %v", f) + } + phones, _ := f["PHONE"].([]any) + if len(phones) != 1 || phones[0].(map[string]any)["VALUE"] != "+79280000000" { + t.Fatalf("телефон: %v", f["PHONE"]) + } +} + +func TestAddLeadReportsBitrixError(t *testing.T) { + c, _ := fakeBitrix(t, map[string]string{"crm.lead.add": `{"error":"ACCESS_DENIED","error_description":"нет прав"}`}) + if _, err := c.AddLead(context.Background(), LeadDraft{Title: "x"}); err == nil || !strings.Contains(err.Error(), "ACCESS_DENIED") { + t.Fatalf("ожидали ошибку прав, получили %v", err) + } +} + +func TestLeadSemanticsFiltersByIDs(t *testing.T) { + c, got := fakeBitrix(t, map[string]string{ + "crm.lead.list": `{"result":[{"ID":"1","STATUS_SEMANTIC_ID":"S"},{"ID":"2","STATUS_SEMANTIC_ID":"F"}],"total":2}`, + }) + m, err := c.LeadSemantics(context.Background(), []string{"1", "2", "3"}) + if err != nil { + t.Fatal(err) + } + // лида 3 в ответе нет (удалён в CRM) — в карте его тоже нет + if m["1"] != SemanticSuccess || m["2"] != SemanticFailure || len(m) != 2 { + t.Fatalf("семантика: %v", m) + } + filter, _ := got["crm.lead.list"]["filter"].(map[string]any) + if ids, _ := filter["@ID"].([]any); len(ids) != 3 { + t.Fatalf("фильтр по ID: %v", filter) + } +} + +func TestFailLeadAndComment(t *testing.T) { + c, got := fakeBitrix(t, map[string]string{ + "crm.lead.update": `{"result":true}`, + "crm.timeline.comment.add": `{"result":55}`, + }) + if err := c.FailLead(context.Background(), "7"); err != nil { + t.Fatal(err) + } + if err := c.AddLeadComment(context.Background(), "7", "Запись отменена"); err != nil { + t.Fatal(err) + } + upd, _ := got["crm.lead.update"]["fields"].(map[string]any) + if got["crm.lead.update"]["id"] != "7" || upd["STATUS_ID"] != FailedStatusID { + t.Fatalf("обновление лида: %v", got["crm.lead.update"]) + } + cm, _ := got["crm.timeline.comment.add"]["fields"].(map[string]any) + if cm["ENTITY_TYPE"] != "lead" || cm["ENTITY_ID"] != "7" { + t.Fatalf("комментарий: %v", cm) + } +} diff --git a/internal/bitrix/redact_test.go b/internal/bitrix/redact_test.go new file mode 100644 index 0000000..6696cfd --- /dev/null +++ b/internal/bitrix/redact_test.go @@ -0,0 +1,25 @@ +package bitrix + +import ( + "context" + "strings" + "testing" + "time" +) + +// Вебхук недоступен (сеть, таймаут): в тексте ошибки не должно быть адреса с секретным ключом — +// он уходит в лог, в базу и в админку. +func TestNetworkErrorsDoNotLeakWebhookKey(t *testing.T) { + c := New("http://127.0.0.1:1/rest/1/SECRETKEY123/") + c.HTTP.Timeout = 2 * time.Second + _, errAdd := c.AddLead(context.Background(), LeadDraft{Title: "x"}) + _, errList := c.Leads(context.Background(), time.Time{}, time.Time{}, nil) + for name, err := range map[string]error{"crm.lead.add": errAdd, "crm.lead.list": errList} { + if err == nil { + t.Fatalf("%s: ожидали сетевую ошибку", name) + } + if strings.Contains(err.Error(), "SECRETKEY123") { + t.Errorf("%s: ключ вебхука в тексте ошибки: %q", name, err) + } + } +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..d2d70d9 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,187 @@ +package config + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" +) + +type Config struct { + Addr string + DatabaseURL string + MediaDir string + AllowedOrigins string + AdminEmail string + AdminPassword string + ShutdownTimeout time.Duration + Debug bool + SiteURL string + WebDir string + SSRRenderer string + NodeBinary string + + // Уведомления о заявках на почту. Без SMTP_HOST отключены. + SMTPHost string + SMTPPort int + SMTPUser string + SMTPPassword string + NotifyFrom string + NotifyTo []string + + // Виртуальная АТС МегаФон: адрес ВАТС (vats970345.megapbx.ru) и ключ REST API. + // Без ключа загрузка звонков выключена. + MegafonURL string + MegafonKey string + // АТС МегаФон клиники «Очевидно» (vats363024.megapbx.ru) — свои звонки и свой отчёт по каналам. + OchevidnoMegafonURL string + OchevidnoMegafonKey string + + // 1С: расписание врачей и запись. Без адреса и ключа запись через 1С выключена, остаются заявки. + OnecURL string + OnecKey string + OnecAuthHeader string + OnecWebhookKey string + OnecSyncSecs int // период опроса 1С, по умолчанию 300 + OnecLongPoll bool // режим long polling: 1С держит запрос /changes + + // Битрикс24: входящий вебхук для разовой выгрузки номеров лидов/контактов в «Отчёт по каналам». + BitrixWebhookURL string + + // SMSC.RU: подтверждение номера звонком (Wait-Call) + SMSCLogin string + SMSCPassword string + // Секрет в адресе обратного вызова SMSC (?token=…). Без него вызов не принимается. + SMSCCallbackToken string + // TrustedProxies — адреса/подсети прокси, чьим X-Real-IP верим (пусто — только сам сервер). + TrustedProxies string + + // Кабинеты ПроДокторов и СберЗдоровья (скачивание с сервера; пусто = выключено). + ProdoctorovCookie string + ProdoctorovLPU string + SberHealthCookie string + SberHealthClinic string +} + +func Load() (Config, error) { + loadDotEnv(".env") + c := Config{ + Addr: env("APP_ADDR", ":8080"), + DatabaseURL: os.Getenv("DATABASE_URL"), + MediaDir: env("MEDIA_DIR", "./media"), + AllowedOrigins: env("ALLOWED_ORIGINS", "*"), + AdminEmail: env("ADMIN_EMAIL", ""), + AdminPassword: env("ADMIN_PASSWORD", ""), + ShutdownTimeout: 15 * time.Second, + Debug: envBool("DEBUG", false), + SiteURL: env("SITE_URL", ""), + WebDir: env("WEB_DIR", "./web/dist"), + SSRRenderer: env("SSR_RENDERER", "./web/scripts/render-page.mjs"), + NodeBinary: env("NODE_BINARY", "node"), + + SMTPHost: env("SMTP_HOST", ""), + SMTPPort: envInt("SMTP_PORT", 465), + SMTPUser: env("SMTP_USER", ""), + SMTPPassword: os.Getenv("SMTP_PASSWORD"), + NotifyFrom: env("NOTIFY_FROM", os.Getenv("SMTP_USER")), + NotifyTo: envList("NOTIFY_TO"), + + MegafonURL: env("MEGAFON_API_URL", ""), + MegafonKey: os.Getenv("MEGAFON_API_KEY"), + + OchevidnoMegafonURL: env("OCHEVIDNO_MEGAFON_API_URL", ""), + OchevidnoMegafonKey: os.Getenv("OCHEVIDNO_MEGAFON_API_KEY"), + + OnecURL: env("ONEC_API_URL", ""), + OnecKey: os.Getenv("ONEC_API_KEY"), + OnecAuthHeader: env("ONEC_AUTH_HEADER", "X-Api-Key"), + OnecWebhookKey: os.Getenv("ONEC_WEBHOOK_KEY"), + OnecSyncSecs: envInt("ONEC_SYNC_SECONDS", 300), + OnecLongPoll: os.Getenv("ONEC_LONGPOLL") == "1", + + BitrixWebhookURL: os.Getenv("BITRIX_WEBHOOK_URL"), + SMSCLogin: os.Getenv("SMSC_LOGIN"), + SMSCPassword: os.Getenv("SMSC_PASSWORD"), + SMSCCallbackToken: os.Getenv("SMSC_CALLBACK_TOKEN"), + TrustedProxies: os.Getenv("TRUSTED_PROXIES"), + ProdoctorovCookie: os.Getenv("PRODOCTOROV_COOKIE"), + ProdoctorovLPU: os.Getenv("PRODOCTOROV_LPU_ID"), + SberHealthCookie: os.Getenv("SBERHEALTH_COOKIE"), + SberHealthClinic: os.Getenv("SBERHEALTH_CLINIC_ID"), + } + if c.DatabaseURL == "" { + return c, fmt.Errorf("DATABASE_URL is required") + } + if c.SMTPHost != "" && c.NotifyFrom == "" { + return c, fmt.Errorf("NOTIFY_FROM or SMTP_USER is required when SMTP_HOST is set") + } + return c, nil +} + +func envInt(key string, def int) int { + n, err := strconv.Atoi(os.Getenv(key)) + if err != nil { + return def + } + return n +} + +// envList читает список через запятую: «a@x.ru, b@x.ru». +func envList(key string) []string { + var out []string + for _, part := range strings.Split(os.Getenv(key), ",") { + if part = strings.TrimSpace(part); part != "" { + out = append(out, part) + } + } + return out +} + +func env(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +} + +func envBool(key string, def bool) bool { + v := os.Getenv(key) + if v == "" { + return def + } + b, err := strconv.ParseBool(v) + if err != nil { + return def + } + return b +} + +// loadDotEnv читает файл .env рядом с сервером: строки «КЛЮЧ=значение», # — комментарий. +// Переменные, уже заданные в окружении, не перезаписываются. Файл в git не попадает (.gitignore): +// в нём живут секреты — пароль базы, ключ API АТС. +func loadDotEnv(path string) { + data, err := os.ReadFile(path) + if err != nil { + return + } + text := strings.TrimPrefix(string(data), "\ufeff") + for _, line := range strings.Split(text, "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + k, v, ok := strings.Cut(line, "=") + if !ok { + continue + } + k = strings.TrimSpace(strings.TrimPrefix(k, "export ")) + v = strings.TrimSpace(v) + if len(v) >= 2 && ((v[0] == '"' && v[len(v)-1] == '"') || (v[0] == '\'' && v[len(v)-1] == '\'')) { + v = v[1 : len(v)-1] + } + if _, set := os.LookupEnv(k); !set && k != "" { + _ = os.Setenv(k, v) + } + } +} diff --git a/internal/domain/content.go b/internal/domain/content.go new file mode 100644 index 0000000..8be3676 --- /dev/null +++ b/internal/domain/content.go @@ -0,0 +1,157 @@ +package domain + +import ( + "encoding/json" + "time" +) + +// Post — новость, статья или акция. Различаются полем Kind. +type Post struct { + ID int64 `json:"id"` + Kind string `json:"kind"` // news | article | promo + Slug string `json:"slug"` + Title string `json:"title"` + Lead string `json:"lead,omitempty"` + Blocks json.RawMessage `json:"blocks,omitempty"` + Image string `json:"image,omitempty"` + StartsAt *time.Time `json:"starts_at,omitempty"` + EndsAt *time.Time `json:"ends_at,omitempty"` + PublishedAt time.Time `json:"published_at"` + SEO SEO `json:"seo"` + + // Только у акций: размер скидки, условия и чек-ап, на который она действует. + DiscountPercent *int `json:"discount_percent,omitempty"` + Terms string `json:"terms,omitempty"` + CheckupSlug string `json:"checkup_slug,omitempty"` +} + +type Review struct { + ID int64 `json:"id"` + AuthorName string `json:"author_name"` + AuthorPhoto string `json:"author_photo,omitempty"` + Rating *int `json:"rating,omitempty"` + Text string `json:"text"` + VideoURL string `json:"video_url,omitempty"` + DoctorSlug string `json:"doctor_slug,omitempty"` + DoctorName string `json:"doctor_name,omitempty"` + Source string `json:"source"` + Reply string `json:"reply,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +type FAQItem struct { + ID int64 `json:"id"` + Question string `json:"question"` + Answer string `json:"answer"` + ServiceID *int64 `json:"service_id,omitempty"` +} + +type Document struct { + ID int64 `json:"id"` + Kind string `json:"kind"` // license | certificate | supervisor | other + Title string `json:"title"` + Body string `json:"body,omitempty"` + File string `json:"file,omitempty"` + Preview string `json:"preview,omitempty"` + IssuedAt *time.Time `json:"issued_at,omitempty"` +} + +type Partner struct { + ID int64 `json:"id"` + Name string `json:"name"` + URL string `json:"url,omitempty"` + Logo string `json:"logo,omitempty"` +} + +type Vacancy struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Description string `json:"description,omitempty"` + Duties string `json:"duties,omitempty"` + Requirements string `json:"requirements,omitempty"` + Conditions string `json:"conditions,omitempty"` + Salary string `json:"salary,omitempty"` + BranchID *int64 `json:"branch_id,omitempty"` +} + +type Branch struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + Address string `json:"address"` + Phones []string `json:"phones"` + WorkHours []string `json:"work_hours"` + Lat *float64 `json:"lat,omitempty"` + Lon *float64 `json:"lon,omitempty"` +} + +type Checkup struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Audience string `json:"audience"` // adults | kids + Name string `json:"name"` + Lead string `json:"lead,omitempty"` + Blocks json.RawMessage `json:"blocks,omitempty"` + Price *float64 `json:"price,omitempty"` + OldPrice *float64 `json:"old_price,omitempty"` + Image string `json:"image,omitempty"` + Featured bool `json:"featured"` + Items []string `json:"items,omitempty"` + SEO SEO `json:"seo"` +} + +type Page struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Blocks json.RawMessage `json:"blocks,omitempty"` + SEO SEO `json:"seo"` +} + +type GalleryAlbum struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Photos []GalleryPhoto `json:"photos,omitempty"` +} + +type GalleryPhoto struct { + ID int64 `json:"id"` + Path string `json:"path"` + Caption string `json:"caption,omitempty"` +} + +// --- формы и заявки --- + +type Form struct { + ID int64 `json:"id"` + Code string `json:"code"` + Title string `json:"title"` + SuccessText string `json:"success_text,omitempty"` + Fields []FormField `json:"fields"` +} + +type FormField struct { + Code string `json:"code"` + Label string `json:"label"` + Type string `json:"type"` + Required bool `json:"required"` + Options json.RawMessage `json:"options,omitempty"` +} + +// LeadInput — то, что приходит с сайта при отправке формы. +type LeadInput struct { + FormCode string `json:"form"` + Name string `json:"name"` + Phone string `json:"phone"` + Email string `json:"email"` + ServiceID *int64 `json:"service_id"` + DoctorID *int64 `json:"doctor_id"` + BranchID *int64 `json:"branch_id"` + Message string `json:"message"` + Extra map[string]any `json:"extra"` + PageURL string `json:"page_url"` + UTM map[string]string `json:"utm"` + Consent bool `json:"consent"` +} diff --git a/internal/domain/domain.go b/internal/domain/domain.go new file mode 100644 index 0000000..b55aab6 --- /dev/null +++ b/internal/domain/domain.go @@ -0,0 +1,120 @@ +package domain + +import "encoding/json" + +type SEO struct { + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + H1 string `json:"h1,omitempty"` +} + +type Service struct { + ID int64 `json:"id"` + ParentID *int64 `json:"parent_id,omitempty"` + Slug string `json:"slug"` + Path string `json:"path"` + Name string `json:"name"` + ShortName string `json:"short_name,omitempty"` + Lead string `json:"lead,omitempty"` + Icon string `json:"icon,omitempty"` + Image string `json:"image,omitempty"` + Blocks json.RawMessage `json:"blocks,omitempty"` + SEO SEO `json:"seo"` + Children []*Service `json:"children,omitempty"` + + // Заполняется только для карточки услуги. + PriceFrom *float64 `json:"price_from,omitempty"` + Prices []PriceItem `json:"prices,omitempty"` + Doctors []Doctor `json:"doctors,omitempty"` +} + +type Specialty struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + NamePlural string `json:"name_plural"` + Count int `json:"count,omitempty"` +} + +type Doctor struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + LastName string `json:"last_name"` + FirstName string `json:"first_name"` + MiddleName string `json:"middle_name,omitempty"` + FullName string `json:"full_name"` + Position string `json:"position,omitempty"` + Category string `json:"category,omitempty"` + Degree string `json:"degree,omitempty"` + Experience *int `json:"experience_years,omitempty"` + Photo string `json:"photo,omitempty"` + Quote string `json:"quote,omitempty"` + Bio string `json:"bio,omitempty"` + Languages []string `json:"languages,omitempty"` + KidsFrom *int `json:"accepts_kids_from,omitempty"` + Booking Booking `json:"booking"` + Absent bool `json:"absent"` + // Рейтинг может быть задан в админке; иначе считается по опубликованным отзывам. + Rating *float64 `json:"rating,omitempty"` + ReviewCount int `json:"review_count"` + Specialties []Specialty `json:"specialties,omitempty"` + SEO SEO `json:"seo"` + + BioEntries []BioEntry `json:"bio_entries,omitempty"` + Services []DoctorService `json:"services,omitempty"` + Branches []DoctorBranch `json:"branches,omitempty"` +} + +// DoctorBranch — филиал, где принимает врач; адрес показывается в карточке. +type DoctorBranch struct { + Slug string `json:"slug"` + Name string `json:"name"` + Address string `json:"address"` +} + +// DoctorService — услуга, которую ведёт врач; для вкладки «Услуги» в карточке. +type DoctorService struct { + Name string `json:"name"` + Path string `json:"path"` + Lead string `json:"lead,omitempty"` +} + +type Booking struct { + Provider string `json:"provider"` // none | native | medflex + URL string `json:"url,omitempty"` +} + +type BioEntry struct { + Kind string `json:"kind"` + YearFrom *int `json:"year_from,omitempty"` + YearTo *int `json:"year_to,omitempty"` + Title string `json:"title"` + Description string `json:"description,omitempty"` +} + +type PriceSection struct { + ID int64 `json:"id"` + ParentID *int64 `json:"parent_id,omitempty"` + Slug string `json:"slug"` + Name string `json:"name"` + Items []PriceItem `json:"items,omitempty"` + Children []*PriceSection `json:"children,omitempty"` +} + +type PriceItem struct { + ID int64 `json:"id"` + Name string `json:"name"` + Price float64 `json:"price"` + Unit string `json:"unit,omitempty"` + From bool `json:"from,omitempty"` + ServiceID *int64 `json:"service_id,omitempty"` +} + +// PriceSectionBrief — раздел прайса без позиций: для меню в шапке, +// где нужны только название, адрес и число услуг. +type PriceSectionBrief struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + Count int `json:"count"` +} diff --git a/internal/domain/notice.go b/internal/domain/notice.go new file mode 100644 index 0000000..3190691 --- /dev/null +++ b/internal/domain/notice.go @@ -0,0 +1,22 @@ +package domain + +import "time" + +// LeadNotice — заявка в том виде, в каком она уходит администратору уведомлением: +// вместо идентификаторов — названия формы, услуги, врача и адрес филиала. +type LeadNotice struct { + ID int64 + FormCode string + FormTitle string + Recipients []string // адреса из настроек формы; пусто — берутся общие из конфига + Name string + Phone string + Email string + Service string + Doctor string + Branch string + Message string + Extra map[string]any + PageURL string + CreatedAt time.Time +} diff --git a/internal/domain/site.go b/internal/domain/site.go new file mode 100644 index 0000000..05b6e16 --- /dev/null +++ b/internal/domain/site.go @@ -0,0 +1,42 @@ +package domain + +// Banner — слайд или промо-блок. +// +// Layout: image — готовая картинка (текст нарисован на ней, заголовок необязателен); +// text — заголовок и подзаголовок на цветном фоне; doctor — фото врача, плашка с датой, +// заголовок, имя врача (Subtitle) и плашки-преимущества (Chips). Theme — цвет фона. +type Banner struct { + ID int64 `json:"id"` + Position string `json:"position"` + Layout string `json:"layout"` + Theme string `json:"theme"` + Title string `json:"title,omitempty"` + Subtitle string `json:"subtitle,omitempty"` + Badge string `json:"badge,omitempty"` + Chips []string `json:"chips,omitempty"` + Image string `json:"image,omitempty"` + MobileImage string `json:"mobile_image,omitempty"` + LinkURL string `json:"link_url,omitempty"` + ButtonText string `json:"button_text,omitempty"` + Disclaimer string `json:"disclaimer,omitempty"` +} + +// SearchHit — одна находка поиска по сайту. +type SearchHit struct { + Type string `json:"type"` // service | doctor | price | checkup + Title string `json:"title"` + Subtitle string `json:"subtitle,omitempty"` + Path string `json:"path,omitempty"` // для услуги + Slug string `json:"slug,omitempty"` // для врача и чек-апа + Price *float64 `json:"price,omitempty"` + Image string `json:"image,omitempty"` +} + +// SearchResult группирует находки по типам — так их показывает выпадающий поиск. +type SearchResult struct { + Query string `json:"query"` + Services []SearchHit `json:"services"` + Doctors []SearchHit `json:"doctors"` + Prices []SearchHit `json:"prices"` + Checkups []SearchHit `json:"checkups"` +} diff --git a/internal/megafon/client.go b/internal/megafon/client.go new file mode 100644 index 0000000..c1770f1 --- /dev/null +++ b/internal/megafon/client.go @@ -0,0 +1,226 @@ +// Package megafon — клиент REST API Виртуальной АТС МегаФон (раздел «Интеграция с CRM» → «Ваша CRM / REST API»). +// +// Запросы: GET https://<домен>.megapbx.ru/crmapi/v1/<команда>, заголовок X-API-KEY. +// История звонков — /history/json?start=…&end=…&type=…, время периода в UTC, формат 20060102T150405Z. +package megafon + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +// ErrUnauthorized — АТС отклонила ключ: он неверный, отозван или API выключен в настройках ВАТС. +var ErrUnauthorized = errors.New("megafon: ключ API не принят") + +type Client struct { + Base string // https://vats970345.megapbx.ru/crmapi/v1 + Key string + HTTP *http.Client +} + +// New собирает адрес API из домена ВАТС или полного адреса. +func New(baseOrDomain, key string) *Client { + b := strings.TrimRight(strings.TrimSpace(baseOrDomain), "/") + if b != "" && !strings.Contains(b, "://") { + b = "https://" + b + } + if b != "" && !strings.HasSuffix(strings.ToLower(b), "/crmapi/v1") { + b += "/crmapi/v1" + } + return &Client{Base: b, Key: strings.TrimSpace(key), HTTP: &http.Client{Timeout: 60 * time.Second}} +} + +func (c *Client) Configured() bool { return c != nil && c.Base != "" && c.Key != "" } + +// Domain — домен ВАТС для показа в админке (без ключа). +func (c *Client) Domain() string { + if c == nil { + return "" + } + if u, err := url.Parse(c.Base); err == nil { + return u.Host + } + return "" +} + +// Call — звонок из истории. Номера — как их прислала АТС. +type Call struct { + UID string + Type string // in | out | missed | … + Status string // success | missed | busy | notavailable | notallowed | … + Client string // номер клиента + Via string // номер, на который звонил клиент или с которого звонили («Через») + User string // логин сотрудника АТС + UserName string // имя сотрудника («DANA - 2ГИС», «КЦ DANA общий») + Start time.Time + Wait int // секунды ожидания + Duration int // секунды разговора + RecordURL string + Raw json.RawMessage +} + +// History — звонки за период [from, to). typ: "" или "all", "in", "out", "missed". +func (c *Client) History(ctx context.Context, from, to time.Time, typ string) ([]Call, error) { + q := url.Values{} + q.Set("start", from.UTC().Format("20060102T150405Z")) + q.Set("end", to.UTC().Format("20060102T150405Z")) + if typ != "" { + q.Set("type", typ) + } + body, err := c.get(ctx, "/history/json", q) + if err != nil { + return nil, err + } + return ParseHistory(body) +} + +// User — сотрудник АТС. +type User struct { + Login string `json:"login"` + Name string `json:"name"` + Ext string `json:"ext"` + Telnum string `json:"telnum"` + Email string `json:"email"` +} + +// Users — сотрудники АТС; заодно проверка, что ключ рабочий. +func (c *Client) Users(ctx context.Context) ([]User, error) { + body, err := c.get(ctx, "/users", nil) + if err != nil { + return nil, err + } + var out []User + if err := json.Unmarshal(body, &out); err != nil { + var wrap struct { + Items []User `json:"items"` + } + if err2 := json.Unmarshal(body, &wrap); err2 != nil { + return nil, fmt.Errorf("megafon: ответ /users не разобран: %w", err) + } + out = wrap.Items + } + return out, nil +} + +func (c *Client) get(ctx context.Context, path string, q url.Values) ([]byte, error) { + if !c.Configured() { + return nil, errors.New("megafon: не задан адрес или ключ API") + } + u := c.Base + path + if len(q) > 0 { + u += "?" + q.Encode() + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) + if err != nil { + return nil, err + } + req.Header.Set("X-API-KEY", c.Key) + req.Header.Set("Accept", "application/json") + resp, err := c.HTTP.Do(req) + if err != nil { + return nil, fmt.Errorf("megafon: АТС не ответила: %w", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(io.LimitReader(resp.Body, 64<<20)) + if err != nil { + return nil, err + } + switch { + case resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden: + return nil, ErrUnauthorized + case resp.StatusCode >= 300: + msg := strings.TrimSpace(string(body)) + if len(msg) > 200 { + msg = msg[:200] + } + return nil, fmt.Errorf("megafon: %s — HTTP %d %s", path, resp.StatusCode, msg) + } + return body, nil +} + +// ParseHistory разбирает ответ /history/json. Разбор терпимый к версиям API: имена полей +// и типы значений (число или строка) у разных ВАТС отличаются; исходная запись сохраняется в Raw. +func ParseHistory(body []byte) ([]Call, error) { + var items []json.RawMessage + if err := json.Unmarshal(body, &items); err != nil { + var wrap struct { + Items []json.RawMessage `json:"items"` + Data []json.RawMessage `json:"data"` + } + if err2 := json.Unmarshal(body, &wrap); err2 != nil { + return nil, fmt.Errorf("megafon: история не разобрана: %w", err) + } + items = append(wrap.Items, wrap.Data...) + } + out := make([]Call, 0, len(items)) + for _, raw := range items { + var m map[string]any + if err := json.Unmarshal(raw, &m); err != nil { + continue + } + c := Call{ + UID: str(m, "uid", "callid", "id"), + Type: strings.ToLower(str(m, "type", "direction")), + Status: strings.ToLower(str(m, "status")), + Client: str(m, "client", "phone", "from"), + Via: str(m, "diversion", "via", "line", "telnum", "to"), + User: str(m, "user", "account", "login"), + UserName: str(m, "user_name", "username", "name"), + Wait: num(m, "wait"), + Duration: num(m, "duration"), + RecordURL: str(m, "record", "link", "record_url"), + Raw: raw, + } + c.Start = parseTime(str(m, "start", "date", "time")) + if c.UID == "" || c.Start.IsZero() { + continue // без идентификатора и времени звонок не сохранить + } + out = append(out, c) + } + return out, nil +} + +func str(m map[string]any, keys ...string) string { + for _, k := range keys { + switch v := m[k].(type) { + case string: + if v = strings.TrimSpace(v); v != "" { + return v + } + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + } + } + return "" +} + +func num(m map[string]any, key string) int { + switch v := m[key].(type) { + case float64: + return int(v) + case string: + n, _ := strconv.Atoi(strings.TrimSpace(v)) + return n + } + return 0 +} + +func parseTime(s string) time.Time { + for _, f := range []string{time.RFC3339, "2006-01-02T15:04:05Z", "20060102T150405Z", "2006-01-02 15:04:05"} { + if t, err := time.Parse(f, s); err == nil { + return t + } + } + if n, err := strconv.ParseInt(s, 10, 64); err == nil && n > 1e9 { + return time.Unix(n, 0) + } + return time.Time{} +} diff --git a/internal/megafon/client_test.go b/internal/megafon/client_test.go new file mode 100644 index 0000000..0ca4c97 --- /dev/null +++ b/internal/megafon/client_test.go @@ -0,0 +1,65 @@ +package megafon + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestNewBase(t *testing.T) { + for in, want := range map[string]string{ + "vats970345.megapbx.ru": "https://vats970345.megapbx.ru/crmapi/v1", + "https://vats970345.megapbx.ru/": "https://vats970345.megapbx.ru/crmapi/v1", + "https://vats970345.megapbx.ru/crmapi/v1": "https://vats970345.megapbx.ru/crmapi/v1", + "https://vats970345.megapbx.ru/crmapi/v1/": "https://vats970345.megapbx.ru/crmapi/v1", + } { + if got := New(in, "k").Base; got != want { + t.Errorf("New(%q).Base = %q, want %q", in, got, want) + } + } + if New("", "k").Configured() || New("x.ru", "").Configured() { + t.Error("без адреса или ключа клиент не настроен") + } +} + +func TestHistory(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-API-KEY") != "secret" { + w.WriteHeader(http.StatusUnauthorized) + return + } + if r.URL.Path != "/crmapi/v1/history/json" || r.URL.Query().Get("start") != "20260920T210000Z" || r.URL.Query().Get("type") != "all" { + t.Errorf("запрос: %s", r.URL.String()) + } + _, _ = w.Write([]byte(`[ + {"uid":"1755936870","type":"in","status":"success","client":"79894787136","diversion":"79280470055", + "user":"kc","user_name":"КЦ DANA общий","start":"2026-09-23T08:22:00Z","wait":14,"duration":"9","record":"https://x/rec.mp3"}, + {"uid":1755936871,"type":"missed","status":"missed","client":"79894787136","via":"+7 (928) 047-00-55","start":"2026-09-23T08:21:00Z","wait":1,"duration":0}, + {"type":"in","client":"79000000000"} + ]`)) + })) + defer srv.Close() + + c := New(srv.URL, "secret") + calls, err := c.History(context.Background(), time.Date(2026, 9, 20, 21, 0, 0, 0, time.UTC), time.Date(2026, 9, 23, 21, 0, 0, 0, time.UTC), "all") + if err != nil { + t.Fatal(err) + } + if len(calls) != 2 { + t.Fatalf("звонков %d, ждали 2 (без uid — пропуск)", len(calls)) + } + a, b := calls[0], calls[1] + if a.UID != "1755936870" || a.Via != "79280470055" || a.UserName != "КЦ DANA общий" || a.Duration != 9 || a.Wait != 14 || a.RecordURL == "" { + t.Errorf("первый звонок: %+v", a) + } + if b.UID != "1755936871" || b.Type != "missed" || b.Via != "+7 (928) 047-00-55" { + t.Errorf("второй звонок: %+v", b) + } + + if _, err := New(srv.URL, "wrong").History(context.Background(), time.Now(), time.Now(), ""); !errors.Is(err, ErrUnauthorized) { + t.Errorf("неверный ключ: %v", err) + } +} diff --git a/internal/notify/email.go b/internal/notify/email.go new file mode 100644 index 0000000..865ed76 --- /dev/null +++ b/internal/notify/email.go @@ -0,0 +1,207 @@ +// Package notify отправляет администраторам уведомления о новых заявках. +// +// Канал — электронная почта через SMTP. Мессенджеры с зарубежными серверами +// (Telegram и т. п.) сюда намеренно не добавлены: в заявке имя, телефон и услуга, +// а политика обработки ПДн клиники (п. 11) заявляет, что трансграничной передачи нет. +package notify + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/base64" + "errors" + "fmt" + "mime" + "net" + "net/mail" + "net/smtp" + "strconv" + "strings" + "time" + + "dana/internal/domain" +) + +type Notifier interface { + LeadCreated(ctx context.Context, lead domain.LeadNotice) error +} + +// Email шлёт письмо о заявке. Порт 465 — сразу TLS (так работает smtp.yandex.ru), +// другие порты — обычное соединение с переходом на TLS через STARTTLS. +type Email struct { + Host string + Port int + User string + Password string + From string // «DANA — сайт » или просто адрес + To []string // общие получатели, если в форме свои не заданы + SiteURL string // для ссылки на заявку в админке; можно пустым +} + +var errNoRecipients = errors.New("notify: no recipients") + +func (e *Email) LeadCreated(ctx context.Context, lead domain.LeadNotice) error { + to := validAddresses(lead.Recipients) + if len(to) == 0 { + to = validAddresses(e.To) + } + if len(to) == 0 { + return errNoRecipients + } + from, err := mail.ParseAddress(e.From) + if err != nil { + return fmt.Errorf("notify: bad From %q: %w", e.From, err) + } + + subject := "Заявка с сайта" + if lead.FormTitle != "" { + subject += ": " + lead.FormTitle + } + msg := buildMessage(from, to, subject, leadText(lead, e.SiteURL)) + return e.send(ctx, from.Address, to, msg) +} + +func (e *Email) send(ctx context.Context, from string, to []string, msg []byte) error { + addr := net.JoinHostPort(e.Host, strconv.Itoa(e.Port)) + dialer := &net.Dialer{Timeout: 15 * time.Second} + tlsCfg := &tls.Config{ServerName: e.Host, MinVersion: tls.VersionTLS12} + + var conn net.Conn + var err error + if e.Port == 465 { + conn, err = (&tls.Dialer{NetDialer: dialer, Config: tlsCfg}).DialContext(ctx, "tcp", addr) + } else { + conn, err = dialer.DialContext(ctx, "tcp", addr) + } + if err != nil { + return fmt.Errorf("notify: dial %s: %w", addr, err) + } + if deadline, ok := ctx.Deadline(); ok { + _ = conn.SetDeadline(deadline) + } + + c, err := smtp.NewClient(conn, e.Host) + if err != nil { + conn.Close() + return fmt.Errorf("notify: smtp handshake: %w", err) + } + defer c.Close() + + if e.Port != 465 { + if ok, _ := c.Extension("STARTTLS"); ok { + if err := c.StartTLS(tlsCfg); err != nil { + return fmt.Errorf("notify: starttls: %w", err) + } + } + } + if e.User != "" { + if err := c.Auth(smtp.PlainAuth("", e.User, e.Password, e.Host)); err != nil { + return fmt.Errorf("notify: auth: %w", err) + } + } + if err := c.Mail(from); err != nil { + return fmt.Errorf("notify: MAIL FROM: %w", err) + } + for _, rcpt := range to { + if err := c.Rcpt(rcpt); err != nil { + return fmt.Errorf("notify: RCPT TO %s: %w", rcpt, err) + } + } + w, err := c.Data() + if err != nil { + return fmt.Errorf("notify: DATA: %w", err) + } + if _, err := w.Write(msg); err != nil { + w.Close() + return fmt.Errorf("notify: write: %w", err) + } + if err := w.Close(); err != nil { + return fmt.Errorf("notify: finish DATA: %w", err) + } + return c.Quit() +} + +func validAddresses(list []string) []string { + out := make([]string, 0, len(list)) + for _, raw := range list { + if a, err := mail.ParseAddress(strings.TrimSpace(raw)); err == nil { + out = append(out, a.Address) + } + } + return out +} + +// oneLine убирает переводы строк: значения из формы не должны превращаться в заголовки письма. +func oneLine(s string) string { + return strings.Join(strings.Fields(s), " ") +} + +func leadText(l domain.LeadNotice, siteURL string) string { + var b strings.Builder + fmt.Fprintf(&b, "Новая заявка № %d\n", l.ID) + if !l.CreatedAt.IsZero() { + fmt.Fprintf(&b, "Время: %s\n", l.CreatedAt.In(moscow).Format("02.01.2006 15:04")) + } + b.WriteString("\n") + + line := func(label, value string) { + if value = strings.TrimSpace(value); value != "" { + fmt.Fprintf(&b, "%s: %s\n", label, value) + } + } + line("Форма", l.FormTitle) + line("Имя", oneLine(l.Name)) + line("Телефон", oneLine(l.Phone)) + line("E-mail", oneLine(l.Email)) + line("Услуга", l.Service) + line("Врач", l.Doctor) + line("Филиал", l.Branch) + if v, ok := l.Extra["date"].(string); ok { + line("Желаемая дата", oneLine(v)) + } + if v, ok := l.Extra["time"].(string); ok { + line("Удобное время", oneLine(v)) + } + if v, ok := l.Extra["rating"].(float64); ok { + line("Оценка", strconv.Itoa(int(v))) + } + if msg := strings.TrimSpace(l.Message); msg != "" { + fmt.Fprintf(&b, "\nСообщение:\n%s\n", msg) + } + b.WriteString("\n") + line("Страница", l.PageURL) + if siteURL != "" { + line("Все заявки", strings.TrimRight(siteURL, "/")+"/admin/") + } + return b.String() +} + +var moscow = func() *time.Location { + if loc, err := time.LoadLocation("Europe/Moscow"); err == nil { + return loc + } + return time.FixedZone("MSK", 3*60*60) +}() + +func buildMessage(from *mail.Address, to []string, subject, body string) []byte { + var buf bytes.Buffer + header := func(k, v string) { fmt.Fprintf(&buf, "%s: %s\r\n", k, v) } + + header("From", from.String()) + header("To", strings.Join(to, ", ")) + header("Subject", mime.BEncoding.Encode("utf-8", oneLine(subject))) + header("Date", time.Now().Format(time.RFC1123Z)) + header("MIME-Version", "1.0") + header("Content-Type", "text/plain; charset=utf-8") + header("Content-Transfer-Encoding", "base64") + buf.WriteString("\r\n") + + encoded := base64.StdEncoding.EncodeToString([]byte(body)) + for len(encoded) > 76 { + buf.WriteString(encoded[:76] + "\r\n") + encoded = encoded[76:] + } + buf.WriteString(encoded + "\r\n") + return buf.Bytes() +} diff --git a/internal/notify/email_test.go b/internal/notify/email_test.go new file mode 100644 index 0000000..b293484 --- /dev/null +++ b/internal/notify/email_test.go @@ -0,0 +1,166 @@ +package notify + +import ( + "bufio" + "context" + "encoding/base64" + "net" + "strconv" + "strings" + "testing" + "time" + + "dana/internal/domain" +) + +// fakeSMTP — минимальный SMTP-сервер без TLS и авторизации: принимает одно письмо. +func fakeSMTP(t *testing.T) (host string, port int, got <-chan string) { + t.Helper() + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { ln.Close() }) + + ch := make(chan string, 1) + go func() { + conn, err := ln.Accept() + if err != nil { + return + } + defer conn.Close() + r := bufio.NewReader(conn) + w := func(s string) { conn.Write([]byte(s + "\r\n")) } + + w("220 fake") + var transcript strings.Builder + inData := false + for { + line, err := r.ReadString('\n') + if err != nil { + return + } + if inData { + if line == ".\r\n" { + inData = false + w("250 queued") + continue + } + transcript.WriteString(line) + continue + } + cmd := strings.ToUpper(strings.TrimSpace(line)) + switch { + case strings.HasPrefix(cmd, "EHLO"), strings.HasPrefix(cmd, "HELO"): + w("250 fake") + case strings.HasPrefix(cmd, "MAIL FROM"), strings.HasPrefix(cmd, "RCPT TO"): + transcript.WriteString(strings.TrimSpace(line) + "\n") + w("250 ok") + case cmd == "DATA": + inData = true + w("354 go") + case cmd == "QUIT": + w("221 bye") + ch <- transcript.String() + return + default: + w("250 ok") + } + } + }() + + addr := ln.Addr().(*net.TCPAddr) + return "127.0.0.1", addr.Port, ch +} + +func TestEmailLeadCreated(t *testing.T) { + host, port, got := fakeSMTP(t) + e := &Email{Host: host, Port: port, From: "DANA сайт ", To: []string{"admin@example.ru"}, SiteURL: "https://danamedical.ru"} + + lead := domain.LeadNotice{ + ID: 42, + FormTitle: "Запись на приём", + Name: "Иван\r\nBcc: evil@example.com", + Phone: "+7 (928) 000-00-99", + Service: "Консультация педиатра", + Extra: map[string]any{"date": "2026-09-20", "time": "Утро, 08:00–12:00"}, + Message: "Тестовая заявка", + PageURL: "/services/detskaya-klinika/priem-pediatra/", + CreatedAt: time.Date(2026, 9, 15, 9, 30, 0, 0, time.UTC), + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := e.LeadCreated(ctx, lead); err != nil { + t.Fatalf("LeadCreated: %v", err) + } + + var transcript string + select { + case transcript = <-got: + case <-time.After(5 * time.Second): + t.Fatal("fake SMTP did not receive message") + } + + if !strings.Contains(transcript, "MAIL FROM:") || !strings.Contains(transcript, "RCPT TO:") { + t.Fatalf("envelope not as expected:\n%s", transcript) + } + // перевод строки в имени не должен стать заголовком письма + if strings.Contains(transcript, "\r\nBcc:") { + t.Fatalf("header injection:\n%s", transcript) + } + + body := decodeBody(t, transcript) + for _, want := range []string{ + "Новая заявка № 42", + "Время: 15.09.2026 12:30", + "Форма: Запись на приём", + "Имя: Иван Bcc: evil@example.com", + "Телефон: +7 (928) 000-00-99", + "Услуга: Консультация педиатра", + "Желаемая дата: 2026-09-20", + "Удобное время: Утро, 08:00–12:00", + "Тестовая заявка", + "Все заявки: https://danamedical.ru/admin/", + } { + if !strings.Contains(body, want) { + t.Errorf("body has no %q:\n%s", want, body) + } + } +} + +func TestFormRecipientsOverrideDefault(t *testing.T) { + host, port, got := fakeSMTP(t) + e := &Email{Host: host, Port: port, From: "site@example.ru", To: []string{"default@example.ru"}} + lead := domain.LeadNotice{ID: 1, Recipients: []string{"doctors@example.ru", "не адрес"}} + + if err := e.LeadCreated(context.Background(), lead); err != nil { + t.Fatal(err) + } + transcript := <-got + if !strings.Contains(transcript, "RCPT TO:") || strings.Contains(transcript, "default@example.ru") { + t.Fatalf("recipients of the form must replace defaults:\n%s", transcript) + } +} + +func TestNoRecipients(t *testing.T) { + e := &Email{Host: "127.0.0.1", Port: 1, From: "site@example.ru"} + if err := e.LeadCreated(context.Background(), domain.LeadNotice{ID: 1}); err != errNoRecipients { + t.Fatalf("want errNoRecipients, got %v", err) + } +} + +func decodeBody(t *testing.T, transcript string) string { + t.Helper() + parts := strings.SplitN(transcript, "\r\n\r\n", 2) + if len(parts) != 2 { + t.Fatalf("no body separator:\n%s", transcript) + } + raw, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(parts[1], "\r\n", "")) + if err != nil { + t.Fatalf("decode body: %v", err) + } + return string(raw) +} + +var _ = strconv.Itoa diff --git a/internal/onec/client.go b/internal/onec/client.go new file mode 100644 index 0000000..53050fc --- /dev/null +++ b/internal/onec/client.go @@ -0,0 +1,297 @@ +// Package onec — клиент HTTP-сервиса 1С: филиалы, врачи, расписание и запись на приём. +// Контракт описан в ТЗ «расписание врачей и запись из 1С»; адреса методов в 1С могут отличаться — +// тогда правится только этот файл. +package onec + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" +) + +var ( + ErrUnauthorized = errors.New("1С не приняла ключ доступа") + ErrSlotBusy = errors.New("окно уже занято") + ErrDoctorNotFound = errors.New("врач не найден в 1С") + ErrValidation = errors.New("1С отклонила данные записи") + ErrUnavailable = errors.New("1С недоступна") +) + +type Client struct { + Base string + Key string + AuthHeader string + HTTP *http.Client +} + +func New(base, key, authHeader string) *Client { + if authHeader == "" { + authHeader = "X-Api-Key" + } + return &Client{ + Base: strings.TrimRight(strings.TrimSpace(base), "/"), + Key: strings.TrimSpace(key), + AuthHeader: authHeader, + HTTP: &http.Client{Timeout: 20 * time.Second}, + } +} + +func (c *Client) Configured() bool { return c != nil && c.Base != "" && c.Key != "" } + +// Host — адрес 1С для показа в админке (без ключа и пути). +func (c *Client) Host() string { + if c == nil { + return "" + } + if u, err := url.Parse(c.Base); err == nil { + return u.Host + } + return "" +} + +type Branch struct { + ID string `json:"id"` + Name string `json:"name"` + Address string `json:"address"` +} + +type Doctor struct { + ID string `json:"id"` + Name string `json:"name"` + Specialties []string `json:"specialties"` + BranchIDs []string `json:"branch_ids"` + KidsFrom *int `json:"kids_from"` + Active *bool `json:"active"` +} + +func (d Doctor) IsActive() bool { return d.Active == nil || *d.Active } + +type Service struct { + ID string `json:"id"` + Name string `json:"name"` + DurationMin int `json:"duration_min"` + Price float64 `json:"price"` +} + +type Slot struct { + DoctorID string `json:"doctor_id"` + BranchID string `json:"branch_id"` + Cabinet string `json:"cabinet"` + Start time.Time `json:"start"` + End time.Time `json:"end"` + Status string `json:"status"` // free | busy + SlotID string `json:"slot_id"` +} + +func (s Slot) Free() bool { return s.Status == "free" } + +type AppointmentRequest struct { + RequestID string `json:"request_id"` + DoctorID string `json:"doctor_id"` + BranchID string `json:"branch_id"` + ServiceID string `json:"service_id,omitempty"` + SlotID string `json:"slot_id,omitempty"` + Start time.Time `json:"start"` + Name string `json:"patient_name"` + Phone string `json:"patient_phone"` + BirthDate string `json:"birth_date,omitempty"` + Comment string `json:"comment,omitempty"` + Source string `json:"source"` +} + +type Appointment struct { + ID string `json:"appointment_id"` + Status string `json:"status"` // booked | arrived | cancelled + Start time.Time `json:"start"` + End time.Time `json:"end"` +} + +func (c *Client) Branches(ctx context.Context) ([]Branch, error) { + var out []Branch + return out, c.getList(ctx, "/branches", nil, "branches", &out) +} + +func (c *Client) Doctors(ctx context.Context) ([]Doctor, error) { + var out []Doctor + return out, c.getList(ctx, "/doctors", nil, "doctors", &out) +} + +func (c *Client) Services(ctx context.Context, doctorID string) ([]Service, error) { + var out []Service + return out, c.getList(ctx, "/doctors/"+url.PathEscape(doctorID)+"/services", nil, "services", &out) +} + +// Schedule — окна за период [from, to]; doctorID пустой — по всем врачам. +func (c *Client) Schedule(ctx context.Context, from, to time.Time, doctorID string) ([]Slot, error) { + q := url.Values{} + q.Set("from", from.Format(time.RFC3339)) + q.Set("to", to.Format(time.RFC3339)) + if doctorID != "" { + q.Set("doctor_id", doctorID) + } + var out []Slot + return out, c.getList(ctx, "/schedule", q, "slots", &out) +} + +// Changes — long polling: 1С держит запрос до wait, пока версия расписания не станет больше since, +// и отвечает {"version": N, "doctor_ids": [...]} (пустой doctor_ids — изменилось у многих). +func (c *Client) Changes(ctx context.Context, since int64, wait time.Duration) (int64, []string, error) { + q := url.Values{} + q.Set("since", fmt.Sprint(since)) + q.Set("wait", fmt.Sprint(int(wait.Seconds()))) + raw, err := c.doWith(ctx, &http.Client{Timeout: wait + 10*time.Second}, http.MethodGet, "/changes", q, nil) + if err != nil { + return since, nil, err + } + var out struct { + Version int64 `json:"version"` + DoctorIDs []string `json:"doctor_ids"` + } + if err := json.Unmarshal(raw, &out); err != nil { + return since, nil, fmt.Errorf("onec: /changes — ответ не разобран: %s", short(raw)) + } + return out.Version, out.DoctorIDs, nil +} + +func (c *Client) Book(ctx context.Context, req AppointmentRequest) (*Appointment, error) { + body, err := json.Marshal(req) + if err != nil { + return nil, err + } + raw, err := c.do(ctx, http.MethodPost, "/appointments", nil, body) + if err != nil { + return nil, err + } + var a Appointment + if err := json.Unmarshal(raw, &a); err != nil || a.ID == "" { + return nil, fmt.Errorf("onec: ответ на запись не разобран: %s", short(raw)) + } + return &a, nil +} + +func (c *Client) Appointment(ctx context.Context, id string) (*Appointment, error) { + raw, err := c.do(ctx, http.MethodGet, "/appointments/"+url.PathEscape(id), nil, nil) + if err != nil { + return nil, err + } + var a Appointment + if err := json.Unmarshal(raw, &a); err != nil { + return nil, fmt.Errorf("onec: статус записи не разобран: %s", short(raw)) + } + return &a, nil +} + +func (c *Client) Cancel(ctx context.Context, id string) error { + _, err := c.do(ctx, http.MethodDelete, "/appointments/"+url.PathEscape(id), nil, nil) + return err +} + +// getList принимает ответ и в виде массива, и в виде объекта {"": [...]}. +func (c *Client) getList(ctx context.Context, path string, q url.Values, key string, out any) error { + raw, err := c.do(ctx, http.MethodGet, path, q, nil) + if err != nil { + return err + } + trimmed := bytes.TrimSpace(raw) + if len(trimmed) > 0 && trimmed[0] == '[' { + return json.Unmarshal(trimmed, out) + } + var wrap map[string]json.RawMessage + if err := json.Unmarshal(trimmed, &wrap); err != nil { + return fmt.Errorf("onec: %s — ответ не разобран: %s", path, short(raw)) + } + part, found := wrap[key] + if !found { + return fmt.Errorf("onec: %s — в ответе нет поля %q", path, key) + } + return json.Unmarshal(part, out) +} + +func (c *Client) do(ctx context.Context, method, path string, q url.Values, body []byte) ([]byte, error) { + return c.doWith(ctx, c.HTTP, method, path, q, body) +} + +func (c *Client) doWith(ctx context.Context, hc *http.Client, method, path string, q url.Values, body []byte) ([]byte, error) { + if !c.Configured() { + return nil, errors.New("onec: не задан адрес или ключ 1С") + } + u := c.Base + path + if len(q) > 0 { + u += "?" + q.Encode() + } + var rd io.Reader + if body != nil { + rd = bytes.NewReader(body) + } + req, err := http.NewRequestWithContext(ctx, method, u, rd) + if err != nil { + return nil, err + } + req.Header.Set(c.AuthHeader, c.Key) + req.Header.Set("Accept", "application/json") + if body != nil { + req.Header.Set("Content-Type", "application/json; charset=utf-8") + } + resp, err := hc.Do(req) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrUnavailable, err) + } + defer resp.Body.Close() + raw, err := io.ReadAll(io.LimitReader(resp.Body, 32<<20)) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrUnavailable, err) + } + if resp.StatusCode/100 == 2 { + return raw, nil + } + return nil, apiError(resp.StatusCode, raw) +} + +// apiError превращает ошибку 1С ({"code":"slot_busy","message":"…"} или {"error":{…}}) в типизированную. +func apiError(status int, raw []byte) error { + var e struct { + Code string `json:"code"` + Message string `json:"message"` + Error *struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error"` + } + _ = json.Unmarshal(raw, &e) + code, msg := e.Code, e.Message + if e.Error != nil { + code, msg = e.Error.Code, e.Error.Message + } + var base error + switch { + case code == "slot_busy" || status == http.StatusConflict: + base = ErrSlotBusy + case code == "doctor_not_found" || status == http.StatusNotFound: + base = ErrDoctorNotFound + case code == "unauthorized" || status == http.StatusUnauthorized || status == http.StatusForbidden: + base = ErrUnauthorized + case code == "validation" || status == http.StatusBadRequest: + base = ErrValidation + default: + base = ErrUnavailable + } + if msg == "" { + msg = short(raw) + } + return fmt.Errorf("%w (HTTP %d): %s", base, status, msg) +} + +func short(b []byte) string { + s := strings.TrimSpace(string(b)) + if r := []rune(s); len(r) > 200 { + s = string(r[:200]) + "…" + } + return s +} diff --git a/internal/onec/client_test.go b/internal/onec/client_test.go new file mode 100644 index 0000000..099597e --- /dev/null +++ b/internal/onec/client_test.go @@ -0,0 +1,93 @@ +package onec + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func newFake(t *testing.T, h http.HandlerFunc) *Client { + t.Helper() + srv := httptest.NewServer(h) + t.Cleanup(srv.Close) + return New(srv.URL, "secret", "") +} + +func TestClientSendsKeyAndParsesBothShapes(t *testing.T) { + c := newFake(t, func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-Api-Key") != "secret" { + http.Error(w, `{"code":"unauthorized","message":"нет ключа"}`, http.StatusUnauthorized) + return + } + switch r.URL.Path { + case "/branches": + _, _ = w.Write([]byte(`[{"id":"b1","name":"Ляхова, 22","address":"ул. Ляхова, 22"}]`)) + case "/doctors": + _, _ = w.Write([]byte(`{"doctors":[{"id":"d1","name":"Иванов Иван Иванович","branch_ids":["b1"],"active":false}]}`)) + case "/schedule": + if r.URL.Query().Get("doctor_id") != "d1" { + t.Errorf("doctor_id не передан: %s", r.URL.RawQuery) + } + _, _ = w.Write([]byte(`{"slots":[{"doctor_id":"d1","branch_id":"b1","start":"2026-10-01T10:00:00+03:00","end":"2026-10-01T10:30:00+03:00","status":"free"}]}`)) + } + }) + ctx := context.Background() + br, err := c.Branches(ctx) + if err != nil || len(br) != 1 || br[0].ID != "b1" { + t.Fatalf("branches: %v %+v", err, br) + } + docs, err := c.Doctors(ctx) + if err != nil || len(docs) != 1 || docs[0].IsActive() { + t.Fatalf("doctors: %v %+v", err, docs) + } + from := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC) + slots, err := c.Schedule(ctx, from, from.Add(24*time.Hour), "d1") + if err != nil || len(slots) != 1 || !slots[0].Free() { + t.Fatalf("schedule: %v %+v", err, slots) + } + + bad := New(c.Base, "wrong", "") + if _, err := bad.Branches(ctx); !errors.Is(err, ErrUnauthorized) { + t.Fatalf("ждали ErrUnauthorized, получили %v", err) + } +} + +func TestBookErrors(t *testing.T) { + cases := []struct { + status int + body string + want error + }{ + {409, `{"code":"slot_busy","message":"Окно занято"}`, ErrSlotBusy}, + {404, `{"error":{"code":"doctor_not_found","message":"нет врача"}}`, ErrDoctorNotFound}, + {400, `{"code":"validation","message":"нет телефона"}`, ErrValidation}, + {500, `oops`, ErrUnavailable}, + } + for _, tc := range cases { + c := newFake(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tc.status) + _, _ = w.Write([]byte(tc.body)) + }) + _, err := c.Book(context.Background(), AppointmentRequest{DoctorID: "d1"}) + if !errors.Is(err, tc.want) { + t.Errorf("HTTP %d: ждали %v, получили %v", tc.status, tc.want, err) + } + } +} + +func TestBookSuccess(t *testing.T) { + c := newFake(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/appointments" { + t.Errorf("не тот запрос: %s %s", r.Method, r.URL.Path) + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"appointment_id":"A-1","status":"booked","start":"2026-10-01T10:00:00+03:00","end":"2026-10-01T10:30:00+03:00"}`)) + }) + a, err := c.Book(context.Background(), AppointmentRequest{DoctorID: "d1", Source: "Сайт"}) + if err != nil || a.ID != "A-1" || a.Status != "booked" { + t.Fatalf("book: %v %+v", err, a) + } +} diff --git a/internal/prodoctorov/client.go b/internal/prodoctorov/client.go new file mode 100644 index 0000000..6e51912 --- /dev/null +++ b/internal/prodoctorov/client.go @@ -0,0 +1,111 @@ +// Package prodoctorov reads the cabinet's authenticated online-appointment export. +// This is NOT a public API key integration and does not export telephone calls. +package prodoctorov + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + "time" +) + +const downloadURL = "https://prodoctorov.ru/api/cabinet/lpu/appointments/direct/download/" +const maxDownload = 10 << 20 + +type Client struct { + cookie, lpu string + http *http.Client + endpoint string +} + +// New keeps the cabinet session on the server. Both empty values disable the +// integration. Supplying only one, or malformed credentials, is a config error. +func New(cookie, lpu string) (*Client, error) { + lpu = strings.TrimSpace(lpu) + if cookie == "" && lpu == "" { + return nil, nil + } + if strings.TrimSpace(cookie) == "" || strings.ContainsAny(cookie, "\r\n") || + len(cookie) > 16384 || !strings.Contains(cookie, "=") || + !regexp.MustCompile(`^[1-9][0-9]{0,17}$`).MatchString(lpu) { + return nil, fmt.Errorf("ПроДокторов: задайте корректные PRODOCTOROV_COOKIE и PRODOCTOROV_LPU_ID на сервере") + } + return &Client{cookie: cookie, lpu: lpu, endpoint: downloadURL, http: &http.Client{ + Timeout: 45 * time.Second, + // Never forward the cabinet session through a redirect, even same-host: + // login redirects are an explicit expired-session error, not empty data. + CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, + }}, nil +} + +func (c *Client) Configured() bool { return c != nil && c.cookie != "" && c.lpu != "" } + +// Download returns XLSX bytes for one calendar month. No body, cookies, patient +// values, or response URLs are included in errors or logs. +func (c *Client) Download(ctx context.Context, year int, month time.Month) ([]byte, error) { + if !c.Configured() { + return nil, fmt.Errorf("ПроДокторов не подключён") + } + if year < 2000 || year > 2100 || month < time.January || month > time.December { + return nil, fmt.Errorf("ПроДокторов: некорректный месяц выгрузки") + } + u, err := url.Parse(c.endpoint) + if err != nil { + return nil, fmt.Errorf("ПроДокторов: некорректный адрес выгрузки") + } + q := u.Query() + q.Set("month", strconv.Itoa(int(month))) + q.Set("year", strconv.Itoa(year)) + q.Set("lpu", c.lpu) + u.RawQuery = q.Encode() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) + if err != nil { + return nil, fmt.Errorf("ПроДокторов: не удалось подготовить запрос") + } + req.Header.Set("Cookie", c.cookie) + req.Header.Set("Accept", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + res, err := c.http.Do(req) + if err != nil { + if ctx.Err() != nil { + return nil, ctx.Err() + } + return nil, fmt.Errorf("ПроДокторов: не удалось получить выгрузку (соединение или таймаут)") + } + defer res.Body.Close() + // Servicepipe can reject the request before it reaches the cabinet. This + // does not establish that the cabinet session has expired. Do not retry a + // challenge, expose its body, or publish it as an empty appointment export. + protected := res.Header.Get("X-SP-CRID") != "" + if res.StatusCode != http.StatusOK { + if res.StatusCode == http.StatusForbidden && protected { + return nil, fmt.Errorf("ПроДокторов: доступ запрещён (HTTP 403, ответ через Servicepipe); это не подтверждает истечение сессии. Проверьте полный Cookie; при повторном отказе согласуйте серверную выгрузку с поддержкой ПроДокторов") + } + if res.StatusCode == http.StatusUnauthorized || res.StatusCode >= 300 && res.StatusCode < 400 { + return nil, fmt.Errorf("ПроДокторов: кабинет не авторизовал выгрузку (HTTP %d); войдите заново и обновите полный Cookie", res.StatusCode) + } + if res.StatusCode == http.StatusForbidden { + return nil, fmt.Errorf("ПроДокторов: доступ к выгрузке не получен (HTTP %d); проверьте сессию кабинета и защиту сайта", res.StatusCode) + } + return nil, fmt.Errorf("ПроДокторов: ошибка выгрузки (HTTP %d)", res.StatusCode) + } + data, err := io.ReadAll(io.LimitReader(res.Body, maxDownload+1)) + if err != nil { + return nil, fmt.Errorf("ПроДокторов: файл не дочитался") + } + if len(data) > maxDownload { + return nil, fmt.Errorf("ПроДокторов: файл больше 10 МБ") + } + if !bytes.HasPrefix(data, []byte{'P', 'K', 3, 4}) { + if protected { + return nil, fmt.Errorf("ПроДокторов: вместо XLSX получена другая страница через Servicepipe; автоматическая выгрузка не выполнена") + } + return nil, fmt.Errorf("ПроДокторов: вместо XLSX получена другая страница; проверьте сессию кабинета и защиту сайта") + } + return data, nil +} diff --git a/internal/prodoctorov/client_test.go b/internal/prodoctorov/client_test.go new file mode 100644 index 0000000..5e2517f --- /dev/null +++ b/internal/prodoctorov/client_test.go @@ -0,0 +1,145 @@ +package prodoctorov + +import ( + "bytes" + "context" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +func TestConfig(t *testing.T) { + c, err := New("", "") + if err != nil || c.Configured() { + t.Fatal("empty configuration must disable the connector") + } + for _, args := range [][2]string{{"", "82347"}, {"session=test", ""}, {"session=x\r\nInjected: bad", "82347"}, {"session=test", "-1"}, {"session=test", "82347&lpu=other"}, {"Cookie", "82347"}} { + if _, err := New(args[0], args[1]); err == nil { + t.Fatal("malformed configuration accepted") + } + } +} + +func TestDownloadQueryAndCookie(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/api/cabinet/lpu/appointments/direct/download/" || r.Method != "GET" || + r.URL.Query().Get("month") != "10" || r.URL.Query().Get("year") != "2026" || r.URL.Query().Get("lpu") != "82347" || + r.Header.Get("Cookie") != "session=synthetic" { + t.Error("wrong export request") + } + _, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'}) + })) + defer s.Close() + c, _ := New("session=synthetic", "82347") + c.endpoint = s.URL + "/api/cabinet/lpu/appointments/direct/download/" + data, err := c.Download(context.Background(), 2026, time.October) + if err != nil || !bytes.HasPrefix(data, []byte("PK")) { + t.Fatal("valid export failed", err) + } +} + +func TestDownloadErrorsDoNotLeakResponse(t *testing.T) { + for _, status := range []int{200, 401, 403, 429, 500} { + t.Run(http.StatusText(status), func(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(status) + _, _ = io.WriteString(w, "patient-secret session=synthetic") + })) + defer s.Close() + c, _ := New("session=synthetic", "82347") + c.endpoint = s.URL + _, err := c.Download(context.Background(), 2026, time.October) + if err == nil || strings.Contains(err.Error(), "patient-secret") || strings.Contains(err.Error(), "synthetic") { + t.Fatal("missing or leaking error") + } + }) + } +} + +func TestDownloadProtectionIsNotExpiredSession(t *testing.T) { + for _, tc := range []struct { + name string + status int + body string + want string + }{ + {"forbidden", http.StatusForbidden, "private challenge", "HTTP 403, ответ через Servicepipe"}, + {"challenge page", http.StatusOK, "private challenge", "другая страница через Servicepipe"}, + } { + t.Run(tc.name, func(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-SP-CRID", "private-request-id") + w.WriteHeader(tc.status) + _, _ = io.WriteString(w, tc.body) + })) + defer s.Close() + c, _ := New("session=synthetic", "82347") + c.endpoint = s.URL + _, err := c.Download(context.Background(), 2026, time.October) + if err == nil || !strings.Contains(err.Error(), tc.want) || + strings.Contains(err.Error(), "private") || strings.Contains(err.Error(), "synthetic") { + t.Fatal("protection was misreported or secret data leaked") + } + }) + } +} + +func TestDownloadProtectionHeaderDoesNotRejectXLSX(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-SP-CRID", "synthetic-request-id") + _, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'}) + })) + defer s.Close() + c, _ := New("session=synthetic", "82347") + c.endpoint = s.URL + if _, err := c.Download(context.Background(), 2026, time.October); err != nil { + t.Fatal("valid export rejected solely because of proxy header", err) + } +} + +func TestRedirectDoesNotForwardSession(t *testing.T) { + for _, sameHost := range []bool{false, true} { + calls := 0 + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ })) + var source *httptest.Server + source = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/login" { + calls++ + return + } + location := target.URL + if sameHost { + location = source.URL + "/login" + } + http.Redirect(w, r, location, http.StatusFound) + })) + c, _ := New("session=synthetic", "82347") + c.endpoint = source.URL + _, err := c.Download(context.Background(), 2026, time.October) + source.Close() + target.Close() + if err == nil || calls != 0 { + t.Fatal("redirect followed or accepted") + } + } +} + +func TestDownloadBoundAndCancellation(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(append([]byte{'P', 'K', 3, 4}, bytes.Repeat([]byte{'x'}, maxDownload)...)) + })) + defer s.Close() + c, _ := New("session=synthetic", "82347") + c.endpoint = s.URL + if _, err := c.Download(context.Background(), 2026, time.October); err == nil { + t.Fatal("oversized export accepted") + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + if _, err := c.Download(ctx, 2026, time.October); err != context.Canceled { + t.Fatal("cancellation lost", err) + } +} diff --git a/internal/sberhealth/biff.go b/internal/sberhealth/biff.go new file mode 100644 index 0000000..c0d57a5 --- /dev/null +++ b/internal/sberhealth/biff.go @@ -0,0 +1,470 @@ +package sberhealth + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "strconv" + "unicode/utf16" + + "github.com/extrame/ole2" +) + +// Минимальный читатель книг Excel 97–2003 (BIFF8). Нужен потому, что общая таблица строк (SST) +// у больших выгрузок разбивается на записи CONTINUE посреди строки, и библиотека extrame/xls на +// этих границах смещает значения: часть ячеек получала чужие данные, а все строки ниже сдвигались. +// Здесь строки собираются строго по спецификации: у продолжения строки первым байтом идёт флаг +// кодировки (1 или 2 байта на символ), а блоки форматирования и фонетики могут пересекать границу. +// +// Поддерживаются только ячейки, которые встречаются в выгрузках: LABELSST, LABEL, NUMBER, RK, MULRK +// (и пустые BLANK/MULBLANK). Формулы, ошибки и прочее — явная ошибка: данные нельзя тихо терять. + +const ( + recEOF = 0x000A + recFormula = 0x0006 + recBoundSheet = 0x0085 + recMulRK = 0x00BD + recMulBlank = 0x00BE + recRString = 0x00D6 + recSST = 0x00FC + recLabelSST = 0x00FD + recContinue = 0x003C + recBlank = 0x0201 + recNumber = 0x0203 + recLabel = 0x0204 + recBoolErr = 0x0205 + recString = 0x0207 + recRK = 0x027E + recBOF = 0x0809 + + biff8Version = 0x0600 + maxSheetRows = 50000 + maxSheetCols = 100 + maxSSTItems = 1_000_000 +) + +type record struct { + id uint16 + data []byte + off int +} + +type biffSheet struct { + name string + rows [][]string +} + +// workbookStream достаёт из контейнера OLE2 поток Workbook (или Book) целиком. +func workbookStream(data []byte) ([]byte, error) { + o, err := ole2.Open(bytes.NewReader(data), "utf-8") + if err != nil || o == nil { + return nil, fmt.Errorf("контейнер XLS не открылся") + } + dir, err := o.ListDir() + if err != nil { + return nil, fmt.Errorf("каталог XLS не прочитан") + } + var book, root *ole2.File + for _, f := range dir { + switch f.Name() { + case "Workbook", "Book": + book = f + case "Root Entry": + root = f + } + } + if book == nil { + return nil, fmt.Errorf("в XLS нет потока Workbook") + } + stream, err := io.ReadAll(io.LimitReader(o.OpenFile(book, root), maxDownload+1)) + if err != nil { + return nil, fmt.Errorf("поток Workbook не дочитался") + } + if len(stream) > maxDownload { + return nil, fmt.Errorf("поток Workbook слишком велик") + } + return stream, nil +} + +func readRecords(stream []byte) ([]record, error) { + var recs []record + for pos := 0; pos < len(stream); { + if pos+4 > len(stream) { + if allZero(stream[pos:]) { + break + } + return nil, fmt.Errorf("оборванная запись в конце книги") + } + id := binary.LittleEndian.Uint16(stream[pos:]) + n := int(binary.LittleEndian.Uint16(stream[pos+2:])) + if id == 0 && n == 0 && allZero(stream[pos:]) { + break // выравнивание нулями после последней записи + } + if pos+4+n > len(stream) { + return nil, fmt.Errorf("запись выходит за пределы книги") + } + recs = append(recs, record{id: id, data: stream[pos+4 : pos+4+n], off: pos}) + pos += 4 + n + if len(recs) > 2_000_000 { + return nil, fmt.Errorf("слишком много записей в книге") + } + } + return recs, nil +} + +func allZero(b []byte) bool { + for _, v := range b { + if v != 0 { + return false + } + } + return true +} + +// chunkReader читает данные, разбитые на записи SST и CONTINUE. +type chunkReader struct { + chunks [][]byte + ci int + pos int +} + +func (r *chunkReader) avail() int { + if r.ci >= len(r.chunks) { + return 0 + } + return len(r.chunks[r.ci]) - r.pos +} + +// advance переходит к следующему непустому фрагменту; false — данные кончились. +func (r *chunkReader) advance() bool { + for { + r.ci++ + r.pos = 0 + if r.ci >= len(r.chunks) { + return false + } + if len(r.chunks[r.ci]) > 0 { + return true + } + } +} + +// take отдаёт n непрерывных байт из текущего фрагмента (заголовки полей через границу не переходят). +func (r *chunkReader) take(n int) ([]byte, error) { + if r.avail() == 0 && !r.advance() { + return nil, fmt.Errorf("таблица строк оборвана") + } + if r.avail() < n { + return nil, fmt.Errorf("поле таблицы строк разорвано границей записи") + } + b := r.chunks[r.ci][r.pos : r.pos+n] + r.pos += n + return b, nil +} + +// skip пропускает n байт, переходя между фрагментами без флага кодировки. +func (r *chunkReader) skip(n int) error { + for n > 0 { + if r.avail() == 0 && !r.advance() { + return fmt.Errorf("таблица строк оборвана при пропуске форматирования") + } + k := min(n, r.avail()) + r.pos += k + n -= k + } + return nil +} + +// readString читает одну строку Unicode (cch, флаги, символы, блоки форматирования/фонетики). +func (r *chunkReader) readString() (string, error) { + head, err := r.take(3) + if err != nil { + return "", err + } + cch := int(binary.LittleEndian.Uint16(head)) + flags := head[2] + high := flags&0x01 != 0 + runs, ext := 0, 0 + if flags&0x08 != 0 { + b, err := r.take(2) + if err != nil { + return "", err + } + runs = int(binary.LittleEndian.Uint16(b)) + } + if flags&0x04 != 0 { + b, err := r.take(4) + if err != nil { + return "", err + } + ext = int(binary.LittleEndian.Uint32(b)) + } + units := make([]uint16, 0, cch) + for remaining := cch; remaining > 0; { + if r.avail() == 0 { + // Продолжение строки: первый байт нового фрагмента — флаг кодировки. + if !r.advance() { + return "", fmt.Errorf("таблица строк оборвана внутри строки") + } + b, err := r.take(1) + if err != nil { + return "", err + } + high = b[0]&0x01 != 0 + } + width := 1 + if high { + width = 2 + } + n := min(remaining, r.avail()/width) + if n == 0 { + return "", fmt.Errorf("символ разорван границей записи") + } + raw, _ := r.take(n * width) + for i := 0; i < n; i++ { + if high { + units = append(units, binary.LittleEndian.Uint16(raw[i*2:])) + } else { + units = append(units, uint16(raw[i])) + } + } + remaining -= n + } + if err := r.skip(runs*4 + ext); err != nil { + return "", err + } + return string(utf16.Decode(units)), nil +} + +// parseSST разбирает общую таблицу строк, начиная с записи SST (i) и её CONTINUE. +func parseSST(recs []record, i int) ([]string, error) { + data := recs[i].data + if len(data) < 8 { + return nil, fmt.Errorf("слишком короткая таблица строк") + } + unique := int(binary.LittleEndian.Uint32(data[4:])) + if unique < 0 || unique > maxSSTItems { + return nil, fmt.Errorf("неверный размер таблицы строк") + } + chunks := [][]byte{data[8:]} + for j := i + 1; j < len(recs) && recs[j].id == recContinue; j++ { + chunks = append(chunks, recs[j].data) + } + r := &chunkReader{chunks: chunks} + out := make([]string, 0, unique) + for k := 0; k < unique; k++ { + s, err := r.readString() + if err != nil { + return nil, fmt.Errorf("строка %d из %d: %w", k+1, unique, err) + } + out = append(out, s) + } + return out, nil +} + +func decodeRK(v uint32) float64 { + var f float64 + if v&2 != 0 { + f = float64(int32(v) >> 2) + } else { + f = math.Float64frombits(uint64(v&0xFFFFFFFC) << 32) + } + if v&1 != 0 { + f /= 100 + } + return f +} + +func numberString(f float64) string { + if f == math.Trunc(f) && math.Abs(f) < 1e15 { + return strconv.FormatInt(int64(f), 10) + } + return strconv.FormatFloat(f, 'f', -1, 64) +} + +type boundSheet struct { + name string + offset int + kind byte +} + +func parseBoundSheet(d []byte) (boundSheet, error) { + if len(d) < 8 { + return boundSheet{}, fmt.Errorf("повреждённая запись листа") + } + cch, flags := int(d[6]), d[7] + width := 1 + if flags&1 != 0 { + width = 2 + } + if len(d) < 8+cch*width { + return boundSheet{}, fmt.Errorf("повреждённое имя листа") + } + units := make([]uint16, cch) + for i := range units { + if width == 2 { + units[i] = binary.LittleEndian.Uint16(d[8+i*2:]) + } else { + units[i] = uint16(d[8+i]) + } + } + return boundSheet{name: string(utf16.Decode(units)), offset: int(binary.LittleEndian.Uint32(d)), kind: d[5]}, nil +} + +// parseBIFF8 читает все листы книги из потока Workbook. +func parseBIFF8(stream []byte) ([]biffSheet, error) { + recs, err := readRecords(stream) + if err != nil { + return nil, err + } + if len(recs) == 0 || recs[0].id != recBOF || len(recs[0].data) < 4 || + binary.LittleEndian.Uint16(recs[0].data) != biff8Version { + return nil, fmt.Errorf("поддерживается только формат Excel 97–2003 (BIFF8)") + } + byOffset := make(map[int]int, len(recs)) + for i, rec := range recs { + byOffset[rec.off] = i + } + var sst []string + var bounds []boundSheet + for i, rec := range recs { + if i > 0 && rec.id == recBOF { + break // началась первая страница: общая часть книги закончилась + } + switch rec.id { + case recSST: + if sst, err = parseSST(recs, i); err != nil { + return nil, err + } + case recBoundSheet: + b, err := parseBoundSheet(rec.data) + if err != nil { + return nil, err + } + bounds = append(bounds, b) + } + } + sheets := make([]biffSheet, 0, len(bounds)) + for _, b := range bounds { + start, ok := byOffset[b.offset] + if !ok || recs[start].id != recBOF { + return nil, fmt.Errorf("лист %q: не найдено начало данных", b.name) + } + rows, err := readSheet(recs, start, sst) + if err != nil { + return nil, fmt.Errorf("лист %q: %w", b.name, err) + } + sheets = append(sheets, biffSheet{name: b.name, rows: rows}) + } + return sheets, nil +} + +type cellValue struct { + row, col int + text string +} + +func readSheet(recs []record, start int, sst []string) ([][]string, error) { + var cells []cellValue + maxRow, maxCol := -1, -1 + put := func(row, col int, text string) error { + if row >= maxSheetRows || col >= maxSheetCols { + return fmt.Errorf("лист слишком велик (строка %d, столбец %d)", row+1, col+1) + } + cells = append(cells, cellValue{row, col, text}) + maxRow, maxCol = max(maxRow, row), max(maxCol, col) + return nil + } + depth := 0 + for i := start; i < len(recs); i++ { + rec := recs[i] + d := rec.data + switch rec.id { + case recBOF: + depth++ + continue + case recEOF: + depth-- + if depth == 0 { + i = len(recs) + } + continue + } + if depth != 1 { + continue + } + switch rec.id { + case recLabelSST: + if len(d) < 10 { + return nil, fmt.Errorf("повреждённая ячейка LABELSST") + } + idx := int(binary.LittleEndian.Uint32(d[6:])) + if idx < 0 || idx >= len(sst) { + return nil, fmt.Errorf("ячейка ссылается на несуществующую строку %d", idx) + } + if err := put(int(binary.LittleEndian.Uint16(d)), int(binary.LittleEndian.Uint16(d[2:])), sst[idx]); err != nil { + return nil, err + } + case recLabel: + if len(d) < 8 { + return nil, fmt.Errorf("повреждённая ячейка LABEL") + } + r := &chunkReader{chunks: [][]byte{d[6:]}} + s, err := r.readString() + if err != nil { + return nil, err + } + if err := put(int(binary.LittleEndian.Uint16(d)), int(binary.LittleEndian.Uint16(d[2:])), s); err != nil { + return nil, err + } + case recNumber: + if len(d) < 14 { + return nil, fmt.Errorf("повреждённая ячейка NUMBER") + } + f := math.Float64frombits(binary.LittleEndian.Uint64(d[6:])) + if err := put(int(binary.LittleEndian.Uint16(d)), int(binary.LittleEndian.Uint16(d[2:])), numberString(f)); err != nil { + return nil, err + } + case recRK: + if len(d) < 10 { + return nil, fmt.Errorf("повреждённая ячейка RK") + } + if err := put(int(binary.LittleEndian.Uint16(d)), int(binary.LittleEndian.Uint16(d[2:])), + numberString(decodeRK(binary.LittleEndian.Uint32(d[6:])))); err != nil { + return nil, err + } + case recMulRK: + if len(d) < 6 { + return nil, fmt.Errorf("повреждённая запись MULRK") + } + row, first := int(binary.LittleEndian.Uint16(d)), int(binary.LittleEndian.Uint16(d[2:])) + n := (len(d) - 6) / 6 + for k := 0; k < n; k++ { + v := binary.LittleEndian.Uint32(d[4+k*6+2:]) + if err := put(row, first+k, numberString(decodeRK(v))); err != nil { + return nil, err + } + } + case recBlank, recMulBlank, recString: + // пустые ячейки значения не несут + case recFormula, recBoolErr, recRString: + return nil, fmt.Errorf("неподдерживаемый тип ячейки 0x%04X: выгрузка не должна содержать формул и ошибок", rec.id) + } + } + rows := make([][]string, maxRow+1) + width := make([]int, maxRow+1) + for _, c := range cells { + width[c.row] = max(width[c.row], c.col+1) + } + for r := range rows { + if width[r] > 0 { + rows[r] = make([]string, width[r]) + } + } + for _, c := range cells { + rows[c.row][c.col] = c.text + } + return rows, nil +} diff --git a/internal/sberhealth/biff_test.go b/internal/sberhealth/biff_test.go new file mode 100644 index 0000000..942c7f5 --- /dev/null +++ b/internal/sberhealth/biff_test.go @@ -0,0 +1,290 @@ +package sberhealth + +import ( + "encoding/binary" + "fmt" + "math" + "math/rand" + "reflect" + "strings" + "testing" + "unicode/utf16" +) + +// ---- сборка синтетической книги BIFF8: без персональных данных ---- + +func biffRec(id uint16, data []byte) []byte { + out := make([]byte, 4, 4+len(data)) + binary.LittleEndian.PutUint16(out, id) + binary.LittleEndian.PutUint16(out[2:], uint16(len(data))) + return append(out, data...) +} + +func le16(v int) []byte { b := make([]byte, 2); binary.LittleEndian.PutUint16(b, uint16(v)); return b } +func le32(v uint32) []byte { b := make([]byte, 4); binary.LittleEndian.PutUint32(b, v); return b } + +func latin1(s string) bool { + for _, r := range s { + if r > 0xFF { + return false + } + } + return true +} + +type testString struct { + text string + runs int // блоки форматирования (по 4 байта) + ext int // байты фонетики +} + +// sstRecords раскладывает строки по записям SST и CONTINUE так же, как это делает Excel: +// строка может прерваться на любой границе, а у продолжения первым байтом идёт флаг кодировки; +// на каждой границе кодировка может поменяться. +func sstRecords(strs []testString, maxData int) []byte { + var chunks [][]byte + cur := append(le32(uint32(len(strs))), le32(uint32(len(strs)))...) + flush := func() { chunks = append(chunks, cur); cur = nil } + space := func() int { return maxData - len(cur) } + for _, s := range strs { + units := utf16.Encode([]rune(s.text)) + headLen := 3 + if s.runs > 0 { + headLen += 2 + } + if s.ext > 0 { + headLen += 4 + } + if space() < headLen+1 { // заголовок не делится между записями + flush() + } + flags := byte(0) + if !latin1(s.text) { + flags |= 1 + } + if s.runs > 0 { + flags |= 0x08 + } + if s.ext > 0 { + flags |= 0x04 + } + cur = append(cur, le16(len(units))...) + cur = append(cur, flags) + if s.runs > 0 { + cur = append(cur, le16(s.runs)...) + } + if s.ext > 0 { + cur = append(cur, le32(uint32(s.ext))...) + } + high := flags&1 != 0 + for done := 0; done < len(units); { + width := 1 + if high { + width = 2 + } + n := min(len(units)-done, space()/width) + if n == 0 { + flush() + rest := units[done:] + high = false + for _, u := range rest { + if u > 0xFF { + high = true + } + } + flag := byte(0) + if high { + flag = 1 + } + cur = append(cur, flag) + continue + } + for _, u := range units[done : done+n] { + if high { + cur = append(cur, byte(u), byte(u>>8)) + } else { + cur = append(cur, byte(u)) + } + } + done += n + } + for left := s.runs*4 + s.ext; left > 0; { + if space() == 0 { + flush() + } + n := min(left, space()) + cur = append(cur, make([]byte, n)...) + left -= n + } + } + flush() + var out []byte + for i, c := range chunks { + id := uint16(recContinue) + if i == 0 { + id = recSST + } + out = append(out, biffRec(id, c)...) + } + return out +} + +func boundSheetRec(name string, offset int) []byte { + d := append(le32(uint32(offset)), 0, 0, byte(len(name)), 0) + return biffRec(recBoundSheet, append(d, []byte(name)...)) +} + +func bofRec(kind int) []byte { + return biffRec(recBOF, append(append(le16(biff8Version), le16(kind)...), make([]byte, 12)...)) +} + +func labelSSTCell(row, col, idx int) []byte { + return biffRec(recLabelSST, append(append(append(le16(row), le16(col)...), le16(0)...), le32(uint32(idx))...)) +} + +func numberCell(row, col int, f float64) []byte { + d := append(append(le16(row), le16(col)...), le16(0)...) + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, math.Float64bits(f)) + return biffRec(recNumber, append(d, b...)) +} + +func rkCell(row, col int, rk uint32) []byte { + return biffRec(recRK, append(append(append(le16(row), le16(col)...), le16(0)...), le32(rk)...)) +} + +func mulRKCells(row, first int, rks ...uint32) []byte { + d := append(le16(row), le16(first)...) + for _, rk := range rks { + d = append(d, le16(0)...) + d = append(d, le32(rk)...) + } + d = append(d, le16(first+len(rks)-1)...) + return biffRec(recMulRK, d) +} + +// buildBook собирает книгу: общая часть (SST) и один лист с готовыми записями ячеек. +func buildBook(strs []testString, maxData int, name string, cells ...[]byte) []byte { + sst := sstRecords(strs, maxData) + globalsLen := len(bofRec(5)) + len(boundSheetRec(name, 0)) + len(sst) + len(biffRec(recEOF, nil)) + var out []byte + out = append(out, bofRec(5)...) + out = append(out, boundSheetRec(name, globalsLen)...) + out = append(out, sst...) + out = append(out, biffRec(recEOF, nil)...) + out = append(out, bofRec(0x10)...) + for _, c := range cells { + out = append(out, c...) + } + out = append(out, biffRec(recEOF, nil)...) + return out +} + +// Главный регрессионный тест: строки через границы CONTINUE при любых размерах записей. +func TestSSTStringsAcrossContinueBoundaries(t *testing.T) { + rng := rand.New(rand.NewSource(7)) + words := []string{"Врач", "Невролог", "Лабораторная диагностика", "ул. Ляхова, д. 7", "Report", "Дата создания записи", "Привет, мир", "Qwerty"} + var strs []testString + var want []string + for i := 0; i < 220; i++ { + var parts []string + for k := rng.Intn(4); k >= 0; k-- { + parts = append(parts, words[rng.Intn(len(words))]) + } + s := strings.Join(parts, " ") + if i%7 == 0 { + s = fmt.Sprintf("%02d.%02d.2026 %02d:%02d", i%28+1, i%12+1, i%24, i%60) // ASCII: сжатая форма + } + ts := testString{text: s} + if i%11 == 0 { + ts.runs = 2 + } + if i%13 == 0 { + ts.ext = 9 + } + strs = append(strs, ts) + want = append(want, s) + } + for _, maxData := range []int{24, 25, 31, 40, 63, 64, 97, 128, 255, 1000, 8224} { + cells := make([][]byte, len(strs)) + for i := range strs { + cells[i] = labelSSTCell(i, 0, i) + } + sheets, err := parseBIFF8(buildBook(strs, maxData, "Report", cells...)) + if err != nil { + t.Fatalf("maxData=%d: %v", maxData, err) + } + if len(sheets) != 1 || sheets[0].name != "Report" || len(sheets[0].rows) != len(want) { + t.Fatalf("maxData=%d: неверный состав листов", maxData) + } + for i, w := range want { + if got := sheets[0].rows[i][0]; got != w { + t.Fatalf("maxData=%d, строка %d: получили %q, ожидали %q", maxData, i, got, w) + } + } + } +} + +func TestNumbersAndShape(t *testing.T) { + strs := []testString{{text: "a"}, {text: "b"}} + book := buildBook(strs, 100, "Report", + labelSSTCell(0, 1, 1), // ряд 0 начинается с пустого столбца 0 + numberCell(1, 0, 59935608), + numberCell(1, 1, 2500.5), + rkCell(2, 0, uint32(7<<2)|2), // целое 7 + rkCell(2, 1, uint32(12345<<2)|2|1), // 123,45 + mulRKCells(3, 2, uint32(10<<2)|2, uint32(20<<2)|2), + labelSSTCell(4, 0, 0), + ) + sheets, err := parseBIFF8(book) + if err != nil { + t.Fatal(err) + } + want := [][]string{{"", "b"}, {"59935608", "2500.5"}, {"7", "123.45"}, {"", "", "10", "20"}, {"a"}} + if !reflect.DeepEqual(sheets[0].rows, want) { + t.Fatalf("получили %q, ожидали %q", sheets[0].rows, want) + } +} + +func TestUnsupportedAndBrokenInput(t *testing.T) { + strs := []testString{{text: "x"}} + formula := biffRec(recFormula, make([]byte, 20)) + if _, err := parseBIFF8(buildBook(strs, 100, "Report", labelSSTCell(0, 0, 0), formula)); err == nil { + t.Error("формула должна давать явную ошибку, а не пропускаться") + } + if _, err := parseBIFF8(buildBook(strs, 100, "Report", labelSSTCell(0, 0, 5))); err == nil { + t.Error("ссылка на несуществующую строку должна давать ошибку") + } + good := buildBook(strs, 100, "Report", labelSSTCell(0, 0, 0)) + for cut := 1; cut < len(good); cut += 7 { + func() { + defer func() { + if recover() != nil { + t.Fatalf("обрезанная книга (%d байт) вызвала панику", cut) + } + }() + _, _ = parseBIFF8(good[:cut]) + }() + } + bad := append([]byte(nil), good...) + binary.LittleEndian.PutUint16(bad[4:], 0x0500) // BIFF5 + if _, err := parseBIFF8(bad); err == nil { + t.Error("BIFF5 должен отклоняться") + } + huge := buildBook(strs, 100, "Report", labelSSTCell(maxSheetRows, 0, 0)) + if _, err := parseBIFF8(huge); err == nil { + t.Error("слишком большой лист должен отклоняться") + } +} + +func TestReadXLSRejectsNonExcel(t *testing.T) { + for name, data := range map[string][]byte{ + "html": []byte("
x
" + strings.Repeat(" ", 600)), + "пусто": nil, + "zip": append([]byte{'P', 'K', 3, 4}, make([]byte, 600)...), + } { + if _, err := ReadXLS(data); err == nil { + t.Errorf("%s: ожидали ошибку", name) + } + } +} diff --git a/internal/sberhealth/client.go b/internal/sberhealth/client.go new file mode 100644 index 0000000..b59bc78 --- /dev/null +++ b/internal/sberhealth/client.go @@ -0,0 +1,135 @@ +// Package sberhealth downloads the authenticated docdoc.ru clinic export. +// A cabinet session is required; this is not a public API-key integration. +// Downloaded bytes MUST be schema-validated before use in channel reports. +package sberhealth + +import ( + "bytes" + "context" + "fmt" + "io" + "mime" + "net/http" + "net/url" + "regexp" + "strings" + "time" +) + +const downloadURL = "https://docdoc.ru/lk/requests/listOld" +const maxDownload = 10 << 20 + +type Client struct { + cookie, clinicID string + endpoint string + http *http.Client +} + +// Export is only a download, NOT a parsed/validated report. The XLS export may +// be binary Excel, OOXML, or an HTML table; do not guess its columns/dates. +type Export struct { + Data []byte + Format string // xls, xlsx, html; format detection does not establish schema +} + +func New(cookie, clinicID string) (*Client, error) { + clinicID = strings.TrimSpace(clinicID) + if cookie == "" && clinicID == "" { + return nil, nil + } + if strings.TrimSpace(cookie) == "" || strings.ContainsAny(cookie, "\r\n") || + len(cookie) > 16384 || !strings.Contains(cookie, "=") || + !regexp.MustCompile(`^[1-9][0-9]{0,17}$`).MatchString(clinicID) { + return nil, fmt.Errorf("СберЗдоровье: требуется сессия кабинета и корректный ID клиники") + } + return &Client{cookie: cookie, clinicID: clinicID, endpoint: downloadURL, http: &http.Client{ + Timeout: 45 * time.Second, + // A redirect to login is an error; never forward credentials elsewhere. + CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, + }}, nil +} + +func (c *Client) Configured() bool { return c != nil && c.cookie != "" && c.clinicID != "" } + +// Download reproduces the supplied export URL's date filters, without assuming +// crDateTill is inclusive/exclusive or that page=1 includes every request. +// These provider semantics must be checked against an actual cabinet export +// before adapting a 1C period [from,to) to these filters. +// periodInput is only week/month, and both date arguments are date-only Moscow. +func (c *Client) Download(ctx context.Context, dateFrom, dateTill time.Time, periodInput string) (Export, error) { + if !c.Configured() { + return Export{}, fmt.Errorf("СберЗдоровье не подключено") + } + if periodInput != "week" && periodInput != "month" { + return Export{}, fmt.Errorf("СберЗдоровье: режим выгрузки — week или month") + } + moscow := time.FixedZone("Europe/Moscow", 3*60*60) + dateFrom, dateTill = dateFrom.In(moscow), dateTill.In(moscow) + for _, date := range []time.Time{dateFrom, dateTill} { + if date.Year() < 2000 || date.Year() > 2100 || date.Hour() != 0 || date.Minute() != 0 || date.Second() != 0 || date.Nanosecond() != 0 { + return Export{}, fmt.Errorf("СберЗдоровье: фильтры должны быть датами без времени (Москва)") + } + } + if dateTill.Before(dateFrom) || dateTill.Sub(dateFrom) > 366*24*time.Hour { + return Export{}, fmt.Errorf("СберЗдоровье: некорректный период выгрузки") + } + u, err := url.Parse(c.endpoint) + if err != nil { + return Export{}, fmt.Errorf("СберЗдоровье: некорректный адрес выгрузки") + } + u.RawQuery = url.Values{ + "kind": {"doctor"}, "clinic_id": {c.clinicID}, "periodInput": {periodInput}, + "crDateFrom": {dateFrom.Format("02.01.2006")}, "crDateTill": {dateTill.Format("02.01.2006")}, + "page": {"1"}, "admissionStatus": {"-1"}, "sourceType": {"-1"}, "type": {"xls"}, + }.Encode() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) + if err != nil { + return Export{}, fmt.Errorf("СберЗдоровье: не удалось подготовить запрос") + } + req.Header.Set("Cookie", c.cookie) + req.Header.Set("Accept", "application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + res, err := c.http.Do(req) + if err != nil { + if ctx.Err() != nil { + return Export{}, ctx.Err() + } + return Export{}, fmt.Errorf("СберЗдоровье: не удалось получить выгрузку (соединение или таймаут)") + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + if res.StatusCode == 401 || res.StatusCode == 403 || res.StatusCode >= 300 && res.StatusCode < 400 { + return Export{}, fmt.Errorf("СберЗдоровье: доступ к выгрузке не получен (HTTP %d); проверьте сессию кабинета и защиту сайта", res.StatusCode) + } + return Export{}, fmt.Errorf("СберЗдоровье: ошибка выгрузки (HTTP %d)", res.StatusCode) + } + data, err := io.ReadAll(io.LimitReader(res.Body, maxDownload+1)) + if err != nil { + return Export{}, fmt.Errorf("СберЗдоровье: файл не дочитался") + } + if len(data) > maxDownload { + return Export{}, fmt.Errorf("СберЗдоровье: файл больше 10 МБ") + } + format := "" + switch { + case bytes.HasPrefix(data, []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}): + format = "xls" + case bytes.HasPrefix(data, []byte{'P', 'K', 3, 4}): + format = "xlsx" + default: + // Legacy exports sometimes contain HTML under an .xls filename. Only + // recognise it when explicitly offered as an Excel download, not an + // arbitrary HTML page with a login/error table. Still needs a parser. + disposition, params, _ := mime.ParseMediaType(res.Header.Get("Content-Disposition")) + ct, _, _ := mime.ParseMediaType(res.Header.Get("Content-Type")) + filename := strings.ToLower(params["filename"]) + excelAttachment := disposition == "attachment" && strings.HasSuffix(filename, ".xls") + if (excelAttachment || ct == "application/vnd.ms-excel") && bytes.Contains(bytes.ToLower(data), []byte("
synthetic
", "text/html", `attachment; filename="requests.xls"`, "html"}, + {"html-excel-mime", "
synthetic
", "application/vnd.ms-excel", "", "html"}, + {"login-table", "private-secret Login
", "text/html", "", ""}, + {"login-attachment", "private-secret Login", "text/html", `attachment; filename="requests.xls"`, ""}, + {"json-error", `{"error":"private-secret"}`, "application/json", "", ""}, + } { + t.Run(tc.name, func(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", tc.ct) + w.Header().Set("Content-Disposition", tc.disposition) + _, _ = io.WriteString(w, tc.body) + })) + defer s.Close() + c, _ := New("session=synthetic", "294888") + c.endpoint = s.URL + export, err := c.Download(context.Background(), from, till, "week") + if tc.format == "" { + if err == nil || strings.Contains(err.Error(), "private-secret") { + t.Fatal("unknown/login response accepted or leaked") + } + } else if err != nil || export.Format != tc.format { + t.Fatal("format detection incorrect", err) + } + }) + } +} + +func TestErrorsAndRedirects(t *testing.T) { + from, till := sampleDates() + redirected := 0 + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { redirected++ })) + defer target.Close() + for _, status := range []int{302, 401, 403, 429, 500} { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Location", target.URL) + w.WriteHeader(status) + _, _ = io.WriteString(w, "private-secret") + })) + c, _ := New("session=synthetic", "294888") + c.endpoint = s.URL + export, err := c.Download(context.Background(), from, till, "week") + s.Close() + if err == nil || len(export.Data) != 0 || strings.Contains(err.Error(), "private-secret") { + t.Fatal("missing or leaking error") + } + } + if redirected != 0 { + t.Fatal("redirect forwarded session") + } +} + +func TestMonthlyFiltersAndInvalidDates(t *testing.T) { + zone := time.FixedZone("Europe/Moscow", 10800) + from, till := time.Date(2026, 10, 1, 0, 0, 0, 0, zone), time.Date(2026, 10, 31, 0, 0, 0, 0, zone) + requests := 0 + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests++ + q := r.URL.Query() + if q.Get("periodInput") != "month" || q.Get("crDateFrom") != "01.10.2026" || q.Get("crDateTill") != "31.10.2026" { + t.Error("monthly date filters incorrect") + } + _, _ = w.Write([]byte("PK\x03\x04placeholder")) + })) + defer s.Close() + c, _ := New("session=synthetic", "294888") + c.endpoint = s.URL + if _, err := c.Download(context.Background(), from, till, "month"); err != nil { + t.Fatal(err) + } + for _, args := range []struct { + from, till time.Time + mode string + }{{till, from, "week"}, {from.Add(time.Hour), till, "week"}, {from, till, "auto"}, {from, from.AddDate(2, 0, 0), "month"}} { + if _, err := c.Download(context.Background(), args.from, args.till, args.mode); err == nil { + t.Fatal("invalid filters accepted") + } + } + if requests != 1 { + t.Fatal("invalid filters reached server") + } +} + +func TestSizeLimitAndCancellation(t *testing.T) { + from, till := sampleDates() + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(append([]byte("PK\x03\x04"), bytes.Repeat([]byte{'x'}, maxDownload)...)) + })) + defer s.Close() + c, _ := New("session=synthetic", "294888") + c.endpoint = s.URL + if _, err := c.Download(context.Background(), from, till, "week"); err == nil { + t.Fatal("oversized download accepted") + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + if _, err := c.Download(ctx, from, till, "week"); err != context.Canceled { + t.Fatal("cancellation lost", err) + } +} diff --git a/internal/sberhealth/xls.go b/internal/sberhealth/xls.go new file mode 100644 index 0000000..eabb158 --- /dev/null +++ b/internal/sberhealth/xls.go @@ -0,0 +1,34 @@ +package sberhealth + +import ( + "bytes" + "fmt" +) + +// ReadXLS reads only the confirmed legacy Report sheet. Unexpected formats or +// sparse/oversized files fail explicitly, rather than becoming an empty channel. +// Parsing is done by biff.go (own BIFF8 reader): the third-party library used before +// shifted values in large exports because it mishandled strings split across CONTINUE +// records. No Excel/Python process is needed on the VPS. +func ReadXLS(data []byte) (rows [][]string, err error) { + defer func() { + if recover() != nil { + rows, err = nil, fmt.Errorf("СберЗдоровье: повреждённая книга XLS") + } + }() + if len(data) > maxDownload || len(data) < 512 || !bytes.HasPrefix(data, []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}) { + return nil, fmt.Errorf("СберЗдоровье: нужен бинарный XLS из кабинета") + } + stream, err := workbookStream(data) + if err != nil { + return nil, fmt.Errorf("СберЗдоровье: не удалось прочитать XLS (%v)", err) + } + sheets, err := parseBIFF8(stream) + if err != nil { + return nil, fmt.Errorf("СберЗдоровье: не удалось прочитать XLS (%v)", err) + } + if len(sheets) != 1 || sheets[0].name != "Report" { + return nil, fmt.Errorf("СберЗдоровье: неизвестный лист или слишком много листов XLS") + } + return sheets[0].rows, nil +} diff --git a/internal/storage/admin.go b/internal/storage/admin.go new file mode 100644 index 0000000..ec2751e --- /dev/null +++ b/internal/storage/admin.go @@ -0,0 +1,120 @@ +package storage + +import ( + "context" + "crypto/rand" + "encoding/base64" + "errors" + "time" + + "github.com/jackc/pgx/v5" + "golang.org/x/crypto/bcrypt" +) + +var ErrBadCredentials = errors.New("bad credentials") + +const sessionTTL = 12 * time.Hour + +type AdminUser struct { + ID int64 `json:"id"` + Email string `json:"email"` + Name string `json:"name"` + Role string `json:"role"` +} + +// EnsureAdmin создаёт первого администратора, если таблица пуста. +func (s *Storage) EnsureAdmin(ctx context.Context, email, password string) error { + if email == "" || password == "" { + return nil + } + var n int + if err := s.pool.QueryRow(ctx, `SELECT count(*) FROM admin_users`).Scan(&n); err != nil { + return err + } + if n > 0 { + return nil + } + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + _, err = s.pool.Exec(ctx, + `INSERT INTO admin_users (email, password_hash, name, role) VALUES ($1,$2,'Администратор','owner')`, + email, string(hash)) + return err +} + +// Login проверяет пароль и заводит сессию, возвращая её токен. +func (s *Storage) Login(ctx context.Context, email, password, ip, ua string) (string, *AdminUser, error) { + var ( + u AdminUser + hash string + ) + err := s.pool.QueryRow(ctx, + `SELECT id, email, name, role, password_hash FROM admin_users WHERE email = $1 AND active`, + email).Scan(&u.ID, &u.Email, &u.Name, &u.Role, &hash) + if errors.Is(err, pgx.ErrNoRows) { + return "", nil, ErrBadCredentials + } + if err != nil { + return "", nil, err + } + if bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) != nil { + return "", nil, ErrBadCredentials + } + + raw := make([]byte, 32) + if _, err := rand.Read(raw); err != nil { + return "", nil, err + } + token := base64.RawURLEncoding.EncodeToString(raw) + + var ipVal any + if ip != "" { + ipVal = ip + } + if _, err := s.pool.Exec(ctx, + `INSERT INTO admin_sessions (token, user_id, expires_at, ip, user_agent) VALUES ($1,$2,$3,$4,$5)`, + token, u.ID, time.Now().Add(sessionTTL), ipVal, ua); err != nil { + return "", nil, err + } + _, _ = s.pool.Exec(ctx, `UPDATE admin_users SET last_login_at = now() WHERE id = $1`, u.ID) + s.Audit(ctx, u.ID, "login", "admin_user", u.ID, ip) + return token, &u, nil +} + +func (s *Storage) Logout(ctx context.Context, token string) error { + _, err := s.pool.Exec(ctx, `DELETE FROM admin_sessions WHERE token = $1`, token) + return err +} + +// UserByToken возвращает пользователя действующей сессии. +func (s *Storage) UserByToken(ctx context.Context, token string) (*AdminUser, error) { + var u AdminUser + err := s.pool.QueryRow(ctx, ` + SELECT u.id, u.email, u.name, u.role + FROM admin_sessions s JOIN admin_users u ON u.id = s.user_id + WHERE s.token = $1 AND s.expires_at > now() AND u.active`, token). + Scan(&u.ID, &u.Email, &u.Name, &u.Role) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrNotFound + } + if err != nil { + return nil, err + } + return &u, nil +} + +// Audit пишет действие в журнал; ошибки журнала не должны ломать запрос. +func (s *Storage) Audit(ctx context.Context, userID int64, action, entity string, entityID int64, ip string) { + var ipVal, idVal any + if ip != "" { + ipVal = ip + } + if entityID != 0 { + idVal = entityID + } + _, _ = s.pool.Exec(ctx, + `INSERT INTO audit_log (user_id, action, entity, entity_id, ip) VALUES ($1,$2,$3,$4,$5)`, + userID, action, entity, idVal, ipVal) +} diff --git a/internal/storage/admin_checkups.go b/internal/storage/admin_checkups.go new file mode 100644 index 0000000..7bec937 --- /dev/null +++ b/internal/storage/admin_checkups.go @@ -0,0 +1,159 @@ +package storage + +import ( + "context" + "encoding/json" + + "github.com/jackc/pgx/v5" +) + +type AdminCheckup struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Audience string `json:"audience"` + Name string `json:"name"` + Lead string `json:"lead"` + Blocks json.RawMessage `json:"blocks"` + Price *float64 `json:"price"` + OldPrice *float64 `json:"old_price"` + ImageID *int64 `json:"image_id"` + Image string `json:"image,omitempty"` + Items []string `json:"items"` + Sort int `json:"sort"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +// AdminCheckups — все программы со составом. Blocks отдаём, чтобы админка могла +// предложить состав из старого описания, пока его не сохранили списком. +func (s *Storage) AdminCheckups(ctx context.Context) ([]AdminCheckup, error) { + rows, err := s.pool.Query(ctx, ` + SELECT c.id, c.slug, c.audience, c.name, c.lead, c.blocks, c.price, c.old_price, + c.image_id, COALESCE(m.path, ''), c.sort, c.published, + c.seo_title, c.seo_description, c.seo_h1 + FROM checkups c + LEFT JOIN media m ON m.id = c.image_id + WHERE c.deleted_at IS NULL + ORDER BY c.audience, c.sort, c.name`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminCheckup + index := map[int64]int{} + for rows.Next() { + var a AdminCheckup + if err := rows.Scan(&a.ID, &a.Slug, &a.Audience, &a.Name, &a.Lead, &a.Blocks, + &a.Price, &a.OldPrice, &a.ImageID, &a.Image, &a.Sort, &a.Published, + &a.SEOTitle, &a.SEODesc, &a.SEOH1); err != nil { + return nil, err + } + a.Items = []string{} + index[a.ID] = len(out) + out = append(out, a) + } + if err := rows.Err(); err != nil { + return nil, err + } + + itemRows, err := s.pool.Query(ctx, `SELECT checkup_id, name FROM checkup_items ORDER BY checkup_id, sort, id`) + if err != nil { + return nil, err + } + defer itemRows.Close() + for itemRows.Next() { + var checkupID int64 + var name string + if err := itemRows.Scan(&checkupID, &name); err != nil { + return nil, err + } + if i, found := index[checkupID]; found { + out[i].Items = append(out[i].Items, name) + } + } + return out, itemRows.Err() +} + +type CheckupInput struct { + Slug string + Audience string + Name string + Lead string + Price *float64 + OldPrice *float64 + ImageID *int64 + Items []string + Sort int + Published bool + SEOTitle string + SEODesc string + SEOH1 string +} + +func (s *Storage) AdminCreateCheckup(ctx context.Context, in CheckupInput) (int64, error) { + tx, err := s.pool.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + + var id int64 + err = tx.QueryRow(ctx, ` + INSERT INTO checkups (slug, audience, name, lead, price, old_price, image_id, sort, published, + seo_title, seo_description, seo_h1) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12) + RETURNING id`, + in.Slug, in.Audience, in.Name, in.Lead, in.Price, in.OldPrice, in.ImageID, in.Sort, in.Published, + in.SEOTitle, in.SEODesc, in.SEOH1).Scan(&id) + if err != nil { + return 0, wrapConflict(err) + } + if err := replaceCheckupItems(ctx, tx, id, in.Items); err != nil { + return 0, err + } + return id, tx.Commit(ctx) +} + +func (s *Storage) AdminUpdateCheckup(ctx context.Context, id int64, in CheckupInput) error { + tx, err := s.pool.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + + tag, err := tx.Exec(ctx, ` + UPDATE checkups SET slug=$2, audience=$3, name=$4, lead=$5, price=$6, old_price=$7, + image_id=$8, sort=$9, published=$10, + seo_title=$11, seo_description=$12, seo_h1=$13, updated_at=now() + WHERE id=$1 AND deleted_at IS NULL`, + id, in.Slug, in.Audience, in.Name, in.Lead, in.Price, in.OldPrice, + in.ImageID, in.Sort, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + if err := replaceCheckupItems(ctx, tx, id, in.Items); err != nil { + return err + } + return tx.Commit(ctx) +} + +// replaceCheckupItems перезаписывает состав целиком: порядок строк в админке — порядок на сайте. +func replaceCheckupItems(ctx context.Context, tx pgx.Tx, checkupID int64, items []string) error { + if _, err := tx.Exec(ctx, `DELETE FROM checkup_items WHERE checkup_id = $1`, checkupID); err != nil { + return err + } + for i, name := range items { + if _, err := tx.Exec(ctx, + `INSERT INTO checkup_items (checkup_id, name, sort) VALUES ($1, $2, $3)`, + checkupID, name, (i+1)*10); err != nil { + return err + } + } + return nil +} diff --git a/internal/storage/admin_content.go b/internal/storage/admin_content.go new file mode 100644 index 0000000..ed8d24e --- /dev/null +++ b/internal/storage/admin_content.go @@ -0,0 +1,221 @@ +package storage + +import ( + "context" + "errors" + + "github.com/jackc/pgx/v5" +) + +// ---------- Услуги ---------- + +type AdminService struct { + ID int64 `json:"id"` + ParentID *int64 `json:"parent_id"` + ParentName string `json:"parent_name,omitempty"` + Slug string `json:"slug"` + Path string `json:"path"` + Name string `json:"name"` + ShortName string `json:"short_name"` + Lead string `json:"lead"` + Sort int `json:"sort"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` + PriceFrom *float64 `json:"price_from,omitempty"` + PriceCount int `json:"price_count"` +} + +// AdminServices возвращает плоский список услуг, включая скрытые. +func (s *Storage) AdminServices(ctx context.Context, q string) ([]AdminService, error) { + rows, err := s.pool.Query(ctx, ` + SELECT s.id, s.parent_id, COALESCE(p.name, ''), s.slug, s.path, s.name, + s.short_name, s.lead, s.sort, s.published, + s.seo_title, s.seo_description, s.seo_h1, + (SELECT min(pi.price) FROM price_items pi + WHERE pi.service_id = s.id AND pi.published AND NOT pi.exclude_from_min), + (SELECT count(*) FROM price_items pi WHERE pi.service_id = s.id) + FROM services s + LEFT JOIN services p ON p.id = s.parent_id + WHERE s.deleted_at IS NULL + AND ($1 = '' OR s.name ILIKE '%' || $1 || '%' OR s.path ILIKE '%' || $1 || '%') + ORDER BY COALESCE(p.sort, s.sort), COALESCE(p.name, s.name), s.parent_id NULLS FIRST, s.sort, s.name`, q) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminService + for rows.Next() { + var a AdminService + if err := rows.Scan(&a.ID, &a.ParentID, &a.ParentName, &a.Slug, &a.Path, &a.Name, + &a.ShortName, &a.Lead, &a.Sort, &a.Published, + &a.SEOTitle, &a.SEODesc, &a.SEOH1, &a.PriceFrom, &a.PriceCount); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type ServiceUpdate struct { + Name string `json:"name"` + ShortName string `json:"short_name"` + Lead string `json:"lead"` + Sort int `json:"sort"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +func (s *Storage) AdminUpdateService(ctx context.Context, id int64, u ServiceUpdate) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE services SET name=$2, short_name=$3, lead=$4, sort=$5, published=$6, + seo_title=$7, seo_description=$8, seo_h1=$9, updated_at=now() + WHERE id=$1 AND deleted_at IS NULL`, + id, u.Name, u.ShortName, u.Lead, u.Sort, u.Published, u.SEOTitle, u.SEODesc, u.SEOH1) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// ---------- Прайс ---------- + +type AdminPriceSection struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + Count int `json:"count"` +} + +func (s *Storage) AdminPriceSections(ctx context.Context) ([]AdminPriceSection, error) { + rows, err := s.pool.Query(ctx, ` + SELECT ps.id, ps.slug, ps.name, (SELECT count(*) FROM price_items pi WHERE pi.section_id = ps.id) + FROM price_sections ps + ORDER BY ps.sort, ps.name`) + if err != nil { + return nil, err + } + defer rows.Close() + var out []AdminPriceSection + for rows.Next() { + var a AdminPriceSection + if err := rows.Scan(&a.ID, &a.Slug, &a.Name, &a.Count); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type AdminPriceItem struct { + ID int64 `json:"id"` + SectionID int64 `json:"section_id"` + SectionName string `json:"section_name"` + ServiceID *int64 `json:"service_id"` + ServiceName string `json:"service_name,omitempty"` + Name string `json:"name"` + Price float64 `json:"price"` + Unit string `json:"unit"` + PriceFrom bool `json:"price_from"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +// AdminPriceItems: постранично, с поиском и фильтром по разделу. +func (s *Storage) AdminPriceItems(ctx context.Context, sectionID int64, q string, limit, offset int) ([]AdminPriceItem, int, error) { + var total int + if err := s.pool.QueryRow(ctx, ` + SELECT count(*) FROM price_items pi + WHERE ($1 = 0 OR pi.section_id = $1) + AND ($2 = '' OR pi.name ILIKE '%' || $2 || '%')`, sectionID, q).Scan(&total); err != nil { + return nil, 0, err + } + + rows, err := s.pool.Query(ctx, ` + SELECT pi.id, pi.section_id, ps.name, pi.service_id, COALESCE(sv.name, ''), + pi.name, pi.price, pi.unit, pi.price_from, pi.sort, pi.published + FROM price_items pi + JOIN price_sections ps ON ps.id = pi.section_id + LEFT JOIN services sv ON sv.id = pi.service_id + WHERE ($1 = 0 OR pi.section_id = $1) + AND ($2 = '' OR pi.name ILIKE '%' || $2 || '%') + ORDER BY ps.sort, pi.sort, pi.name + LIMIT $3 OFFSET $4`, sectionID, q, limit, offset) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var out []AdminPriceItem + for rows.Next() { + var a AdminPriceItem + if err := rows.Scan(&a.ID, &a.SectionID, &a.SectionName, &a.ServiceID, &a.ServiceName, + &a.Name, &a.Price, &a.Unit, &a.PriceFrom, &a.Sort, &a.Published); err != nil { + return nil, 0, err + } + out = append(out, a) + } + return out, total, rows.Err() +} + +type PriceItemInput struct { + SectionID int64 `json:"section_id"` + ServiceID *int64 `json:"service_id"` + Name string `json:"name"` + Price float64 `json:"price"` + Unit string `json:"unit"` + PriceFrom bool `json:"price_from"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminUpdatePriceItem(ctx context.Context, id int64, in PriceItemInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE price_items SET section_id=$2, service_id=$3, name=$4, price=$5, + unit=$6, price_from=$7, sort=$8, published=$9, updated_at=now() + WHERE id=$1`, + id, in.SectionID, in.ServiceID, in.Name, in.Price, in.Unit, in.PriceFrom, in.Sort, in.Published) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminCreatePriceItem(ctx context.Context, in PriceItemInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO price_items (section_id, service_id, name, price, unit, price_from, sort, published) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id`, + in.SectionID, in.ServiceID, in.Name, in.Price, in.Unit, in.PriceFrom, in.Sort, in.Published).Scan(&id) + return id, err +} + +func (s *Storage) AdminDeletePriceItem(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM price_items WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) priceItemExists(ctx context.Context, id int64) error { + var x int + err := s.pool.QueryRow(ctx, `SELECT 1 FROM price_items WHERE id=$1`, id).Scan(&x) + if errors.Is(err, pgx.ErrNoRows) { + return ErrNotFound + } + return err +} diff --git a/internal/storage/admin_doctors.go b/internal/storage/admin_doctors.go new file mode 100644 index 0000000..dfebc52 --- /dev/null +++ b/internal/storage/admin_doctors.go @@ -0,0 +1,248 @@ +package storage + +import "context" + +type AdminDoctor struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + LastName string `json:"last_name"` + FirstName string `json:"first_name"` + MiddleName string `json:"middle_name"` + Position string `json:"position"` + Category string `json:"category"` + Degree string `json:"degree"` + PracticeYear *int `json:"practice_start_year"` + Rating *float64 `json:"rating"` + PhotoID *int64 `json:"photo_id"` + Photo string `json:"photo,omitempty"` + Quote string `json:"quote"` + Bio string `json:"bio"` + Specialties string `json:"specialties,omitempty"` // через запятую, для списка + // Основная специальность — раздел в адресе страницы: /company/staff/<раздел>//. + SpecialtyID *int64 `json:"specialty_id"` + SpecialtySlug string `json:"specialty_slug"` + Booking string `json:"booking_provider"` + MedflexUser string `json:"medflex_user"` + Sort int `json:"sort"` + Published bool `json:"published"` + Absent bool `json:"absent"` + ShowOnHome bool `json:"show_on_home"` + BioCount int `json:"bio_count"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` + // Связи для вкладки «Врачи» в админке: какие услуги ведёт и в каких филиалах принимает. + ServiceIDs []int64 `json:"service_ids"` + BranchIDs []int64 `json:"branch_ids"` +} + +func (s *Storage) AdminDoctors(ctx context.Context, q string) ([]AdminDoctor, error) { + rows, err := s.pool.Query(ctx, ` + SELECT d.id, d.slug, d.last_name, d.first_name, d.middle_name, + d.position, d.category, d.degree, d.practice_start_year, d.rating_override::float8, + d.photo_id, COALESCE(m.path, ''), d.quote, d.bio, + COALESCE(( + SELECT string_agg(sp.name, ', ' ORDER BY sp.name) + FROM doctor_specialties ds + JOIN specialties sp ON sp.id = ds.specialty_id + WHERE ds.doctor_id = d.id + ), ''), + ps.id, COALESCE(ps.slug, ''), + d.booking_provider, d.medflex_user, d.sort, d.published, + d.absent, d.show_on_home, + (SELECT count(*) FROM doctor_bio_entries e WHERE e.doctor_id = d.id), + d.seo_title, d.seo_description, d.seo_h1, + ARRAY(SELECT service_id FROM doctor_services WHERE doctor_id = d.id ORDER BY service_id), + ARRAY(SELECT branch_id FROM doctor_branches WHERE doctor_id = d.id ORDER BY branch_id) + FROM doctors d + LEFT JOIN media m ON m.id = d.photo_id + LEFT JOIN LATERAL ( + SELECT sp.id, sp.slug FROM doctor_specialties ds + JOIN specialties sp ON sp.id = ds.specialty_id + WHERE ds.doctor_id = d.id + ORDER BY ds.is_primary DESC, sp.sort, sp.id LIMIT 1 + ) ps ON true + WHERE d.deleted_at IS NULL + AND ($1 = '' OR d.last_name ILIKE '%' || $1 || '%' + OR d.first_name ILIKE '%' || $1 || '%' + OR d.position ILIKE '%' || $1 || '%') + ORDER BY d.sort, d.last_name, d.first_name`, q) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminDoctor + for rows.Next() { + var a AdminDoctor + if err := rows.Scan(&a.ID, &a.Slug, &a.LastName, &a.FirstName, &a.MiddleName, + &a.Position, &a.Category, &a.Degree, &a.PracticeYear, &a.Rating, + &a.PhotoID, &a.Photo, &a.Quote, &a.Bio, &a.Specialties, + &a.SpecialtyID, &a.SpecialtySlug, + &a.Booking, &a.MedflexUser, &a.Sort, &a.Published, + &a.Absent, &a.ShowOnHome, &a.BioCount, + &a.SEOTitle, &a.SEODesc, &a.SEOH1, &a.ServiceIDs, &a.BranchIDs); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type DoctorUpdate struct { + Slug string `json:"slug"` + LastName string `json:"last_name"` + FirstName string `json:"first_name"` + MiddleName string `json:"middle_name"` + Position string `json:"position"` + Category string `json:"category"` + Degree string `json:"degree"` + PracticeYear *int `json:"practice_start_year"` + Rating *float64 `json:"rating"` + PhotoID *int64 `json:"photo_id"` + Quote string `json:"quote"` + Bio string `json:"bio"` + Booking string `json:"booking_provider"` + MedflexUser string `json:"medflex_user"` + Sort int `json:"sort"` + Published bool `json:"published"` + Absent bool `json:"absent"` + ShowOnHome bool `json:"show_on_home"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +func (s *Storage) AdminUpdateDoctor(ctx context.Context, id int64, in DoctorUpdate) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE doctors SET slug=$2, last_name=$3, first_name=$4, middle_name=$5, + position=$6, category=$7, degree=$8, practice_start_year=$9, rating_override=$10, + quote=$11, bio=$12, booking_provider=$13, medflex_user=$14, + sort=$15, published=$16, absent=$17, show_on_home=$18, + seo_title=$19, seo_description=$20, seo_h1=$21, photo_id=$22, updated_at=now() + WHERE id=$1 AND deleted_at IS NULL`, + id, in.Slug, in.LastName, in.FirstName, in.MiddleName, + in.Position, in.Category, in.Degree, in.PracticeYear, in.Rating, + in.Quote, in.Bio, in.Booking, in.MedflexUser, + in.Sort, in.Published, in.Absent, in.ShowOnHome, + in.SEOTitle, in.SEODesc, in.SEOH1, in.PhotoID) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// SetDoctorLinks заменяет услуги и филиалы врача целиком, в одной транзакции. +func (s *Storage) SetDoctorLinks(ctx context.Context, doctorID int64, serviceIDs, branchIDs []int64) error { + tx, err := s.pool.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + var exists bool + if err := tx.QueryRow(ctx, `SELECT EXISTS (SELECT 1 FROM doctors WHERE id=$1 AND deleted_at IS NULL)`, doctorID).Scan(&exists); err != nil { + return err + } + if !exists { + return ErrNotFound + } + if _, err := tx.Exec(ctx, `DELETE FROM doctor_services WHERE doctor_id=$1`, doctorID); err != nil { + return err + } + if _, err := tx.Exec(ctx, ` + INSERT INTO doctor_services (doctor_id, service_id) + SELECT $1, unnest($2::bigint[]) ON CONFLICT DO NOTHING`, doctorID, serviceIDs); err != nil { + return wrapConflict(err) + } + if _, err := tx.Exec(ctx, `DELETE FROM doctor_branches WHERE doctor_id=$1`, doctorID); err != nil { + return err + } + if _, err := tx.Exec(ctx, ` + INSERT INTO doctor_branches (doctor_id, branch_id) + SELECT $1, unnest($2::bigint[]) ON CONFLICT DO NOTHING`, doctorID, branchIDs); err != nil { + return wrapConflict(err) + } + return tx.Commit(ctx) +} + +// ---------- Записи биографии врача ---------- + +type AdminBioEntry struct { + ID int64 `json:"id"` + DoctorID int64 `json:"doctor_id"` + Kind string `json:"kind"` + YearFrom *int `json:"year_from"` + YearTo *int `json:"year_to"` + Title string `json:"title"` + Description string `json:"description"` + Sort int `json:"sort"` +} + +func (s *Storage) AdminBioEntries(ctx context.Context, doctorID int64) ([]AdminBioEntry, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, doctor_id, kind, year_from, year_to, title, description, sort + FROM doctor_bio_entries WHERE doctor_id=$1 + ORDER BY kind, sort, id`, doctorID) + if err != nil { + return nil, err + } + defer rows.Close() + var out []AdminBioEntry + for rows.Next() { + var a AdminBioEntry + if err := rows.Scan(&a.ID, &a.DoctorID, &a.Kind, &a.YearFrom, &a.YearTo, + &a.Title, &a.Description, &a.Sort); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type BioEntryInput struct { + DoctorID int64 `json:"doctor_id"` + Kind string `json:"kind"` + YearFrom *int `json:"year_from"` + YearTo *int `json:"year_to"` + Title string `json:"title"` + Description string `json:"description"` + Sort int `json:"sort"` +} + +func (s *Storage) AdminCreateBioEntry(ctx context.Context, in BioEntryInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO doctor_bio_entries (doctor_id, kind, year_from, year_to, title, description, sort) + VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING id`, + in.DoctorID, in.Kind, in.YearFrom, in.YearTo, in.Title, in.Description, in.Sort).Scan(&id) + return id, err +} + +func (s *Storage) AdminUpdateBioEntry(ctx context.Context, id int64, in BioEntryInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE doctor_bio_entries SET kind=$2, year_from=$3, year_to=$4, + title=$5, description=$6, sort=$7 + WHERE id=$1`, + id, in.Kind, in.YearFrom, in.YearTo, in.Title, in.Description, in.Sort) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminDeleteBioEntry(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM doctor_bio_entries WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_documents.go b/internal/storage/admin_documents.go new file mode 100644 index 0000000..04ddbca --- /dev/null +++ b/internal/storage/admin_documents.go @@ -0,0 +1,94 @@ +package storage + +import ( + "context" + "time" +) + +type AdminDocument struct { + ID int64 `json:"id"` + Kind string `json:"kind"` + Title string `json:"title"` + Body string `json:"body"` + FileID *int64 `json:"file_id"` + File string `json:"file,omitempty"` + FileMime string `json:"file_mime,omitempty"` + PreviewID *int64 `json:"preview_id"` + Preview string `json:"preview,omitempty"` + IssuedAt *time.Time `json:"issued_at"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminDocuments(ctx context.Context) ([]AdminDocument, error) { + rows, err := s.pool.Query(ctx, ` + SELECT d.id, d.kind, d.title, d.body, d.file_id, COALESCE(f.path, ''), COALESCE(f.mime, ''), + d.preview_id, COALESCE(p.path, ''), d.issued_at, d.sort, d.published + FROM documents d + LEFT JOIN media f ON f.id = d.file_id + LEFT JOIN media p ON p.id = d.preview_id + ORDER BY d.kind, d.sort, d.id`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminDocument + for rows.Next() { + var d AdminDocument + if err := rows.Scan(&d.ID, &d.Kind, &d.Title, &d.Body, &d.FileID, &d.File, &d.FileMime, + &d.PreviewID, &d.Preview, &d.IssuedAt, &d.Sort, &d.Published); err != nil { + return nil, err + } + out = append(out, d) + } + return out, rows.Err() +} + +type DocumentInput struct { + Kind string + Title string + Body string + FileID *int64 + PreviewID *int64 + IssuedAt *time.Time + Sort int + Published bool +} + +func (s *Storage) AdminCreateDocument(ctx context.Context, in DocumentInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO documents (kind, title, body, file_id, preview_id, issued_at, sort, published) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8) + RETURNING id`, + in.Kind, in.Title, in.Body, in.FileID, in.PreviewID, in.IssuedAt, in.Sort, in.Published).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdateDocument(ctx context.Context, id int64, in DocumentInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE documents SET kind=$2, title=$3, body=$4, file_id=$5, preview_id=$6, issued_at=$7, + sort=$8, published=$9 + WHERE id=$1`, + id, in.Kind, in.Title, in.Body, in.FileID, in.PreviewID, in.IssuedAt, in.Sort, in.Published) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// AdminDeleteDocument удаляет запись; файлы остаются в медиатеке. +func (s *Storage) AdminDeleteDocument(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM documents WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_faq.go b/internal/storage/admin_faq.go new file mode 100644 index 0000000..269c15f --- /dev/null +++ b/internal/storage/admin_faq.go @@ -0,0 +1,79 @@ +package storage + +import "context" + +type AdminFAQ struct { + ID int64 `json:"id"` + Question string `json:"question"` + Answer string `json:"answer"` + ServiceID *int64 `json:"service_id"` + ServiceName string `json:"service_name,omitempty"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminFAQ(ctx context.Context) ([]AdminFAQ, error) { + rows, err := s.pool.Query(ctx, ` + SELECT f.id, f.question, f.answer, f.service_id, COALESCE(s.name, ''), f.sort, f.published + FROM faq f + LEFT JOIN services s ON s.id = f.service_id + ORDER BY f.sort, f.id`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminFAQ + for rows.Next() { + var f AdminFAQ + if err := rows.Scan(&f.ID, &f.Question, &f.Answer, &f.ServiceID, &f.ServiceName, + &f.Sort, &f.Published); err != nil { + return nil, err + } + out = append(out, f) + } + return out, rows.Err() +} + +type FAQInput struct { + Question string + Answer string + ServiceID *int64 + Sort int + Published bool +} + +func (s *Storage) AdminCreateFAQ(ctx context.Context, in FAQInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO faq (question, answer, service_id, sort, published) + VALUES ($1,$2,$3,$4,$5) + RETURNING id`, + in.Question, in.Answer, in.ServiceID, in.Sort, in.Published).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdateFAQ(ctx context.Context, id int64, in FAQInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE faq SET question=$2, answer=$3, service_id=$4, sort=$5, published=$6 + WHERE id=$1`, + id, in.Question, in.Answer, in.ServiceID, in.Sort, in.Published) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminDeleteFAQ(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM faq WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_inbox.go b/internal/storage/admin_inbox.go new file mode 100644 index 0000000..52285ec --- /dev/null +++ b/internal/storage/admin_inbox.go @@ -0,0 +1,240 @@ +package storage + +import ( + "context" + "encoding/json" + "time" +) + +// ---------- Заявки ---------- + +type AdminLead struct { + ID int64 `json:"id"` + FormCode string `json:"form_code,omitempty"` + FormTitle string `json:"form_title,omitempty"` + Name string `json:"name"` + Phone string `json:"phone"` + Email string `json:"email,omitempty"` + ServiceName string `json:"service_name,omitempty"` + DoctorName string `json:"doctor_name,omitempty"` + Message string `json:"message,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` + Status string `json:"status"` + Comment string `json:"comment,omitempty"` + PageURL string `json:"page_url,omitempty"` + UTM json.RawMessage `json:"utm,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +// AdminLeads: постранично, с фильтрами по статусу, тексту и календарным датам. +func (s *Storage) AdminLeads(ctx context.Context, status, q string, from, to *time.Time, limit, offset int) ([]AdminLead, int, error) { + var total int + if err := s.pool.QueryRow(ctx, ` + SELECT count(*) FROM leads l + WHERE ($1 = '' OR l.status = $1) + AND ($2 = '' OR l.name ILIKE '%' || $2 || '%' OR l.phone ILIKE '%' || $2 || '%' OR l.message ILIKE '%' || $2 || '%') + AND ($3::timestamptz IS NULL OR l.created_at >= $3) + AND ($4::timestamptz IS NULL OR l.created_at < $4)`, + status, q, from, to).Scan(&total); err != nil { + return nil, 0, err + } + + rows, err := s.pool.Query(ctx, ` + SELECT l.id, COALESCE(f.code, ''), COALESCE(f.title, ''), + l.name, l.phone, l.email, + COALESCE(sv.name, ''), COALESCE(d.last_name || ' ' || d.first_name, ''), + l.message, l.payload, l.status, l.comment, l.page_url, l.utm, l.created_at + FROM leads l + LEFT JOIN forms f ON f.id = l.form_id + LEFT JOIN services sv ON sv.id = l.service_id + LEFT JOIN doctors d ON d.id = l.doctor_id + WHERE ($1 = '' OR l.status = $1) + AND ($2 = '' OR l.name ILIKE '%' || $2 || '%' OR l.phone ILIKE '%' || $2 || '%' OR l.message ILIKE '%' || $2 || '%') + AND ($3::timestamptz IS NULL OR l.created_at >= $3) + AND ($4::timestamptz IS NULL OR l.created_at < $4) + ORDER BY l.created_at DESC + LIMIT $5 OFFSET $6`, status, q, from, to, limit, offset) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var out []AdminLead + for rows.Next() { + var a AdminLead + if err := rows.Scan(&a.ID, &a.FormCode, &a.FormTitle, &a.Name, &a.Phone, &a.Email, + &a.ServiceName, &a.DoctorName, &a.Message, &a.Payload, + &a.Status, &a.Comment, &a.PageURL, &a.UTM, &a.CreatedAt); err != nil { + return nil, 0, err + } + out = append(out, a) + } + return out, total, rows.Err() +} + +// LeadCounts — сколько заявок в каждом статусе, для бейджей в интерфейсе. +func (s *Storage) LeadCounts(ctx context.Context) (map[string]int, error) { + rows, err := s.pool.Query(ctx, `SELECT status, count(*) FROM leads GROUP BY status`) + if err != nil { + return nil, err + } + defer rows.Close() + out := map[string]int{} + for rows.Next() { + var st string + var n int + if err := rows.Scan(&st, &n); err != nil { + return nil, err + } + out[st] = n + } + return out, rows.Err() +} + +func (s *Storage) LeadTodayCount(ctx context.Context) (int, error) { + var count int + now := time.Now().In(time.Local) + start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) + err := s.pool.QueryRow(ctx, `SELECT count(*) FROM leads WHERE created_at >= $1 AND created_at < $2`, start, start.AddDate(0, 0, 1)).Scan(&count) + return count, err +} + +type LeadUpdate struct { + Status string `json:"status"` + Comment string `json:"comment"` +} + +func (s *Storage) AdminUpdateLead(ctx context.Context, id int64, in LeadUpdate) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE leads SET status=$2, comment=$3, updated_at=now() WHERE id=$1`, + id, in.Status, in.Comment) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// ---------- Отзывы (включая непроверенные) ---------- + +type AdminReview struct { + ID int64 `json:"id"` + AuthorName string `json:"author_name"` + Rating *int `json:"rating"` + Text string `json:"text"` + VideoURL string `json:"video_url"` + DoctorID *int64 `json:"doctor_id"` + DoctorName string `json:"doctor_name,omitempty"` + Source string `json:"source"` + Status string `json:"status"` + Reply string `json:"reply"` + CreatedAt time.Time `json:"created_at"` +} + +func (s *Storage) AdminReviews(ctx context.Context, status, q string, limit, offset int) ([]AdminReview, int, error) { + var total int + if err := s.pool.QueryRow(ctx, ` + SELECT count(*) FROM reviews r + WHERE ($1 = '' OR r.status = $1) + AND ($2 = '' OR r.author_name ILIKE '%' || $2 || '%' OR r.text ILIKE '%' || $2 || '%')`, + status, q).Scan(&total); err != nil { + return nil, 0, err + } + + rows, err := s.pool.Query(ctx, ` + SELECT r.id, r.author_name, r.rating, r.text, r.video_url, r.doctor_id, + COALESCE(d.last_name || ' ' || d.first_name, ''), + r.source, r.status, r.reply, r.created_at + FROM reviews r + LEFT JOIN doctors d ON d.id = r.doctor_id + WHERE ($1 = '' OR r.status = $1) + AND ($2 = '' OR r.author_name ILIKE '%' || $2 || '%' OR r.text ILIKE '%' || $2 || '%') + ORDER BY + CASE r.status WHEN 'pending' THEN 0 ELSE 1 END, -- непроверенные сверху + r.created_at DESC + LIMIT $3 OFFSET $4`, status, q, limit, offset) + if err != nil { + return nil, 0, err + } + defer rows.Close() + + var out []AdminReview + for rows.Next() { + var a AdminReview + if err := rows.Scan(&a.ID, &a.AuthorName, &a.Rating, &a.Text, &a.VideoURL, &a.DoctorID, + &a.DoctorName, &a.Source, &a.Status, &a.Reply, &a.CreatedAt); err != nil { + return nil, 0, err + } + out = append(out, a) + } + return out, total, rows.Err() +} + +func (s *Storage) ReviewCounts(ctx context.Context) (map[string]int, error) { + rows, err := s.pool.Query(ctx, `SELECT status, count(*) FROM reviews GROUP BY status`) + if err != nil { + return nil, err + } + defer rows.Close() + out := map[string]int{} + for rows.Next() { + var st string + var n int + if err := rows.Scan(&st, &n); err != nil { + return nil, err + } + out[st] = n + } + return out, rows.Err() +} + +type ReviewUpdate struct { + AuthorName string `json:"author_name"` + Rating *int `json:"rating"` + Text string `json:"text"` + VideoURL string `json:"video_url"` + DoctorID *int64 `json:"doctor_id"` + Status string `json:"status"` + Reply string `json:"reply"` +} + +func (s *Storage) AdminUpdateReview(ctx context.Context, id int64, in ReviewUpdate) error { + // moderated_at проставляется, когда отзыв уходит из ожидания проверки + tag, err := s.pool.Exec(ctx, ` + UPDATE reviews SET author_name=$2, rating=$3, text=$4, video_url=$5, doctor_id=$6, + status=$7, reply=$8, + moderated_at = CASE WHEN $7 <> 'pending' THEN now() ELSE moderated_at END, + updated_at = now() + WHERE id=$1`, + id, in.AuthorName, in.Rating, in.Text, in.VideoURL, in.DoctorID, in.Status, in.Reply) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminCreateReview(ctx context.Context, in ReviewUpdate) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO reviews (author_name, rating, text, video_url, doctor_id, source, status, reply, moderated_at) + VALUES ($1,$2,$3,$4,$5,'manual',$6,$7, CASE WHEN $6 <> 'pending' THEN now() END) + RETURNING id`, + in.AuthorName, in.Rating, in.Text, in.VideoURL, in.DoctorID, in.Status, in.Reply).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminDeleteReview(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM reviews WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_partners.go b/internal/storage/admin_partners.go new file mode 100644 index 0000000..8c4b2d0 --- /dev/null +++ b/internal/storage/admin_partners.go @@ -0,0 +1,78 @@ +package storage + +import "context" + +type AdminPartner struct { + ID int64 `json:"id"` + Name string `json:"name"` + URL string `json:"url"` + LogoID *int64 `json:"logo_id"` + Logo string `json:"logo,omitempty"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminPartners(ctx context.Context) ([]AdminPartner, error) { + rows, err := s.pool.Query(ctx, ` + SELECT p.id, p.name, p.url, p.logo_id, COALESCE(m.path, ''), p.sort, p.published + FROM partners p + LEFT JOIN media m ON m.id = p.logo_id + ORDER BY p.sort, p.name`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminPartner + for rows.Next() { + var p AdminPartner + if err := rows.Scan(&p.ID, &p.Name, &p.URL, &p.LogoID, &p.Logo, &p.Sort, &p.Published); err != nil { + return nil, err + } + out = append(out, p) + } + return out, rows.Err() +} + +type PartnerInput struct { + Name string + URL string + LogoID *int64 + Sort int + Published bool +} + +func (s *Storage) AdminCreatePartner(ctx context.Context, in PartnerInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO partners (name, url, logo_id, sort, published) + VALUES ($1,$2,$3,$4,$5) + RETURNING id`, + in.Name, in.URL, in.LogoID, in.Sort, in.Published).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdatePartner(ctx context.Context, id int64, in PartnerInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE partners SET name=$2, url=$3, logo_id=$4, sort=$5, published=$6 + WHERE id=$1`, + id, in.Name, in.URL, in.LogoID, in.Sort, in.Published) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminDeletePartner(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM partners WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_posts.go b/internal/storage/admin_posts.go new file mode 100644 index 0000000..3821e08 --- /dev/null +++ b/internal/storage/admin_posts.go @@ -0,0 +1,107 @@ +package storage + +import ( + "context" + "encoding/json" + "time" +) + +// AdminPost — новость или статья для админки, включая скрытые. +type AdminPost struct { + ID int64 `json:"id"` + Kind string `json:"kind"` + Slug string `json:"slug"` + Title string `json:"title"` + Lead string `json:"lead"` + Blocks json.RawMessage `json:"blocks"` + ImageID *int64 `json:"image_id"` + Image string `json:"image,omitempty"` + Published bool `json:"published"` + PublishedAt time.Time `json:"published_at"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +// AdminPosts — публикации одного вида (news | article), новые сверху. +func (s *Storage) AdminPosts(ctx context.Context, kind string) ([]AdminPost, error) { + rows, err := s.pool.Query(ctx, ` + SELECT p.id, p.kind, p.slug, p.title, p.lead, p.blocks, p.image_id, COALESCE(m.path, ''), + p.published, p.published_at, p.seo_title, p.seo_description, p.seo_h1 + FROM posts p + LEFT JOIN media m ON m.id = p.image_id + WHERE p.kind = $1 AND p.deleted_at IS NULL + ORDER BY p.published_at DESC, p.id DESC`, kind) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminPost + for rows.Next() { + var a AdminPost + if err := rows.Scan(&a.ID, &a.Kind, &a.Slug, &a.Title, &a.Lead, &a.Blocks, &a.ImageID, &a.Image, + &a.Published, &a.PublishedAt, &a.SEOTitle, &a.SEODesc, &a.SEOH1); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type PostInput struct { + Kind string + Slug string + Title string + Lead string + Blocks json.RawMessage + ImageID *int64 + Published bool + PublishedAt time.Time + SEOTitle string + SEODesc string + SEOH1 string +} + +func (s *Storage) AdminCreatePost(ctx context.Context, in PostInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO posts (kind, slug, title, lead, blocks, image_id, published, published_at, + seo_title, seo_description, seo_h1) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) + RETURNING id`, + in.Kind, in.Slug, in.Title, in.Lead, in.Blocks, in.ImageID, in.Published, in.PublishedAt, + in.SEOTitle, in.SEODesc, in.SEOH1).Scan(&id) + return id, wrapConflict(err) +} + +// AdminUpdatePost меняет публикацию; вид (новость или статья) после создания не меняется. +func (s *Storage) AdminUpdatePost(ctx context.Context, id int64, in PostInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE posts SET slug=$3, title=$4, lead=$5, blocks=$6, image_id=$7, published=$8, + published_at=$9, seo_title=$10, seo_description=$11, seo_h1=$12, updated_at=now() + WHERE id=$1 AND kind=$2 AND deleted_at IS NULL`, + id, in.Kind, in.Slug, in.Title, in.Lead, in.Blocks, in.ImageID, in.Published, + in.PublishedAt, in.SEOTitle, in.SEODesc, in.SEOH1) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// AdminDeletePost — мягкое удаление, как у акций. +func (s *Storage) AdminDeletePost(ctx context.Context, kind string, id int64) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE posts SET deleted_at=now(), published=false, updated_at=now() + WHERE id=$1 AND kind=$2 AND deleted_at IS NULL`, id, kind) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_promos.go b/internal/storage/admin_promos.go new file mode 100644 index 0000000..d268d0a --- /dev/null +++ b/internal/storage/admin_promos.go @@ -0,0 +1,116 @@ +package storage + +import ( + "context" + "time" +) + +type AdminPromo struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Lead string `json:"lead"` + ImageID *int64 `json:"image_id"` + Image string `json:"image,omitempty"` + DiscountPercent *int `json:"discount_percent"` + Terms string `json:"terms"` + CheckupID *int64 `json:"checkup_id"` + CheckupName string `json:"checkup_name,omitempty"` + StartsAt *time.Time `json:"starts_at"` + EndsAt *time.Time `json:"ends_at"` + Published bool `json:"published"` + PublishedAt time.Time `json:"published_at"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` +} + +// AdminPromos — все акции, включая скрытые и завершённые. +func (s *Storage) AdminPromos(ctx context.Context) ([]AdminPromo, error) { + rows, err := s.pool.Query(ctx, ` + SELECT p.id, p.slug, p.title, p.lead, p.image_id, COALESCE(m.path, ''), + p.discount_percent, p.terms, p.checkup_id, COALESCE(c.name, ''), + p.starts_at, p.ends_at, p.published, p.published_at, + p.seo_title, p.seo_description, p.seo_h1 + FROM posts p + LEFT JOIN media m ON m.id = p.image_id + LEFT JOIN checkups c ON c.id = p.checkup_id + WHERE p.kind = 'promo' AND p.deleted_at IS NULL + ORDER BY p.published_at DESC, p.id DESC`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminPromo + for rows.Next() { + var a AdminPromo + if err := rows.Scan(&a.ID, &a.Slug, &a.Title, &a.Lead, &a.ImageID, &a.Image, + &a.DiscountPercent, &a.Terms, &a.CheckupID, &a.CheckupName, + &a.StartsAt, &a.EndsAt, &a.Published, &a.PublishedAt, + &a.SEOTitle, &a.SEODesc, &a.SEOH1); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type PromoInput struct { + Slug string + Title string + Lead string + ImageID *int64 + DiscountPercent *int + Terms string + CheckupID *int64 + StartsAt *time.Time + EndsAt *time.Time + Published bool + SEOTitle string + SEODesc string + SEOH1 string +} + +func (s *Storage) AdminCreatePromo(ctx context.Context, in PromoInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO posts (kind, slug, title, lead, image_id, discount_percent, terms, checkup_id, + starts_at, ends_at, published, seo_title, seo_description, seo_h1) + VALUES ('promo', $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) + RETURNING id`, + in.Slug, in.Title, in.Lead, in.ImageID, in.DiscountPercent, in.Terms, in.CheckupID, + in.StartsAt, in.EndsAt, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdatePromo(ctx context.Context, id int64, in PromoInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE posts SET slug=$2, title=$3, lead=$4, image_id=$5, discount_percent=$6, terms=$7, + checkup_id=$8, starts_at=$9, ends_at=$10, published=$11, + seo_title=$12, seo_description=$13, seo_h1=$14, updated_at=now() + WHERE id=$1 AND kind='promo' AND deleted_at IS NULL`, + id, in.Slug, in.Title, in.Lead, in.ImageID, in.DiscountPercent, in.Terms, + in.CheckupID, in.StartsAt, in.EndsAt, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// AdminDeletePromo скрывает акцию мягким удалением: запись остаётся для истории и аудита. +func (s *Storage) AdminDeletePromo(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE posts SET deleted_at=now(), published=false, updated_at=now() + WHERE id=$1 AND kind='promo' AND deleted_at IS NULL`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_services.go b/internal/storage/admin_services.go new file mode 100644 index 0000000..58e86d9 --- /dev/null +++ b/internal/storage/admin_services.go @@ -0,0 +1,45 @@ +package storage + +import ( + "context" + "errors" + + "github.com/jackc/pgx/v5" +) + +type NewService struct { + ParentID int64 + Slug string + Name string + Lead string +} + +// AdminCreateService заводит услугу внутри направления, скрытой. Адрес — адрес направления +// плюс slug: /services/<направление>//. Вложенность — только один уровень. +func (s *Storage) AdminCreateService(ctx context.Context, in NewService) (int64, error) { + tx, err := s.pool.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + var parentPath string + if err := tx.QueryRow(ctx, ` + SELECT path FROM services WHERE id=$1 AND parent_id IS NULL AND deleted_at IS NULL`, + in.ParentID).Scan(&parentPath); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return 0, ErrBadReference + } + return 0, err + } + var id int64 + if err := tx.QueryRow(ctx, ` + INSERT INTO services (parent_id, slug, path, name, lead, published, + sort) + VALUES ($1, $2, $3, $4, $5, false, + COALESCE((SELECT max(sort) + 10 FROM services WHERE parent_id = $1 AND deleted_at IS NULL), 100)) + RETURNING id`, + in.ParentID, in.Slug, parentPath+"/"+in.Slug, in.Name, in.Lead).Scan(&id); err != nil { + return 0, wrapConflict(err) + } + return id, tx.Commit(ctx) +} diff --git a/internal/storage/admin_site.go b/internal/storage/admin_site.go new file mode 100644 index 0000000..a1b2bbe --- /dev/null +++ b/internal/storage/admin_site.go @@ -0,0 +1,181 @@ +package storage + +import ( + "context" + "encoding/json" + "time" +) + +// ---------- Страницы ---------- + +type AdminPage struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Blocks json.RawMessage `json:"blocks"` + Published bool `json:"published"` + SEOTitle string `json:"seo_title"` + SEODesc string `json:"seo_description"` + SEOH1 string `json:"seo_h1"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (s *Storage) AdminPages(ctx context.Context) ([]AdminPage, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at + FROM pages ORDER BY slug`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminPage + for rows.Next() { + var p AdminPage + if err := rows.Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published, + &p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt); err != nil { + return nil, err + } + out = append(out, p) + } + return out, rows.Err() +} + +func (s *Storage) AdminPage(ctx context.Context, id int64) (*AdminPage, error) { + var p AdminPage + err := s.pool.QueryRow(ctx, ` + SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at + FROM pages WHERE id=$1`, id).Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published, + &p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt) + if err != nil { + return nil, wrapNotFound(err) + } + return &p, nil +} + +type PageInput struct { + Title string + Blocks json.RawMessage + Published bool + SEOTitle string + SEODesc string + SEOH1 string +} + +// AdminUpdatePage меняет содержимое страницы. Адрес (slug) не меняется: страницы +// привязаны к маршрутам сайта. Новых страниц админка не создаёт по той же причине. +func (s *Storage) AdminUpdatePage(ctx context.Context, id int64, in PageInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE pages SET title=$2, blocks=$3, published=$4, seo_title=$5, seo_description=$6, + seo_h1=$7, updated_at=now() + WHERE id=$1`, + id, in.Title, in.Blocks, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// ---------- Настройки ---------- + +// AdminSetSettings записывает несколько ключей за раз в одной транзакции. +func (s *Storage) AdminSetSettings(ctx context.Context, values map[string]any) error { + tx, err := s.pool.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + for key, value := range values { + raw, err := json.Marshal(value) + if err != nil { + return err + } + if _, err := tx.Exec(ctx, ` + INSERT INTO settings (key, value, updated_at) VALUES ($1, $2, now()) + ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()`, + key, raw); err != nil { + return err + } + } + return tx.Commit(ctx) +} + +// ---------- Филиалы ---------- + +type AdminBranch struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + Address string `json:"address"` + Phones []string `json:"phones"` + WorkHours []string `json:"work_hours"` + Lat *float64 `json:"lat"` + Lon *float64 `json:"lon"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminBranches(ctx context.Context) ([]AdminBranch, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, slug, name, address, phones, work_hours, lat, lon, sort, published + FROM branches ORDER BY sort, name`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminBranch + for rows.Next() { + var b AdminBranch + if err := rows.Scan(&b.ID, &b.Slug, &b.Name, &b.Address, &b.Phones, &b.WorkHours, + &b.Lat, &b.Lon, &b.Sort, &b.Published); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} + +type BranchInput struct { + Slug string + Name string + Address string + Phones []string + WorkHours []string + Lat *float64 + Lon *float64 + Sort int + Published bool +} + +func (s *Storage) AdminCreateBranch(ctx context.Context, in BranchInput) (int64, error) { + phones, _ := json.Marshal(in.Phones) + hours, _ := json.Marshal(in.WorkHours) + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO branches (slug, name, address, phones, work_hours, lat, lon, sort, published) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9) + RETURNING id`, + in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdateBranch(ctx context.Context, id int64, in BranchInput) error { + phones, _ := json.Marshal(in.Phones) + hours, _ := json.Marshal(in.WorkHours) + tag, err := s.pool.Exec(ctx, ` + UPDATE branches SET slug=$2, name=$3, address=$4, phones=$5, work_hours=$6, lat=$7, lon=$8, + sort=$9, published=$10, updated_at=now() + WHERE id=$1`, + id, in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/admin_specialties.go b/internal/storage/admin_specialties.go new file mode 100644 index 0000000..7ab7ae1 --- /dev/null +++ b/internal/storage/admin_specialties.go @@ -0,0 +1,124 @@ +package storage + +import ( + "context" + "errors" +) + +// ErrInUse — запись нельзя удалить, пока на неё ссылаются (например, специальность с врачами). +var ErrInUse = errors.New("storage: in use") + +type AdminSpecialty struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + NamePlural string `json:"name_plural"` + Sort int `json:"sort"` + Published bool `json:"published"` + Doctors int `json:"doctors"` // все врачи раздела, включая скрытых +} + +type SpecialtyInput struct { + Slug string + Name string + NamePlural string + Sort int +} + +func (s *Storage) AdminSpecialties(ctx context.Context) ([]AdminSpecialty, error) { + rows, err := s.pool.Query(ctx, ` + SELECT sp.id, sp.slug, sp.name, sp.name_plural, sp.sort, sp.published, + (SELECT count(*) FROM doctor_specialties ds + JOIN doctors d ON d.id = ds.doctor_id AND d.deleted_at IS NULL + WHERE ds.specialty_id = sp.id) + FROM specialties sp + ORDER BY sp.sort, sp.name_plural, sp.id`) + if err != nil { + return nil, err + } + defer rows.Close() + var out []AdminSpecialty + for rows.Next() { + var sp AdminSpecialty + if err := rows.Scan(&sp.ID, &sp.Slug, &sp.Name, &sp.NamePlural, &sp.Sort, &sp.Published, &sp.Doctors); err != nil { + return nil, err + } + out = append(out, sp) + } + return out, rows.Err() +} + +func (s *Storage) AdminCreateSpecialty(ctx context.Context, in SpecialtyInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO specialties (slug, name, name_plural, sort) VALUES ($1, $2, $3, $4) RETURNING id`, + in.Slug, in.Name, in.NamePlural, in.Sort).Scan(&id) + return id, wrapConflict(err) +} + +// AdminUpdateSpecialty меняет название и порядок. Адрес раздела не меняется: +// он входит в адреса страниц всех врачей раздела. +func (s *Storage) AdminUpdateSpecialty(ctx context.Context, id int64, in SpecialtyInput) error { + tag, err := s.pool.Exec(ctx, `UPDATE specialties SET name=$2, name_plural=$3, sort=$4 WHERE id=$1`, + id, in.Name, in.NamePlural, in.Sort) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// AdminDeleteSpecialty удаляет только пустой раздел: иначе у врачей пропала бы специальность +// (связи удаляются каскадом) и сменились бы адреса их страниц. +func (s *Storage) AdminDeleteSpecialty(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, ` + DELETE FROM specialties sp WHERE sp.id = $1 + AND NOT EXISTS (SELECT 1 FROM doctor_specialties ds WHERE ds.specialty_id = sp.id)`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + var exists bool + if err := s.pool.QueryRow(ctx, `SELECT EXISTS (SELECT 1 FROM specialties WHERE id=$1)`, id).Scan(&exists); err != nil { + return err + } + if exists { + return ErrInUse + } + return ErrNotFound + } + return nil +} + +type NewDoctor struct { + Slug string + LastName string + FirstName string + MiddleName string + Position string + SpecialtyID int64 +} + +// AdminCreateDoctor заводит врача скрытым: на сайт он попадёт, когда карточку заполнят и включат. +func (s *Storage) AdminCreateDoctor(ctx context.Context, in NewDoctor) (int64, error) { + tx, err := s.pool.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает + var id int64 + if err := tx.QueryRow(ctx, ` + INSERT INTO doctors (slug, last_name, first_name, middle_name, position, published) + VALUES ($1, $2, $3, $4, $5, false) RETURNING id`, + in.Slug, in.LastName, in.FirstName, in.MiddleName, in.Position).Scan(&id); err != nil { + return 0, wrapConflict(err) + } + if _, err := tx.Exec(ctx, ` + INSERT INTO doctor_specialties (doctor_id, specialty_id, is_primary) VALUES ($1, $2, true)`, + id, in.SpecialtyID); err != nil { + return 0, wrapConflict(err) + } + return id, tx.Commit(ctx) +} diff --git a/internal/storage/admin_vacancies.go b/internal/storage/admin_vacancies.go new file mode 100644 index 0000000..3655b8a --- /dev/null +++ b/internal/storage/admin_vacancies.go @@ -0,0 +1,90 @@ +package storage + +import "context" + +type AdminVacancy struct { + ID int64 `json:"id"` + Slug string `json:"slug"` + Title string `json:"title"` + Description string `json:"description"` + Duties string `json:"duties"` + Requirements string `json:"requirements"` + Conditions string `json:"conditions"` + Salary string `json:"salary"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminVacancies(ctx context.Context) ([]AdminVacancy, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, slug, title, description, duties, requirements, conditions, salary, sort, published + FROM vacancies + ORDER BY sort, title`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []AdminVacancy + for rows.Next() { + var v AdminVacancy + if err := rows.Scan(&v.ID, &v.Slug, &v.Title, &v.Description, &v.Duties, &v.Requirements, + &v.Conditions, &v.Salary, &v.Sort, &v.Published); err != nil { + return nil, err + } + out = append(out, v) + } + return out, rows.Err() +} + +type VacancyInput struct { + Slug string + Title string + Description string + Duties string + Requirements string + Conditions string + Salary string + Sort int + Published bool +} + +func (s *Storage) AdminCreateVacancy(ctx context.Context, in VacancyInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO vacancies (slug, title, description, duties, requirements, conditions, salary, sort, published) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9) + RETURNING id`, + in.Slug, in.Title, in.Description, in.Duties, in.Requirements, in.Conditions, in.Salary, + in.Sort, in.Published).Scan(&id) + return id, wrapConflict(err) +} + +func (s *Storage) AdminUpdateVacancy(ctx context.Context, id int64, in VacancyInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE vacancies SET slug=$2, title=$3, description=$4, duties=$5, requirements=$6, + conditions=$7, salary=$8, sort=$9, published=$10, updated_at=now() + WHERE id=$1`, + id, in.Slug, in.Title, in.Description, in.Duties, in.Requirements, in.Conditions, in.Salary, + in.Sort, in.Published) + if err != nil { + return wrapConflict(err) + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +// AdminDeleteVacancy удаляет вакансию насовсем: история по ним не нужна, +// а закрытую на время вакансию достаточно скрыть галочкой. +func (s *Storage) AdminDeleteVacancy(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM vacancies WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/banners.go b/internal/storage/banners.go new file mode 100644 index 0000000..e61387b --- /dev/null +++ b/internal/storage/banners.go @@ -0,0 +1,152 @@ +package storage + +import ( + "context" + "time" + + "dana/internal/domain" +) + +// Banners отдаёт действующие баннеры: опубликованные и попадающие в период показа. +func (s *Storage) Banners(ctx context.Context, position string) ([]domain.Banner, error) { + rows, err := s.pool.Query(ctx, ` + SELECT b.id, b.position, b.layout, b.theme, b.title, b.subtitle, b.badge, b.chips, + COALESCE(mi.path, ''), COALESCE(mm.path, ''), + b.link_url, b.button_text, b.disclaimer + FROM banners b + LEFT JOIN media mi ON mi.id = b.image_id + LEFT JOIN media mm ON mm.id = b.mobile_image_id + WHERE b.published + AND (b.starts_at IS NULL OR b.starts_at <= now()) + AND (b.ends_at IS NULL OR b.ends_at > now()) + AND ($1 = '' OR b.position = $1) + ORDER BY b.sort, b.id`, position) + if err != nil { + return nil, err + } + defer rows.Close() + + out := []domain.Banner{} + for rows.Next() { + var b domain.Banner + if err := rows.Scan(&b.ID, &b.Position, &b.Layout, &b.Theme, &b.Title, &b.Subtitle, &b.Badge, &b.Chips, + &b.Image, &b.MobileImage, &b.LinkURL, &b.ButtonText, &b.Disclaimer); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} + +// ---------- Админка ---------- + +type AdminBanner struct { + ID int64 `json:"id"` + Position string `json:"position"` + Layout string `json:"layout"` + Theme string `json:"theme"` + Title string `json:"title"` + Subtitle string `json:"subtitle"` + Badge string `json:"badge"` + Chips []string `json:"chips"` + ImageID *int64 `json:"image_id"` + Image string `json:"image,omitempty"` + MobileImageID *int64 `json:"mobile_image_id"` + MobileImage string `json:"mobile_image,omitempty"` + LinkURL string `json:"link_url"` + ButtonText string `json:"button_text"` + Disclaimer string `json:"disclaimer"` + StartsAt *time.Time `json:"starts_at"` + EndsAt *time.Time `json:"ends_at"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminBanners(ctx context.Context, position string) ([]AdminBanner, error) { + rows, err := s.pool.Query(ctx, ` + SELECT b.id, b.position, b.layout, b.theme, b.title, b.subtitle, b.badge, b.chips, + b.image_id, COALESCE(mi.path, ''), b.mobile_image_id, COALESCE(mm.path, ''), + b.link_url, b.button_text, b.disclaimer, + b.starts_at, b.ends_at, b.sort, b.published + FROM banners b + LEFT JOIN media mi ON mi.id = b.image_id + LEFT JOIN media mm ON mm.id = b.mobile_image_id + WHERE ($1 = '' OR b.position = $1) + ORDER BY b.position, b.sort, b.id`, position) + if err != nil { + return nil, err + } + defer rows.Close() + + out := []AdminBanner{} + for rows.Next() { + var a AdminBanner + if err := rows.Scan(&a.ID, &a.Position, &a.Layout, &a.Theme, &a.Title, &a.Subtitle, &a.Badge, &a.Chips, + &a.ImageID, &a.Image, &a.MobileImageID, &a.MobileImage, + &a.LinkURL, &a.ButtonText, &a.Disclaimer, + &a.StartsAt, &a.EndsAt, &a.Sort, &a.Published); err != nil { + return nil, err + } + out = append(out, a) + } + return out, rows.Err() +} + +type BannerInput struct { + Position string `json:"position"` + Layout string `json:"layout"` + Theme string `json:"theme"` + Title string `json:"title"` + Subtitle string `json:"subtitle"` + Badge string `json:"badge"` + Chips []string `json:"chips"` + ImageID *int64 `json:"image_id"` + MobileImageID *int64 `json:"mobile_image_id"` + LinkURL string `json:"link_url"` + ButtonText string `json:"button_text"` + Disclaimer string `json:"disclaimer"` + StartsAt *time.Time `json:"starts_at"` + EndsAt *time.Time `json:"ends_at"` + Sort int `json:"sort"` + Published bool `json:"published"` +} + +func (s *Storage) AdminCreateBanner(ctx context.Context, in BannerInput) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO banners (position, layout, theme, title, subtitle, badge, chips, image_id, mobile_image_id, + link_url, button_text, disclaimer, starts_at, ends_at, sort, published) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) RETURNING id`, + in.Position, in.Layout, in.Theme, in.Title, in.Subtitle, in.Badge, in.Chips, in.ImageID, in.MobileImageID, + in.LinkURL, in.ButtonText, in.Disclaimer, in.StartsAt, in.EndsAt, in.Sort, in.Published).Scan(&id) + return id, err +} + +func (s *Storage) AdminUpdateBanner(ctx context.Context, id int64, in BannerInput) error { + tag, err := s.pool.Exec(ctx, ` + UPDATE banners SET position=$2, layout=$3, theme=$4, title=$5, subtitle=$6, badge=$7, chips=$8, + image_id=$9, mobile_image_id=$10, link_url=$11, button_text=$12, disclaimer=$13, + starts_at=$14, ends_at=$15, sort=$16, published=$17, updated_at=now() + WHERE id=$1`, + id, in.Position, in.Layout, in.Theme, in.Title, in.Subtitle, in.Badge, in.Chips, + in.ImageID, in.MobileImageID, in.LinkURL, in.ButtonText, in.Disclaimer, + in.StartsAt, in.EndsAt, in.Sort, in.Published) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} + +func (s *Storage) AdminDeleteBanner(ctx context.Context, id int64) error { + tag, err := s.pool.Exec(ctx, `DELETE FROM banners WHERE id=$1`, id) + if err != nil { + return err + } + if tag.RowsAffected() == 0 { + return ErrNotFound + } + return nil +} diff --git a/internal/storage/booking_confirm.go b/internal/storage/booking_confirm.go new file mode 100644 index 0000000..b416973 --- /dev/null +++ b/internal/storage/booking_confirm.go @@ -0,0 +1,179 @@ +package storage + +import ( + "context" + "encoding/json" + "time" +) + +// BookingSettings — правила онлайн-записи, которые меняет администратор (ключ "booking" в settings). +type BookingSettings struct { + // PhoneCheck — требовать проверочный звонок от пациента перед записью. По умолчанию выключен: + // запись подтверждает оператор в Битрикс24. + PhoneCheck bool `json:"phone_check"` + // AutoCancelHours — через сколько часов после записи отменить её, если оператор не подтвердил. + AutoCancelHours int `json:"autocancel_hours"` + // CancelBeforeHours — не позже чем за сколько часов до приёма отменить неподтверждённую запись. + CancelBeforeHours int `json:"cancel_before_hours"` + + // Защита расписания от ботов (0 — правило выключено). Сверх лимита сайт просит проверочный + // звонок: настоящий пациент записывается дальше, бот останавливается. Без SMSC — заявка оператору. + MaxPerDoctorDay int `json:"max_per_doctor_day"` // неподтверждённых записей к врачу на один день + MaxPerHour int `json:"max_per_hour"` // онлайн-записей с сайта за последний час + MaxActivePerPhone int `json:"max_active_per_phone"` // будущих записей на один номер +} + +// DefaultBookingSettings — то, что действует, пока администратор ничего не менял. +// Поля, которых нет в сохранённых настройках, тоже берутся отсюда. +var DefaultBookingSettings = BookingSettings{ + AutoCancelHours: 24, CancelBeforeHours: 2, + MaxPerDoctorDay: 3, MaxPerHour: 10, MaxActivePerPhone: 3, +} + +func (s *Storage) BookingSettings(ctx context.Context) (BookingSettings, error) { + out := DefaultBookingSettings + var raw []byte + err := s.pool.QueryRow(ctx, `SELECT value FROM settings WHERE key = 'booking'`).Scan(&raw) + if wrapNotFound(err) == ErrNotFound { + return out, nil + } + if err != nil { + return out, err + } + if err := json.Unmarshal(raw, &out); err != nil { + return DefaultBookingSettings, err + } + return out, nil +} + +func (s *Storage) SetBookingSettings(ctx context.Context, b BookingSettings) error { + return s.AdminSetSettings(ctx, map[string]any{"booking": b}) +} + +// ConfirmItem — запись с сайта, ждущая решения оператора, со всем, что нужно для лида в Битриксе. +type ConfirmItem struct { + ID int64 + Start time.Time + CreatedAt time.Time + Patient string + Phone string + DoctorOnec string + DoctorName string + Branch string + ServiceName string + OnecID string + BitrixLeadID string + Error string // почему запись не прошла (для заявок без окна в 1С) +} + +const confirmItemSelect = ` + SELECT a.id, a.start_at, a.created_at, a.patient_name, a.patient_phone, a.doctor_onec_id, + COALESCE(NULLIF(o.full_name, ''), a.doctor_onec_id), COALESCE(NULLIF(b.address, ''), b.name, ''), + COALESCE(sv.name, ''), a.onec_appointment_id, a.bitrix_lead_id, a.error + FROM onec_appointments a + LEFT JOIN onec_doctors o ON o.onec_id = a.doctor_onec_id + LEFT JOIN onec_branches b ON b.onec_id = a.branch_onec_id + LEFT JOIN onec_services sv ON sv.doctor_onec_id = a.doctor_onec_id AND sv.onec_id = a.service_onec_id` + +// ConfirmQueue — записи, которые занимают окно в 1С и ещё ждут подтверждения. +func (s *Storage) ConfirmQueue(ctx context.Context, limit int) ([]ConfirmItem, error) { + return s.confirmItems(ctx, confirmItemSelect+` + WHERE a.confirm_state = 'waiting' AND a.status = 'booked' + ORDER BY a.id LIMIT $1`, limit) +} + +// LeadOnlyQueue — заявки, которые не удалось записать в 1С: их надо передать оператору лидом, +// чтобы он записал пациента вручную. confirm_state: lead_only — ждёт отправки, lead_sent — лид создан. +func (s *Storage) LeadOnlyQueue(ctx context.Context, limit int) ([]ConfirmItem, error) { + return s.confirmItems(ctx, confirmItemSelect+` + WHERE a.confirm_state = 'lead_only' AND a.status = 'failed' + ORDER BY a.id LIMIT $1`, limit) +} + +// MarkLeadOnly ставит несостоявшуюся запись в очередь на отправку в Битрикс24. +func (s *Storage) MarkLeadOnly(ctx context.Context, id int64) error { + _, err := s.pool.Exec(ctx, `UPDATE onec_appointments SET confirm_state = 'lead_only', updated_at = now() + WHERE id = $1 AND status = 'failed' AND confirm_state = ''`, id) + return err +} + +// LeadOnlySent — лид для несостоявшейся записи создан, дальше заявку ведёт оператор. +func (s *Storage) LeadOnlySent(ctx context.Context, id int64, leadID string) error { + _, err := s.pool.Exec(ctx, `UPDATE onec_appointments SET bitrix_lead_id = $2, confirm_state = 'lead_sent', + confirm_error = '', updated_at = now() WHERE id = $1 AND confirm_state = 'lead_only'`, id, leadID) + return err +} + +func (s *Storage) confirmItems(ctx context.Context, query string, limit int) ([]ConfirmItem, error) { + rows, err := s.pool.Query(ctx, query, limit) + if err != nil { + return nil, err + } + defer rows.Close() + out := []ConfirmItem{} + for rows.Next() { + var c ConfirmItem + if err := rows.Scan(&c.ID, &c.Start, &c.CreatedAt, &c.Patient, &c.Phone, &c.DoctorOnec, &c.DoctorName, + &c.Branch, &c.ServiceName, &c.OnecID, &c.BitrixLeadID, &c.Error); err != nil { + return nil, err + } + out = append(out, c) + } + return out, rows.Err() +} + +// StartBookingConfirm ставит успешную запись в очередь на подтверждение оператором. +func (s *Storage) StartBookingConfirm(ctx context.Context, id int64) error { + _, err := s.pool.Exec(ctx, `UPDATE onec_appointments SET confirm_state = 'waiting', updated_at = now() + WHERE id = $1 AND status = 'booked' AND confirm_state = ''`, id) + return err +} + +func (s *Storage) SetBitrixLead(ctx context.Context, id int64, leadID string) error { + _, err := s.pool.Exec(ctx, `UPDATE onec_appointments SET bitrix_lead_id = $2, confirm_error = '', updated_at = now() + WHERE id = $1`, id, leadID) + return err +} + +// SetConfirmError запоминает последнюю неудачу (Битрикс или 1С недоступны): попытка повторится. +func (s *Storage) SetConfirmError(ctx context.Context, id int64, text string) error { + _, err := s.pool.Exec(ctx, `UPDATE onec_appointments SET confirm_error = $2, updated_at = now() WHERE id = $1`, id, text) + return err +} + +// FinishBookingConfirm закрывает ожидание. Для отмен (cancelled, expired) запись помечается отменённой. +// Условие confirm_state = 'waiting' защищает от гонки с вебхуком 1С и повторной обработки. +func (s *Storage) FinishBookingConfirm(ctx context.Context, id int64, state string) error { + _, err := s.pool.Exec(ctx, ` + UPDATE onec_appointments SET confirm_state = $2::text, confirm_error = '', + confirmed_at = CASE WHEN $2::text = 'confirmed' THEN now() ELSE confirmed_at END, + status = CASE WHEN $2::text IN ('cancelled', 'expired') THEN 'cancelled' ELSE status END, + updated_at = now() + WHERE id = $1 AND confirm_state = 'waiting'`, id, state) + return err +} + +// WaitingForDoctor — неподтверждённые записи к врачу с приёмом в [from, to). +func (s *Storage) WaitingForDoctor(ctx context.Context, doctorOnecID string, from, to time.Time) (int, error) { + var n int + err := s.pool.QueryRow(ctx, `SELECT count(*) FROM onec_appointments + WHERE doctor_onec_id = $1 AND confirm_state = 'waiting' AND status = 'booked' + AND start_at >= $2 AND start_at < $3`, doctorOnecID, from, to).Scan(&n) + return n, err +} + +// SiteBookingsSince — попытки онлайн-записи с сайта (любой исход) начиная с since: всплеск — признак бота. +func (s *Storage) SiteBookingsSince(ctx context.Context, since time.Time) (int, error) { + var n int + err := s.pool.QueryRow(ctx, `SELECT count(*) FROM onec_appointments WHERE created_at >= $1`, since).Scan(&n) + return n, err +} + +// ActiveBookingsForPhone — будущие записи с сайта на номер (сравнение по последним 10 цифрам). +func (s *Storage) ActiveBookingsForPhone(ctx context.Context, phone10 string) (int, error) { + var n int + err := s.pool.QueryRow(ctx, `SELECT count(*) FROM onec_appointments + WHERE status = 'booked' AND start_at > now() + AND right(regexp_replace(patient_phone, '[^0-9]', '', 'g'), 10) = $1`, phone10).Scan(&n) + return n, err +} diff --git a/internal/storage/booking_confirm_test.go b/internal/storage/booking_confirm_test.go new file mode 100644 index 0000000..79e3579 --- /dev/null +++ b/internal/storage/booking_confirm_test.go @@ -0,0 +1,22 @@ +package storage + +import ( + "encoding/json" + "testing" +) + +// Настройки, сохранённые до появления лимитов, получают значения лимитов по умолчанию. +func TestOldBookingSettingsKeepDefaultLimits(t *testing.T) { + out := DefaultBookingSettings + if err := json.Unmarshal([]byte(`{"phone_check":false,"autocancel_hours":12,"cancel_before_hours":2}`), &out); err != nil { + t.Fatal(err) + } + if out.AutoCancelHours != 12 || out.MaxPerDoctorDay != 3 || out.MaxPerHour != 10 || out.MaxActivePerPhone != 3 { + t.Errorf("слияние с умолчаниями: %+v", out) + } + out = DefaultBookingSettings + _ = json.Unmarshal([]byte(`{"max_per_doctor_day":0}`), &out) + if out.MaxPerDoctorDay != 0 { + t.Error("явный 0 должен выключать правило") + } +} diff --git a/internal/storage/conflict.go b/internal/storage/conflict.go new file mode 100644 index 0000000..28f70a8 --- /dev/null +++ b/internal/storage/conflict.go @@ -0,0 +1,27 @@ +package storage + +import ( + "errors" + "fmt" + + "github.com/jackc/pgx/v5/pgconn" +) + +// ErrConflict — нарушена уникальность: например, адрес (slug) уже занят другой записью. +var ErrConflict = errors.New("storage: conflict") + +// ErrBadReference — ссылка на несуществующую запись: удалённую услугу, файл и т. п. +var ErrBadReference = errors.New("storage: bad reference") + +func wrapConflict(err error) error { + var pg *pgconn.PgError + if errors.As(err, &pg) { + switch pg.Code { + case "23505": + return fmt.Errorf("%w: %s", ErrConflict, pg.ConstraintName) + case "23503": + return fmt.Errorf("%w: %s", ErrBadReference, pg.ConstraintName) + } + } + return err +} diff --git a/internal/storage/content.go b/internal/storage/content.go new file mode 100644 index 0000000..05d3dd2 --- /dev/null +++ b/internal/storage/content.go @@ -0,0 +1,367 @@ +package storage + +import ( + "context" + + "dana/internal/domain" +) + +// ---------- Публикации: новости, статьи, акции ---------- + +// Posts возвращает публикации заданного вида. Для акций отсеиваются просроченные. +func (s *Storage) Posts(ctx context.Context, kind string, limit int) ([]domain.Post, error) { + if limit <= 0 || limit > 100 { + limit = 20 + } + rows, err := s.pool.Query(ctx, ` + SELECT p.id, p.kind, p.slug, p.title, p.lead, COALESCE(m.path, ''), + p.starts_at, p.ends_at, p.published_at, + p.seo_title, p.seo_description, p.seo_h1, + p.discount_percent, p.terms, COALESCE(c.slug, '') + FROM posts p + LEFT JOIN media m ON m.id = p.image_id + LEFT JOIN checkups c ON c.id = p.checkup_id AND c.published AND c.deleted_at IS NULL + WHERE p.published AND p.deleted_at IS NULL + AND ($1 = '' OR p.kind = $1) + AND (p.kind <> 'promo' OR p.ends_at IS NULL OR p.ends_at >= current_date) + ORDER BY p.published_at DESC + LIMIT $2`, kind, limit) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Post + for rows.Next() { + var p domain.Post + if err := rows.Scan(&p.ID, &p.Kind, &p.Slug, &p.Title, &p.Lead, &p.Image, + &p.StartsAt, &p.EndsAt, &p.PublishedAt, + &p.SEO.Title, &p.SEO.Description, &p.SEO.H1, + &p.DiscountPercent, &p.Terms, &p.CheckupSlug); err != nil { + return nil, err + } + out = append(out, p) + } + return out, rows.Err() +} + +func (s *Storage) PostBySlug(ctx context.Context, kind, slug string) (*domain.Post, error) { + var p domain.Post + err := s.pool.QueryRow(ctx, ` + SELECT p.id, p.kind, p.slug, p.title, p.lead, p.blocks, COALESCE(m.path, ''), + p.starts_at, p.ends_at, p.published_at, + p.seo_title, p.seo_description, p.seo_h1, + p.discount_percent, p.terms, COALESCE(c.slug, '') + FROM posts p + LEFT JOIN media m ON m.id = p.image_id + LEFT JOIN checkups c ON c.id = p.checkup_id AND c.published AND c.deleted_at IS NULL + WHERE p.kind = $1 AND p.slug = $2 AND p.published AND p.deleted_at IS NULL`, + kind, slug).Scan(&p.ID, &p.Kind, &p.Slug, &p.Title, &p.Lead, &p.Blocks, &p.Image, + &p.StartsAt, &p.EndsAt, &p.PublishedAt, + &p.SEO.Title, &p.SEO.Description, &p.SEO.H1, + &p.DiscountPercent, &p.Terms, &p.CheckupSlug) + if err != nil { + return nil, wrapNotFound(err) + } + return &p, nil +} + +// ---------- Отзывы ---------- + +// Reviews отдаёт только опубликованные (прошедшие модерацию) отзывы. +func (s *Storage) Reviews(ctx context.Context, doctorSlug string, limit int) ([]domain.Review, error) { + if limit <= 0 || limit > 200 { + limit = 50 + } + rows, err := s.pool.Query(ctx, ` + SELECT r.id, r.author_name, COALESCE(m.path, ''), r.rating, r.text, r.video_url, + COALESCE(d.slug, ''), COALESCE(d.last_name || ' ' || d.first_name, ''), + r.source, r.reply, r.created_at + FROM reviews r + LEFT JOIN media m ON m.id = r.author_photo_id + LEFT JOIN doctors d ON d.id = r.doctor_id + WHERE r.status = 'published' + AND ($1 = '' OR d.slug = $1) + ORDER BY r.created_at DESC, r.id DESC + LIMIT $2`, doctorSlug, limit) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Review + for rows.Next() { + var r domain.Review + if err := rows.Scan(&r.ID, &r.AuthorName, &r.AuthorPhoto, &r.Rating, &r.Text, &r.VideoURL, + &r.DoctorSlug, &r.DoctorName, &r.Source, &r.Reply, &r.CreatedAt); err != nil { + return nil, err + } + out = append(out, r) + } + return out, rows.Err() +} + +// ---------- Вопрос-ответ ---------- + +func (s *Storage) FAQ(ctx context.Context, servicePath string) ([]domain.FAQItem, error) { + rows, err := s.pool.Query(ctx, ` + SELECT f.id, f.question, f.answer, f.service_id + FROM faq f + LEFT JOIN services s ON s.id = f.service_id + WHERE f.published + AND ($1 = '' OR s.path = $1) + ORDER BY f.sort, f.id`, servicePath) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.FAQItem + for rows.Next() { + var f domain.FAQItem + if err := rows.Scan(&f.ID, &f.Question, &f.Answer, &f.ServiceID); err != nil { + return nil, err + } + out = append(out, f) + } + return out, rows.Err() +} + +// ---------- Документы: лицензии, сертификаты, надзорные органы ---------- + +func (s *Storage) Documents(ctx context.Context, kind string) ([]domain.Document, error) { + rows, err := s.pool.Query(ctx, ` + SELECT d.id, d.kind, d.title, d.body, + COALESCE(f.path, ''), COALESCE(p.path, ''), d.issued_at + FROM documents d + LEFT JOIN media f ON f.id = d.file_id + LEFT JOIN media p ON p.id = d.preview_id + WHERE d.published AND ($1 = '' OR d.kind = $1) + ORDER BY d.sort, d.id`, kind) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Document + for rows.Next() { + var d domain.Document + if err := rows.Scan(&d.ID, &d.Kind, &d.Title, &d.Body, + &d.File, &d.Preview, &d.IssuedAt); err != nil { + return nil, err + } + out = append(out, d) + } + return out, rows.Err() +} + +// ---------- Партнёры ---------- + +func (s *Storage) Partners(ctx context.Context) ([]domain.Partner, error) { + rows, err := s.pool.Query(ctx, ` + SELECT p.id, p.name, p.url, COALESCE(m.path, '') + FROM partners p + LEFT JOIN media m ON m.id = p.logo_id + WHERE p.published + ORDER BY p.sort, p.name`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Partner + for rows.Next() { + var p domain.Partner + if err := rows.Scan(&p.ID, &p.Name, &p.URL, &p.Logo); err != nil { + return nil, err + } + out = append(out, p) + } + return out, rows.Err() +} + +// ---------- Вакансии ---------- + +func (s *Storage) Vacancies(ctx context.Context) ([]domain.Vacancy, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, slug, title, description, duties, requirements, conditions, salary, branch_id + FROM vacancies + WHERE published + ORDER BY sort, title`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Vacancy + for rows.Next() { + var v domain.Vacancy + if err := rows.Scan(&v.ID, &v.Slug, &v.Title, &v.Description, &v.Duties, + &v.Requirements, &v.Conditions, &v.Salary, &v.BranchID); err != nil { + return nil, err + } + out = append(out, v) + } + return out, rows.Err() +} + +// ---------- Филиалы ---------- + +func (s *Storage) Branches(ctx context.Context) ([]domain.Branch, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, slug, name, address, phones, work_hours, lat, lon + FROM branches + WHERE published + ORDER BY sort, name`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Branch + for rows.Next() { + var b domain.Branch + if err := rows.Scan(&b.ID, &b.Slug, &b.Name, &b.Address, + &b.Phones, &b.WorkHours, &b.Lat, &b.Lon); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} + +// ---------- Чек-апы ---------- + +func (s *Storage) Checkups(ctx context.Context, audience string) ([]domain.Checkup, error) { + rows, err := s.pool.Query(ctx, ` + SELECT c.id, c.slug, c.audience, c.name, c.lead, c.price, c.old_price, + COALESCE(m.path, ''), c.featured, + c.seo_title, c.seo_description, c.seo_h1 + FROM checkups c + LEFT JOIN media m ON m.id = c.image_id + WHERE c.published AND c.deleted_at IS NULL + AND ($1 = '' OR c.audience = $1) + ORDER BY c.sort, c.name`, audience) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Checkup + for rows.Next() { + var c domain.Checkup + if err := rows.Scan(&c.ID, &c.Slug, &c.Audience, &c.Name, &c.Lead, + &c.Price, &c.OldPrice, &c.Image, &c.Featured, + &c.SEO.Title, &c.SEO.Description, &c.SEO.H1); err != nil { + return nil, err + } + out = append(out, c) + } + return out, rows.Err() +} + +func (s *Storage) CheckupBySlug(ctx context.Context, slug string) (*domain.Checkup, error) { + var c domain.Checkup + err := s.pool.QueryRow(ctx, ` + SELECT c.id, c.slug, c.audience, c.name, c.lead, c.blocks, c.price, c.old_price, + COALESCE(m.path, ''), c.featured, + c.seo_title, c.seo_description, c.seo_h1 + FROM checkups c + LEFT JOIN media m ON m.id = c.image_id + WHERE c.slug = $1 AND c.published AND c.deleted_at IS NULL`, slug). + Scan(&c.ID, &c.Slug, &c.Audience, &c.Name, &c.Lead, &c.Blocks, + &c.Price, &c.OldPrice, &c.Image, &c.Featured, + &c.SEO.Title, &c.SEO.Description, &c.SEO.H1) + if err != nil { + return nil, wrapNotFound(err) + } + + itemRows, err := s.pool.Query(ctx, ` + SELECT name FROM checkup_items WHERE checkup_id = $1 ORDER BY sort, id`, c.ID) + if err != nil { + return nil, err + } + defer itemRows.Close() + for itemRows.Next() { + var name string + if err := itemRows.Scan(&name); err != nil { + return nil, err + } + c.Items = append(c.Items, name) + } + return &c, itemRows.Err() +} + +// ---------- Произвольные страницы ---------- + +func (s *Storage) PageBySlug(ctx context.Context, slug string) (*domain.Page, error) { + var p domain.Page + err := s.pool.QueryRow(ctx, ` + SELECT id, slug, title, blocks, seo_title, seo_description, seo_h1 + FROM pages WHERE slug = $1 AND published`, slug). + Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, + &p.SEO.Title, &p.SEO.Description, &p.SEO.H1) + if err != nil { + return nil, wrapNotFound(err) + } + return &p, nil +} + +// ---------- Галерея ---------- + +func (s *Storage) Gallery(ctx context.Context) ([]domain.GalleryAlbum, error) { + rows, err := s.pool.Query(ctx, ` + SELECT a.id, a.slug, a.title, + COALESCE(p.id, 0), COALESCE(m.path, ''), COALESCE(p.caption, '') + FROM gallery_albums a + LEFT JOIN gallery_photos p ON p.album_id = a.id + LEFT JOIN media m ON m.id = p.media_id + WHERE a.published + ORDER BY a.sort, a.title, p.sort, p.id`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.GalleryAlbum + byID := map[int64]int{} // album id -> индекс в out + for rows.Next() { + var a domain.GalleryAlbum + var ph domain.GalleryPhoto + if err := rows.Scan(&a.ID, &a.Slug, &a.Title, &ph.ID, &ph.Path, &ph.Caption); err != nil { + return nil, err + } + idx, seen := byID[a.ID] + if !seen { + out = append(out, a) + idx = len(out) - 1 + byID[a.ID] = idx + } + if ph.ID != 0 { + out[idx].Photos = append(out[idx].Photos, ph) + } + } + return out, rows.Err() +} + +// ---------- Настройки сайта ---------- + +// Settings отдаёт публичные настройки как плоскую карту key -> value. +func (s *Storage) Settings(ctx context.Context) (map[string]any, error) { + rows, err := s.pool.Query(ctx, `SELECT key, value FROM settings ORDER BY key`) + if err != nil { + return nil, err + } + defer rows.Close() + + out := map[string]any{} + for rows.Next() { + var k string + var v any + if err := rows.Scan(&k, &v); err != nil { + return nil, err + } + out[k] = v + } + return out, rows.Err() +} diff --git a/internal/storage/dashboard.go b/internal/storage/dashboard.go new file mode 100644 index 0000000..81ee907 --- /dev/null +++ b/internal/storage/dashboard.go @@ -0,0 +1,142 @@ +package storage + +import ( + "context" + "time" +) + +// Dashboard — сводка для главного экрана админки. Только счётчики и даты, без персональных данных: +// заявки с именами и телефонами главная страница берёт отдельным запросом (он пишется в журнал). +type Dashboard struct { + Doctors int `json:"doctors"` + Services int `json:"services"` + Branches int `json:"branches"` + NewLeads int `json:"new_leads"` + LeadsToday int `json:"leads_today"` + PendingReviews int `json:"pending_reviews"` + ActivePromos int `json:"active_promos"` + Days []DashboardDay `json:"days"` + Upcoming []DashboardDate `json:"upcoming"` + Todo []DashboardTodo `json:"todo"` +} + +// DashboardDay — заявки за день: все и только записи на приём. +type DashboardDay struct { + Date string `json:"date"` + Leads int `json:"leads"` + Appointments int `json:"appointments"` +} + +// DashboardDate — что скоро начнётся или закончится: акции и слайды на главной. +type DashboardDate struct { + Kind string `json:"kind"` // promo | banner + Title string `json:"title"` + Event string `json:"event"` // starts | ends + At time.Time `json:"at"` +} + +// DashboardTodo — незакрытые дела для колокольчика в шапке. +type DashboardTodo struct { + Key string `json:"key"` + Count int `json:"count"` +} + +func (s *Storage) Dashboard(ctx context.Context, days int) (*Dashboard, error) { + var d Dashboard + err := s.pool.QueryRow(ctx, ` + SELECT + (SELECT count(*) FROM doctors WHERE published AND deleted_at IS NULL), + (SELECT count(*) FROM services WHERE published AND deleted_at IS NULL AND parent_id IS NOT NULL), + (SELECT count(*) FROM branches WHERE published), + (SELECT count(*) FROM leads WHERE status = 'new'), + (SELECT count(*) FROM leads WHERE created_at >= date_trunc('day', now() AT TIME ZONE 'Europe/Moscow') AT TIME ZONE 'Europe/Moscow'), + (SELECT count(*) FROM reviews WHERE status = 'pending'), + (SELECT count(*) FROM posts WHERE kind = 'promo' AND published AND deleted_at IS NULL + AND (starts_at IS NULL OR starts_at <= current_date) AND (ends_at IS NULL OR ends_at >= current_date))`). + Scan(&d.Doctors, &d.Services, &d.Branches, &d.NewLeads, &d.LeadsToday, &d.PendingReviews, &d.ActivePromos) + if err != nil { + return nil, err + } + + rows, err := s.pool.Query(ctx, ` + WITH days AS ( + SELECT generate_series((now() AT TIME ZONE 'Europe/Moscow')::date - ($1::int - 1), (now() AT TIME ZONE 'Europe/Moscow')::date, '1 day')::date AS day + ) + SELECT to_char(d.day, 'YYYY-MM-DD'), + count(l.id), + count(l.id) FILTER (WHERE f.code = 'appointment') + FROM days d + LEFT JOIN leads l ON (l.created_at AT TIME ZONE 'Europe/Moscow')::date = d.day + LEFT JOIN forms f ON f.id = l.form_id + GROUP BY d.day ORDER BY d.day`, days) + if err != nil { + return nil, err + } + for rows.Next() { + var day DashboardDay + if err := rows.Scan(&day.Date, &day.Leads, &day.Appointments); err != nil { + rows.Close() + return nil, err + } + d.Days = append(d.Days, day) + } + rows.Close() + if err := rows.Err(); err != nil { + return nil, err + } + + rows, err = s.pool.Query(ctx, ` + SELECT kind, title, event, at FROM ( + SELECT 'promo' AS kind, title, 'starts' AS event, starts_at::timestamptz AS at FROM posts + WHERE kind = 'promo' AND published AND deleted_at IS NULL AND starts_at > current_date + UNION ALL + SELECT 'promo', title, 'ends', ends_at::timestamptz FROM posts + WHERE kind = 'promo' AND published AND deleted_at IS NULL AND ends_at >= current_date + UNION ALL + SELECT 'banner', COALESCE(NULLIF(title, ''), 'Слайд без заголовка'), 'starts', starts_at FROM banners + WHERE published AND starts_at > now() + UNION ALL + SELECT 'banner', COALESCE(NULLIF(title, ''), 'Слайд без заголовка'), 'ends', ends_at FROM banners + WHERE published AND ends_at > now() + ) x + WHERE at <= now() + interval '60 days' + ORDER BY at LIMIT 12`) + if err != nil { + return nil, err + } + for rows.Next() { + var u DashboardDate + if err := rows.Scan(&u.Kind, &u.Title, &u.Event, &u.At); err != nil { + rows.Close() + return nil, err + } + d.Upcoming = append(d.Upcoming, u) + } + rows.Close() + if err := rows.Err(); err != nil { + return nil, err + } + + var noServices, noBranches, noPhoto, hiddenPages int + if err := s.pool.QueryRow(ctx, ` + SELECT + (SELECT count(*) FROM doctors d WHERE d.published AND d.deleted_at IS NULL + AND NOT EXISTS (SELECT 1 FROM doctor_services s WHERE s.doctor_id = d.id)), + (SELECT count(*) FROM doctors d WHERE d.published AND d.deleted_at IS NULL + AND NOT EXISTS (SELECT 1 FROM doctor_branches b WHERE b.doctor_id = d.id)), + (SELECT count(*) FROM doctors WHERE published AND deleted_at IS NULL AND photo_id IS NULL), + (SELECT count(*) FROM pages WHERE slug = 'patients' AND NOT published)`). + Scan(&noServices, &noBranches, &noPhoto, &hiddenPages); err != nil { + return nil, err + } + for _, t := range []DashboardTodo{ + {"new_leads", d.NewLeads}, {"pending_reviews", d.PendingReviews}, + {"doctors_without_services", noServices}, {"doctors_without_branches", noBranches}, + {"doctors_without_photo", noPhoto}, {"patients_page_hidden", hiddenPages}, + } { + if t.Count > 0 { + d.Todo = append(d.Todo, t) + } + } + return &d, nil +} diff --git a/internal/storage/doctor_quality.go b/internal/storage/doctor_quality.go new file mode 100644 index 0000000..af69e2f --- /dev/null +++ b/internal/storage/doctor_quality.go @@ -0,0 +1,79 @@ +package storage + +import "context" + +// DoctorQuality — то, чего нет в списке врачей админки: записи биографии по видам, +// отзывы и заявки по каждому врачу. Полноту карточки считает страница «Дашборд врачей». +type DoctorQuality struct { + ID int64 `json:"id"` + Education int `json:"education"` + Experience int `json:"experience"` + Training int `json:"training"` + Awards int `json:"awards"` + Conferences int `json:"conferences"` + ReviewsPending int `json:"reviews_pending"` + ReviewsLive int `json:"reviews_published"` + RatingSum int `json:"rating_sum"` // сумма оценок всех отзывов с оценкой + RatingCount int `json:"rating_count"` // сколько отзывов с оценкой + Leads int `json:"leads"` // заявки к врачу или со страницы врача, без спама + KidsFrom bool `json:"kids_from"` + Languages bool `json:"languages"` + PrimarySpecialty *string `json:"primary_specialty"` +} + +type DoctorQualityReport struct { + Doctors []DoctorQuality `json:"doctors"` + ReviewsPendingTotal int `json:"reviews_pending_total"` + ReviewsPendingNoDoc int `json:"reviews_pending_clinic"` // отзывы о клинике в целом + ReviewsLiveTotal int `json:"reviews_published_total"` + LeadsTotal int `json:"leads_total"` +} + +func (s *Storage) DoctorQuality(ctx context.Context) (*DoctorQualityReport, error) { + rep := &DoctorQualityReport{Doctors: []DoctorQuality{}} + rows, err := s.pool.Query(ctx, ` + SELECT d.id, + count(*) FILTER (WHERE e.kind = 'education'), + count(*) FILTER (WHERE e.kind = 'experience'), + count(*) FILTER (WHERE e.kind = 'training'), + count(*) FILTER (WHERE e.kind = 'award'), + count(*) FILTER (WHERE e.kind = 'conference'), + (SELECT count(*) FROM reviews r WHERE r.doctor_id = d.id AND r.status = 'pending'), + (SELECT count(*) FROM reviews r WHERE r.doctor_id = d.id AND r.status = 'published'), + (SELECT COALESCE(sum(r.rating), 0) FROM reviews r WHERE r.doctor_id = d.id AND r.rating IS NOT NULL AND r.status <> 'rejected'), + (SELECT count(r.rating) FROM reviews r WHERE r.doctor_id = d.id AND r.status <> 'rejected'), + (SELECT count(*) FROM leads l WHERE l.status <> 'spam' AND (l.doctor_id = d.id OR + (l.doctor_id IS NULL AND l.page_url LIKE '%/company/staff/%/' || d.slug || '/%'))), + d.accepts_kids_from IS NOT NULL, + cardinality(d.languages) > 0, + (SELECT sp.name_plural FROM doctor_specialties ds JOIN specialties sp ON sp.id = ds.specialty_id + WHERE ds.doctor_id = d.id ORDER BY ds.is_primary DESC, sp.sort, sp.id LIMIT 1) + FROM doctors d + LEFT JOIN doctor_bio_entries e ON e.doctor_id = d.id + WHERE d.deleted_at IS NULL + GROUP BY d.id + ORDER BY d.id`) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var q DoctorQuality + if err := rows.Scan(&q.ID, &q.Education, &q.Experience, &q.Training, &q.Awards, &q.Conferences, + &q.ReviewsPending, &q.ReviewsLive, &q.RatingSum, &q.RatingCount, &q.Leads, + &q.KidsFrom, &q.Languages, &q.PrimarySpecialty); err != nil { + return nil, err + } + rep.Doctors = append(rep.Doctors, q) + } + if err := rows.Err(); err != nil { + return nil, err + } + err = s.pool.QueryRow(ctx, ` + SELECT count(*) FILTER (WHERE status = 'pending'), + count(*) FILTER (WHERE status = 'pending' AND doctor_id IS NULL), + count(*) FILTER (WHERE status = 'published'), + (SELECT count(*) FROM leads WHERE status <> 'spam') + FROM reviews`).Scan(&rep.ReviewsPendingTotal, &rep.ReviewsPendingNoDoc, &rep.ReviewsLiveTotal, &rep.LeadsTotal) + return rep, err +} diff --git a/internal/storage/doctors.go b/internal/storage/doctors.go new file mode 100644 index 0000000..8f05630 --- /dev/null +++ b/internal/storage/doctors.go @@ -0,0 +1,322 @@ +package storage + +import ( + "context" + "errors" + "strconv" + "strings" + "time" + + "github.com/jackc/pgx/v5" + + "dana/internal/domain" +) + +// Число отзывов считается по опубликованным отзывам. Рейтинг из админки имеет +// приоритет, а при пустом значении используется средняя оценка отзывов. +const doctorColumns = `d.id, d.slug, d.last_name, d.first_name, d.middle_name, d.position, + d.category, d.degree, d.practice_start_year, COALESCE(m.path, ''), d.quote, + d.languages, d.accepts_kids_from, d.booking_provider, d.medflex_user, d.absent, + d.seo_title, d.seo_description, d.seo_h1, + COALESCE(d.rating_override::float8, + (SELECT round(avg(r.rating)::numeric, 1)::float8 FROM reviews r + WHERE r.doctor_id = d.id AND r.status = 'published' AND r.rating IS NOT NULL)), + (SELECT count(*) FROM reviews r + WHERE r.doctor_id = d.id AND r.status = 'published')` + +const medflexBase = "https://booking.medflex.ru/?user=" + +func scanDoctor(row pgx.Row) (domain.Doctor, error) { + var d domain.Doctor + var startYear *int + var medflexUser string + err := row.Scan(&d.ID, &d.Slug, &d.LastName, &d.FirstName, &d.MiddleName, &d.Position, + &d.Category, &d.Degree, &startYear, &d.Photo, &d.Quote, + &d.Languages, &d.KidsFrom, &d.Booking.Provider, &medflexUser, &d.Absent, + &d.SEO.Title, &d.SEO.Description, &d.SEO.H1, + &d.Rating, &d.ReviewCount) + if err != nil { + return d, err + } + d.FullName = strings.TrimSpace(strings.Join([]string{d.LastName, d.FirstName, d.MiddleName}, " ")) + if startYear != nil && *startYear > 0 { + // Стаж считается, а не хранится текстом — иначе он устаревает. + years := time.Now().Year() - *startYear + if years >= 0 { + d.Experience = &years + } + } + if d.Booking.Provider == "medflex" && medflexUser != "" { + // При переносе со старого сайта в поле попала ссылка целиком; + // через админку можно ввести и только идентификатор. + if strings.HasPrefix(medflexUser, "http://") || strings.HasPrefix(medflexUser, "https://") { + d.Booking.URL = medflexUser + } else { + d.Booking.URL = medflexBase + medflexUser + } + } + return d, nil +} + +func (s *Storage) collectDoctors(ctx context.Context, sql string, args ...any) ([]domain.Doctor, error) { + rows, err := s.pool.Query(ctx, sql, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Doctor + for rows.Next() { + d, err := scanDoctor(rows) + if err != nil { + return nil, err + } + out = append(out, d) + } + return out, rows.Err() +} + +// DoctorFilter — необязательные фильтры списка врачей. Пустое поле не фильтрует. +type DoctorFilter struct { + Specialty string // slug специальности + Service string // path услуги + Branch string // slug филиала + Query string // поиск по ФИО и должности +} + +func (s *Storage) Doctors(ctx context.Context, f DoctorFilter) ([]domain.Doctor, error) { + sql := ` + SELECT ` + doctorColumns + ` + FROM doctors d + LEFT JOIN media m ON m.id = d.photo_id + WHERE d.published AND d.deleted_at IS NULL` + var args []any + arg := func(v any) string { + args = append(args, v) + return "$" + strconv.Itoa(len(args)) + } + + if f.Specialty != "" { + sql += ` + AND EXISTS ( + SELECT 1 FROM doctor_specialties ds + JOIN specialties sp ON sp.id = ds.specialty_id + WHERE ds.doctor_id = d.id AND sp.slug = ` + arg(f.Specialty) + `)` + } + if f.Service != "" { + sql += ` + AND EXISTS ( + SELECT 1 FROM doctor_services dsv + JOIN services sv ON sv.id = dsv.service_id + WHERE dsv.doctor_id = d.id AND sv.path = ` + arg(f.Service) + `)` + } + if f.Branch != "" { + sql += ` + AND EXISTS ( + SELECT 1 FROM doctor_branches db + JOIN branches b ON b.id = db.branch_id + WHERE db.doctor_id = d.id AND b.slug = ` + arg(f.Branch) + `)` + } + if f.Query != "" { + p := arg(likePattern(f.Query)) + sql += ` + AND (concat_ws(' ', d.last_name, d.first_name, d.middle_name) ILIKE ` + p + ` ESCAPE '\' + OR d.position ILIKE ` + p + ` ESCAPE '\')` + } + sql += ` + ORDER BY d.sort, d.last_name, d.id` + + doctors, err := s.collectDoctors(ctx, sql, args...) + if err != nil { + return nil, err + } + return s.attachSpecialties(ctx, doctors) +} + +func (s *Storage) DoctorsByService(ctx context.Context, serviceID int64) ([]domain.Doctor, error) { + doctors, err := s.collectDoctors(ctx, ` + SELECT `+doctorColumns+` + FROM doctors d + JOIN doctor_services ds ON ds.doctor_id = d.id + LEFT JOIN media m ON m.id = d.photo_id + WHERE ds.service_id = $1 AND d.published AND d.deleted_at IS NULL + ORDER BY d.sort, d.last_name, d.id`, serviceID) + if err != nil { + return nil, err + } + // Cards and JSON-LD must use the same primary-specialty URL as the directory. + return s.attachSpecialties(ctx, doctors) +} + +func (s *Storage) DoctorBySlug(ctx context.Context, slug string) (*domain.Doctor, error) { + row := s.pool.QueryRow(ctx, ` + SELECT `+doctorColumns+` + FROM doctors d + LEFT JOIN media m ON m.id = d.photo_id + WHERE d.slug = $1 AND d.published AND d.deleted_at IS NULL`, slug) + + d, err := scanDoctor(row) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrNotFound + } + if err != nil { + return nil, err + } + + if err := s.pool.QueryRow(ctx, `SELECT bio FROM doctors WHERE id = $1`, d.ID).Scan(&d.Bio); err != nil { + return nil, err + } + + entries, err := s.doctorBio(ctx, d.ID) + if err != nil { + return nil, err + } + d.BioEntries = entries + + services, err := s.doctorServices(ctx, d.ID) + if err != nil { + return nil, err + } + d.Services = services + + branches, err := s.doctorBranches(ctx, d.ID) + if err != nil { + return nil, err + } + d.Branches = branches + + withSpec, err := s.attachSpecialties(ctx, []domain.Doctor{d}) + if err != nil { + return nil, err + } + return &withSpec[0], nil +} + +func (s *Storage) doctorBio(ctx context.Context, doctorID int64) ([]domain.BioEntry, error) { + rows, err := s.pool.Query(ctx, ` + SELECT kind, year_from, year_to, title, description + FROM doctor_bio_entries + WHERE doctor_id = $1 + ORDER BY sort, year_from DESC NULLS LAST, id`, doctorID) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.BioEntry + for rows.Next() { + var e domain.BioEntry + if err := rows.Scan(&e.Kind, &e.YearFrom, &e.YearTo, &e.Title, &e.Description); err != nil { + return nil, err + } + out = append(out, e) + } + return out, rows.Err() +} + +func (s *Storage) doctorServices(ctx context.Context, doctorID int64) ([]domain.DoctorService, error) { + rows, err := s.pool.Query(ctx, ` + SELECT sv.name, sv.path, sv.lead + FROM doctor_services ds + JOIN services sv ON sv.id = ds.service_id + WHERE ds.doctor_id = $1 AND sv.published AND sv.deleted_at IS NULL + ORDER BY sv.sort, sv.name, sv.id`, doctorID) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.DoctorService + for rows.Next() { + var ds domain.DoctorService + if err := rows.Scan(&ds.Name, &ds.Path, &ds.Lead); err != nil { + return nil, err + } + out = append(out, ds) + } + return out, rows.Err() +} + +func (s *Storage) doctorBranches(ctx context.Context, doctorID int64) ([]domain.DoctorBranch, error) { + rows, err := s.pool.Query(ctx, ` + SELECT b.slug, b.name, b.address + FROM doctor_branches db + JOIN branches b ON b.id = db.branch_id + WHERE db.doctor_id = $1 AND b.published + ORDER BY b.sort, b.name`, doctorID) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.DoctorBranch + for rows.Next() { + var b domain.DoctorBranch + if err := rows.Scan(&b.Slug, &b.Name, &b.Address); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} + +// attachSpecialties догружает специальности одним запросом, без N+1. +func (s *Storage) attachSpecialties(ctx context.Context, doctors []domain.Doctor) ([]domain.Doctor, error) { + if len(doctors) == 0 { + return doctors, nil + } + ids := make([]int64, len(doctors)) + index := make(map[int64]int, len(doctors)) + for i, d := range doctors { + ids[i] = d.ID + index[d.ID] = i + } + + rows, err := s.pool.Query(ctx, ` + SELECT ds.doctor_id, sp.id, sp.slug, sp.name, sp.name_plural + FROM doctor_specialties ds + JOIN specialties sp ON sp.id = ds.specialty_id + WHERE ds.doctor_id = ANY($1) AND sp.published + ORDER BY ds.doctor_id, ds.is_primary DESC, sp.sort, sp.id`, ids) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var doctorID int64 + var sp domain.Specialty + if err := rows.Scan(&doctorID, &sp.ID, &sp.Slug, &sp.Name, &sp.NamePlural); err != nil { + return nil, err + } + if i, ok := index[doctorID]; ok { + doctors[i].Specialties = append(doctors[i].Specialties, sp) + } + } + return doctors, rows.Err() +} + +func (s *Storage) Specialties(ctx context.Context) ([]domain.Specialty, error) { + rows, err := s.pool.Query(ctx, ` + SELECT sp.id, sp.slug, sp.name, sp.name_plural, COUNT(d.id) + FROM specialties sp + LEFT JOIN doctor_specialties ds ON ds.specialty_id = sp.id + LEFT JOIN doctors d ON d.id = ds.doctor_id AND d.published AND d.deleted_at IS NULL + WHERE sp.published + GROUP BY sp.id + ORDER BY sp.sort, sp.name_plural, sp.id`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []domain.Specialty + for rows.Next() { + var sp domain.Specialty + if err := rows.Scan(&sp.ID, &sp.Slug, &sp.Name, &sp.NamePlural, &sp.Count); err != nil { + return nil, err + } + out = append(out, sp) + } + return out, rows.Err() +} diff --git a/internal/storage/lead_notice.go b/internal/storage/lead_notice.go new file mode 100644 index 0000000..1a88dfd --- /dev/null +++ b/internal/storage/lead_notice.go @@ -0,0 +1,43 @@ +package storage + +import ( + "context" + "encoding/json" + + "dana/internal/domain" +) + +// LeadNotice собирает заявку для уведомления: одним запросом вместе с названиями +// формы, услуги, врача и филиала, чтобы администратору не пришлось искать их по id. +func (s *Storage) LeadNotice(ctx context.Context, id int64) (domain.LeadNotice, error) { + var ( + n domain.LeadNotice + recipients []byte + payload []byte + ) + err := s.pool.QueryRow(ctx, ` + SELECT l.id, + COALESCE(f.code, ''), COALESCE(f.title, ''), COALESCE(f.recipients, '[]'::jsonb), + l.name, l.phone, l.email, + COALESCE(sv.name, ''), + concat_ws(' ', d.last_name, d.first_name, NULLIF(d.middle_name, '')), + COALESCE(b.address, ''), + l.message, l.payload, l.page_url, l.consent_at + FROM leads l + LEFT JOIN forms f ON f.id = l.form_id + LEFT JOIN services sv ON sv.id = l.service_id + LEFT JOIN doctors d ON d.id = l.doctor_id + LEFT JOIN branches b ON b.id = l.branch_id + WHERE l.id = $1`, id). + Scan(&n.ID, &n.FormCode, &n.FormTitle, &recipients, + &n.Name, &n.Phone, &n.Email, + &n.Service, &n.Doctor, &n.Branch, + &n.Message, &payload, &n.PageURL, &n.CreatedAt) + if err != nil { + return n, wrapNotFound(err) + } + // Битые JSON-поля не должны мешать уведомлению: заявка уже сохранена, отправим без них. + _ = json.Unmarshal(recipients, &n.Recipients) + _ = json.Unmarshal(payload, &n.Extra) + return n, nil +} diff --git a/internal/storage/leads.go b/internal/storage/leads.go new file mode 100644 index 0000000..0d79f93 --- /dev/null +++ b/internal/storage/leads.go @@ -0,0 +1,106 @@ +package storage + +import ( + "context" + "encoding/json" + "errors" + "net/netip" + + "dana/internal/domain" +) + +// ErrFormNotFound — формы с таким кодом нет или она выключена. +var ErrFormNotFound = errors.New("form not found") + +// FormByCode отдаёт описание формы вместе с полями — фронт по нему строит форму, +// а сервер по нему же проверяет присланное. +func (s *Storage) FormByCode(ctx context.Context, code string) (*domain.Form, error) { + var f domain.Form + err := s.pool.QueryRow(ctx, ` + SELECT id, code, title, success_text + FROM forms WHERE code = $1 AND published`, code). + Scan(&f.ID, &f.Code, &f.Title, &f.SuccessText) + if errors.Is(wrapNotFound(err), ErrNotFound) { + return nil, ErrFormNotFound + } + if err != nil { + return nil, err + } + + rows, err := s.pool.Query(ctx, ` + SELECT code, label, type, required, options + FROM form_fields WHERE form_id = $1 ORDER BY sort, id`, f.ID) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var ff domain.FormField + if err := rows.Scan(&ff.Code, &ff.Label, &ff.Type, &ff.Required, &ff.Options); err != nil { + return nil, err + } + f.Fields = append(f.Fields, ff) + } + return &f, rows.Err() +} + +// Forms отдаёт все опубликованные формы (для админки и для фронта разом). +func (s *Storage) Forms(ctx context.Context) ([]domain.Form, error) { + rows, err := s.pool.Query(ctx, ` + SELECT code FROM forms WHERE published ORDER BY id`) + if err != nil { + return nil, err + } + var codes []string + for rows.Next() { + var c string + if err := rows.Scan(&c); err != nil { + rows.Close() + return nil, err + } + codes = append(codes, c) + } + rows.Close() + if err := rows.Err(); err != nil { + return nil, err + } + + out := make([]domain.Form, 0, len(codes)) + for _, c := range codes { + f, err := s.FormByCode(ctx, c) + if err != nil { + return nil, err + } + out = append(out, *f) + } + return out, nil +} + +// CreateLead сохраняет заявку с сайта. Согласие на обработку персональных данных +// фиксируется временем записи — без него заявку принимать нельзя (152-ФЗ). +func (s *Storage) CreateLead(ctx context.Context, formID *int64, in domain.LeadInput, ip, userAgent string) (int64, error) { + extra, err := json.Marshal(in.Extra) + if err != nil { + return 0, err + } + utm, err := json.Marshal(in.UTM) + if err != nil { + return 0, err + } + + // inet не принимает пустую строку и мусор — кладём NULL, если адрес не разобрался. + var addr any + if parsed, err := netip.ParseAddr(ip); err == nil { + addr = parsed.String() + } + + var id int64 + err = s.pool.QueryRow(ctx, ` + INSERT INTO leads (form_id, name, phone, email, service_id, doctor_id, branch_id, + message, payload, page_url, utm, ip, user_agent, consent_at) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13, now()) + RETURNING id`, + formID, in.Name, in.Phone, in.Email, in.ServiceID, in.DoctorID, in.BranchID, + in.Message, extra, in.PageURL, utm, addr, userAgent).Scan(&id) + return id, err +} diff --git a/internal/storage/media.go b/internal/storage/media.go new file mode 100644 index 0000000..c3e95f8 --- /dev/null +++ b/internal/storage/media.go @@ -0,0 +1,33 @@ +package storage + +import "context" + +// CreateMedia регистрирует уже сохранённый на диск файл. width/height могут быть +// nil, если размер картинки определить не удалось. +func (s *Storage) CreateMedia(ctx context.Context, path, mime string, size int64, + width, height *int, alt string) (int64, error) { + var id int64 + err := s.pool.QueryRow(ctx, ` + INSERT INTO media (path, mime, size, width, height, alt) + VALUES ($1,$2,$3,$4,$5,$6) RETURNING id`, + path, mime, size, width, height, alt).Scan(&id) + return id, err +} + +// CreateSiteReview кладёт отзыв, оставленный на сайте, в очередь модерации. +// На сайте он появится только после публикации в админке. +func (s *Storage) CreateSiteReview(ctx context.Context, author, text string, + rating *int, doctorID *int64) error { + _, err := s.pool.Exec(ctx, ` + INSERT INTO reviews (author_name, rating, text, doctor_id, source, status) + VALUES ($1, $2, $3, $4, 'site', 'pending')`, + author, rating, text, doctorID) + return err +} + +// MediaMime отдаёт тип файла из медиатеки: админка проверяет, что в поле картинки не попал PDF. +func (s *Storage) MediaMime(ctx context.Context, id int64) (string, error) { + var mime string + err := s.pool.QueryRow(ctx, `SELECT mime FROM media WHERE id=$1`, id).Scan(&mime) + return mime, wrapNotFound(err) +} diff --git a/internal/storage/migrations/0001_core.sql b/internal/storage/migrations/0001_core.sql new file mode 100644 index 0000000..c511620 --- /dev/null +++ b/internal/storage/migrations/0001_core.sql @@ -0,0 +1,203 @@ +-- Ядро: настройки, медиа, филиалы, услуги, врачи, цены, чек-апы. + +CREATE TABLE settings ( + key text PRIMARY KEY, + value jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE media ( + id bigserial PRIMARY KEY, + path text NOT NULL UNIQUE, + mime text NOT NULL, + size bigint NOT NULL, + width int, + height int, + alt text NOT NULL DEFAULT '', + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE branches ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + name text NOT NULL, + address text NOT NULL DEFAULT '', + phones jsonb NOT NULL DEFAULT '[]'::jsonb, + work_hours jsonb NOT NULL DEFAULT '[]'::jsonb, + lat numeric(9, 6), + lon numeric(9, 6), + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +-- Направления и услуги. path — полный путь без ведущего слэша, +-- например "ginekologiya/kolposkopiya"; он же основа URL. +CREATE TABLE services ( + id bigserial PRIMARY KEY, + parent_id bigint REFERENCES services (id) ON DELETE RESTRICT, + slug text NOT NULL, + path text NOT NULL UNIQUE, + name text NOT NULL, + short_name text NOT NULL DEFAULT '', + lead text NOT NULL DEFAULT '', + blocks jsonb NOT NULL DEFAULT '[]'::jsonb, + image_id bigint REFERENCES media (id) ON DELETE SET NULL, + icon text NOT NULL DEFAULT '', + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + show_in_menu boolean NOT NULL DEFAULT true, + seo_title text NOT NULL DEFAULT '', + seo_description text NOT NULL DEFAULT '', + seo_h1 text NOT NULL DEFAULT '', + og_image_id bigint REFERENCES media (id) ON DELETE SET NULL, + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + deleted_at timestamptz +); + +CREATE UNIQUE INDEX services_parent_slug_idx + ON services (COALESCE(parent_id, 0), slug) WHERE deleted_at IS NULL; +CREATE INDEX services_parent_idx ON services (parent_id) WHERE deleted_at IS NULL; + +CREATE TABLE specialties ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + name text NOT NULL, -- «Гинеколог» + name_plural text NOT NULL, -- «Гинекологи» + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true +); + +CREATE TABLE doctors ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + last_name text NOT NULL, + first_name text NOT NULL, + middle_name text NOT NULL DEFAULT '', + position text NOT NULL DEFAULT '', + category text NOT NULL DEFAULT '', -- нет / вторая / первая / высшая + degree text NOT NULL DEFAULT '', + practice_start_year int, + photo_id bigint REFERENCES media (id) ON DELETE SET NULL, + quote text NOT NULL DEFAULT '', + bio text NOT NULL DEFAULT '', + languages text[] NOT NULL DEFAULT '{}', + accepts_kids_from int, -- возраст в годах, NULL = не принимает детей + booking_provider text NOT NULL DEFAULT 'none' -- none | native | medflex + CHECK (booking_provider IN ('none', 'native', 'medflex')), + medflex_user text NOT NULL DEFAULT '', + published boolean NOT NULL DEFAULT true, + show_on_home boolean NOT NULL DEFAULT false, + absent boolean NOT NULL DEFAULT false, + absent_until date, + sort int NOT NULL DEFAULT 100, + seo_title text NOT NULL DEFAULT '', + seo_description text NOT NULL DEFAULT '', + seo_h1 text NOT NULL DEFAULT '', + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + deleted_at timestamptz +); + +CREATE INDEX doctors_published_idx ON doctors (published) WHERE deleted_at IS NULL; + +CREATE TABLE doctor_specialties ( + doctor_id bigint NOT NULL REFERENCES doctors (id) ON DELETE CASCADE, + specialty_id bigint NOT NULL REFERENCES specialties (id) ON DELETE CASCADE, + is_primary boolean NOT NULL DEFAULT false, + PRIMARY KEY (doctor_id, specialty_id) +); + +CREATE TABLE doctor_services ( + doctor_id bigint NOT NULL REFERENCES doctors (id) ON DELETE CASCADE, + service_id bigint NOT NULL REFERENCES services (id) ON DELETE CASCADE, + PRIMARY KEY (doctor_id, service_id) +); + +CREATE TABLE doctor_branches ( + doctor_id bigint NOT NULL REFERENCES doctors (id) ON DELETE CASCADE, + branch_id bigint NOT NULL REFERENCES branches (id) ON DELETE CASCADE, + PRIMARY KEY (doctor_id, branch_id) +); + +-- Биография врача повторяемыми записями вместо простыни текста. +CREATE TABLE doctor_bio_entries ( + id bigserial PRIMARY KEY, + doctor_id bigint NOT NULL REFERENCES doctors (id) ON DELETE CASCADE, + kind text NOT NULL + CHECK (kind IN ('education', 'experience', 'training', 'award', 'conference')), + year_from int, + year_to int, + title text NOT NULL, + description text NOT NULL DEFAULT '', + sort int NOT NULL DEFAULT 100 +); + +CREATE INDEX doctor_bio_entries_doctor_idx ON doctor_bio_entries (doctor_id, kind); + +CREATE TABLE price_sections ( + id bigserial PRIMARY KEY, + parent_id bigint REFERENCES price_sections (id) ON DELETE RESTRICT, + slug text NOT NULL UNIQUE, + name text NOT NULL, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint +); + +CREATE TABLE price_items ( + id bigserial PRIMARY KEY, + section_id bigint NOT NULL REFERENCES price_sections (id) ON DELETE CASCADE, + service_id bigint REFERENCES services (id) ON DELETE SET NULL, + branch_id bigint REFERENCES branches (id) ON DELETE SET NULL, -- NULL = во всех филиалах + name text NOT NULL, + price numeric(12, 2) NOT NULL, + unit text NOT NULL DEFAULT '', + price_from boolean NOT NULL DEFAULT false, -- «от» + -- позиция-выброс, не участвует в расчёте «от N ₽» на карточке услуги + exclude_from_min boolean NOT NULL DEFAULT false, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX price_items_section_idx ON price_items (section_id); +CREATE INDEX price_items_service_idx ON price_items (service_id) WHERE service_id IS NOT NULL; + +CREATE TABLE checkups ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + audience text NOT NULL DEFAULT 'adults' + CHECK (audience IN ('adults', 'kids')), + name text NOT NULL, + lead text NOT NULL DEFAULT '', + blocks jsonb NOT NULL DEFAULT '[]'::jsonb, + price numeric(12, 2), + old_price numeric(12, 2), + image_id bigint REFERENCES media (id) ON DELETE SET NULL, + featured boolean NOT NULL DEFAULT false, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + seo_title text NOT NULL DEFAULT '', + seo_description text NOT NULL DEFAULT '', + seo_h1 text NOT NULL DEFAULT '', + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + deleted_at timestamptz +); + +CREATE TABLE checkup_items ( + id bigserial PRIMARY KEY, + checkup_id bigint NOT NULL REFERENCES checkups (id) ON DELETE CASCADE, + name text NOT NULL, + sort int NOT NULL DEFAULT 100 +); + +CREATE INDEX checkup_items_checkup_idx ON checkup_items (checkup_id); diff --git a/internal/storage/migrations/0002_content.sql b/internal/storage/migrations/0002_content.sql new file mode 100644 index 0000000..7d6f95b --- /dev/null +++ b/internal/storage/migrations/0002_content.sql @@ -0,0 +1,150 @@ +-- Контентные разделы: публикации, отзывы, документы, галерея, меню, страницы. + +-- Новости, статьи и акции устроены одинаково, различаются только kind. +-- У акций дополнительно работают starts_at / ends_at. +CREATE TABLE posts ( + id bigserial PRIMARY KEY, + kind text NOT NULL + CHECK (kind IN ('news', 'article', 'promo')), + slug text NOT NULL, + title text NOT NULL, + lead text NOT NULL DEFAULT '', + blocks jsonb NOT NULL DEFAULT '[]'::jsonb, + image_id bigint REFERENCES media (id) ON DELETE SET NULL, + starts_at date, + ends_at date, + published boolean NOT NULL DEFAULT true, + published_at timestamptz NOT NULL DEFAULT now(), + seo_title text NOT NULL DEFAULT '', + seo_description text NOT NULL DEFAULT '', + seo_h1 text NOT NULL DEFAULT '', + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + deleted_at timestamptz +); + +CREATE UNIQUE INDEX posts_kind_slug_idx ON posts (kind, slug) WHERE deleted_at IS NULL; +CREATE INDEX posts_kind_published_idx ON posts (kind, published, published_at DESC); + +CREATE TABLE reviews ( + id bigserial PRIMARY KEY, + author_name text NOT NULL, + author_photo_id bigint REFERENCES media (id) ON DELETE SET NULL, + rating int CHECK (rating BETWEEN 1 AND 5), + text text NOT NULL, + doctor_id bigint REFERENCES doctors (id) ON DELETE SET NULL, + service_id bigint REFERENCES services (id) ON DELETE SET NULL, + source text NOT NULL DEFAULT 'site' -- site | yandex | prodoctorov | manual + CHECK (source IN ('site', 'yandex', 'prodoctorov', 'manual')), + status text NOT NULL DEFAULT 'pending' + CHECK (status IN ('pending', 'published', 'rejected')), + reply text NOT NULL DEFAULT '', -- ответ клиники + moderated_at timestamptz, + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX reviews_status_idx ON reviews (status, created_at DESC); +CREATE INDEX reviews_doctor_idx ON reviews (doctor_id) WHERE doctor_id IS NOT NULL; + +CREATE TABLE faq ( + id bigserial PRIMARY KEY, + question text NOT NULL, + answer text NOT NULL, + service_id bigint REFERENCES services (id) ON DELETE CASCADE, -- NULL = общий вопрос + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint +); + +CREATE TABLE vacancies ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + title text NOT NULL, + description text NOT NULL DEFAULT '', + requirements text NOT NULL DEFAULT '', + conditions text NOT NULL DEFAULT '', + salary text NOT NULL DEFAULT '', + branch_id bigint REFERENCES branches (id) ON DELETE SET NULL, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +-- Лицензии, сертификаты, сведения о надзорных органах и прочие документы. +CREATE TABLE documents ( + id bigserial PRIMARY KEY, + kind text NOT NULL + CHECK (kind IN ('license', 'certificate', 'supervisor', 'other')), + title text NOT NULL, + body text NOT NULL DEFAULT '', + file_id bigint REFERENCES media (id) ON DELETE SET NULL, + preview_id bigint REFERENCES media (id) ON DELETE SET NULL, + issued_at date, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint +); + +CREATE TABLE partners ( + id bigserial PRIMARY KEY, + name text NOT NULL, + url text NOT NULL DEFAULT '', + logo_id bigint REFERENCES media (id) ON DELETE SET NULL, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true, + legacy_id bigint +); + +CREATE TABLE gallery_albums ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + title text NOT NULL, + branch_id bigint REFERENCES branches (id) ON DELETE SET NULL, + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true +); + +CREATE TABLE gallery_photos ( + id bigserial PRIMARY KEY, + album_id bigint NOT NULL REFERENCES gallery_albums (id) ON DELETE CASCADE, + media_id bigint NOT NULL REFERENCES media (id) ON DELETE CASCADE, + caption text NOT NULL DEFAULT '', + sort int NOT NULL DEFAULT 100 +); + +-- Меню редактируется в админке, а не зашито в шаблон. +CREATE TABLE menu_items ( + id bigserial PRIMARY KEY, + menu text NOT NULL DEFAULT 'main' -- main | footer | mobile + CHECK (menu IN ('main', 'footer', 'mobile')), + parent_id bigint REFERENCES menu_items (id) ON DELETE CASCADE, + title text NOT NULL, + url text NOT NULL DEFAULT '', + -- если задан service_id, ссылка строится автоматически по услуге + service_id bigint REFERENCES services (id) ON DELETE CASCADE, + icon text NOT NULL DEFAULT '', + wide boolean NOT NULL DEFAULT false, -- пункт раскрывается мегаменю + sort int NOT NULL DEFAULT 100, + published boolean NOT NULL DEFAULT true +); + +CREATE INDEX menu_items_menu_idx ON menu_items (menu, parent_id, sort); + +-- Произвольные страницы: о клинике, юридические документы, главная. +CREATE TABLE pages ( + id bigserial PRIMARY KEY, + slug text NOT NULL UNIQUE, + title text NOT NULL, + blocks jsonb NOT NULL DEFAULT '[]'::jsonb, + published boolean NOT NULL DEFAULT true, + seo_title text NOT NULL DEFAULT '', + seo_description text NOT NULL DEFAULT '', + seo_h1 text NOT NULL DEFAULT '', + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); diff --git a/internal/storage/migrations/0003_leads_admin.sql b/internal/storage/migrations/0003_leads_admin.sql new file mode 100644 index 0000000..c9eff7e --- /dev/null +++ b/internal/storage/migrations/0003_leads_admin.sql @@ -0,0 +1,108 @@ +-- Формы и заявки, редиректы со старых адресов, пользователи админки. + +CREATE TABLE forms ( + id bigserial PRIMARY KEY, + code text NOT NULL UNIQUE, -- appointment | callback | service_order | review | resume + title text NOT NULL, + success_text text NOT NULL DEFAULT '', + recipients jsonb NOT NULL DEFAULT '[]'::jsonb, -- куда слать уведомления + published boolean NOT NULL DEFAULT true +); + +CREATE TABLE form_fields ( + id bigserial PRIMARY KEY, + form_id bigint NOT NULL REFERENCES forms (id) ON DELETE CASCADE, + code text NOT NULL, + label text NOT NULL, + type text NOT NULL DEFAULT 'text' + CHECK (type IN ('text', 'textarea', 'phone', 'email', 'date', 'select', 'service', 'doctor', 'rating', 'file')), + required boolean NOT NULL DEFAULT false, + options jsonb NOT NULL DEFAULT '[]'::jsonb, + sort int NOT NULL DEFAULT 100, + UNIQUE (form_id, code) +); + +CREATE TABLE leads ( + id bigserial PRIMARY KEY, + form_id bigint REFERENCES forms (id) ON DELETE SET NULL, + -- разобранные поля, по которым строится поиск и статистика + name text NOT NULL DEFAULT '', + phone text NOT NULL DEFAULT '', + email text NOT NULL DEFAULT '', + service_id bigint REFERENCES services (id) ON DELETE SET NULL, + doctor_id bigint REFERENCES doctors (id) ON DELETE SET NULL, + branch_id bigint REFERENCES branches (id) ON DELETE SET NULL, + message text NOT NULL DEFAULT '', + -- остальные поля формы как есть + payload jsonb NOT NULL DEFAULT '{}'::jsonb, + status text NOT NULL DEFAULT 'new' + CHECK (status IN ('new', 'in_progress', 'done', 'spam')), + comment text NOT NULL DEFAULT '', + page_url text NOT NULL DEFAULT '', + utm jsonb NOT NULL DEFAULT '{}'::jsonb, + ip inet, + user_agent text NOT NULL DEFAULT '', + -- согласие на обработку персональных данных фиксируется явно + consent_at timestamptz NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX leads_created_idx ON leads (created_at DESC); +CREATE INDEX leads_status_idx ON leads (status, created_at DESC); +CREATE INDEX leads_phone_idx ON leads (phone); + +-- Старые адреса Bitrix, чтобы не потерять позиции в поиске. +CREATE TABLE redirects ( + id bigserial PRIMARY KEY, + from_path text NOT NULL UNIQUE, + to_path text NOT NULL, + code int NOT NULL DEFAULT 301 CHECK (code IN (301, 302)), + hits bigint NOT NULL DEFAULT 0, + created_at timestamptz NOT NULL DEFAULT now() +); + +-- Журнал ненайденных адресов: подсказывает, какие редиректы ещё нужны. +CREATE TABLE not_found_log ( + path text PRIMARY KEY, + hits bigint NOT NULL DEFAULT 1, + last_seen timestamptz NOT NULL DEFAULT now(), + referer text NOT NULL DEFAULT '' +); + +CREATE TABLE admin_users ( + id bigserial PRIMARY KEY, + email text NOT NULL UNIQUE, + password_hash text NOT NULL, + name text NOT NULL DEFAULT '', + role text NOT NULL DEFAULT 'editor' + CHECK (role IN ('owner', 'admin', 'editor', 'registrar')), + active boolean NOT NULL DEFAULT true, + last_login_at timestamptz, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE admin_sessions ( + token text PRIMARY KEY, + user_id bigint NOT NULL REFERENCES admin_users (id) ON DELETE CASCADE, + expires_at timestamptz NOT NULL, + ip inet, + user_agent text NOT NULL DEFAULT '', + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX admin_sessions_user_idx ON admin_sessions (user_id); + +-- Кто что менял: нужно и для разбора ошибок, и по требованиям к медданным. +CREATE TABLE audit_log ( + id bigserial PRIMARY KEY, + user_id bigint REFERENCES admin_users (id) ON DELETE SET NULL, + action text NOT NULL, -- create | update | delete | login | view_leads + entity text NOT NULL DEFAULT '', + entity_id bigint, + diff jsonb NOT NULL DEFAULT '{}'::jsonb, + ip inet, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX audit_log_created_idx ON audit_log (created_at DESC); diff --git a/internal/storage/migrations/0004_seed_forms.sql b/internal/storage/migrations/0004_seed_forms.sql new file mode 100644 index 0000000..3516d46 --- /dev/null +++ b/internal/storage/migrations/0004_seed_forms.sql @@ -0,0 +1,51 @@ +-- Формы сайта. На старом сайте их было 11, но заявки приходили только в пять — +-- остальные («Написать главврачу», «Отправить резюме», «Купить в 1 клик», +-- «Заказать продукт», «Оформление заказа») за всю историю не собрали ни одной, +-- поэтому здесь заводим только рабочие. + +INSERT INTO forms (code, title, success_text) VALUES + ('appointment', 'Записаться на приём', + 'Спасибо! Мы свяжемся с вами, чтобы подтвердить запись.'), + ('callback', 'Обратный звонок', + 'Спасибо! Перезвоним вам в ближайшее время.'), + ('service_order', 'Заказать услугу', + 'Спасибо! Мы свяжемся с вами и расскажем подробности.'), + ('review', 'Оставить отзыв', + 'Спасибо за отзыв! Он появится на сайте после проверки.'), + ('resume', 'Отклик на вакансию', + 'Спасибо! Мы рассмотрим ваш отклик и свяжемся с вами.'); + +INSERT INTO form_fields (form_id, code, label, type, required, sort) +SELECT f.id, v.code, v.label, v.type, v.required, v.sort +FROM forms f +JOIN (VALUES + -- запись на приём + ('appointment', 'name', 'Ваше имя', 'text', true, 10), + ('appointment', 'phone', 'Телефон', 'phone', true, 20), + ('appointment', 'service', 'Услуга', 'service', false, 30), + ('appointment', 'doctor', 'Специалист', 'doctor', false, 40), + ('appointment', 'date', 'Удобные дата и время','date', false, 50), + ('appointment', 'message', 'Комментарий', 'textarea', false, 60), + -- обратный звонок + ('callback', 'name', 'Ваше имя', 'text', true, 10), + ('callback', 'phone', 'Телефон', 'phone', true, 20), + -- заказать услугу + ('service_order', 'name', 'Ваше имя', 'text', true, 10), + ('service_order', 'phone', 'Телефон', 'phone', true, 20), + ('service_order', 'email', 'E-mail', 'email', false, 30), + ('service_order', 'service', 'Услуга', 'service', false, 40), + ('service_order', 'message', 'Сообщение', 'textarea', false, 50), + -- отзыв + ('review', 'name', 'Ваше имя', 'text', true, 10), + ('review', 'phone', 'Телефон', 'phone', true, 20), + ('review', 'rating', 'Оценка', 'rating', false, 30), + ('review', 'doctor', 'Специалист', 'doctor', false, 40), + ('review', 'message', 'Текст отзыва', 'textarea', true, 50), + -- отклик на вакансию + ('resume', 'name', 'Ваше имя', 'text', true, 10), + ('resume', 'phone', 'Телефон', 'phone', true, 20), + ('resume', 'email', 'E-mail', 'email', false, 30), + ('resume', 'message', 'О себе', 'textarea', false, 40), + ('resume', 'file', 'Резюме файлом', 'file', false, 50) +) AS v(form_code, code, label, type, required, sort) + ON v.form_code = f.code; diff --git a/internal/storage/migrations/0005_review_video.sql b/internal/storage/migrations/0005_review_video.sql new file mode 100644 index 0000000..bb69f1a --- /dev/null +++ b/internal/storage/migrations/0005_review_video.sql @@ -0,0 +1,12 @@ +-- Видео-отзывы. На старом сайте они лежали как

&fD%$}IO?|S32Hia2@hFvm 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