Compare commits

...

2 Commits

Author SHA1 Message Date
e42c62820b Add README.md 2025-06-05 03:53:54 +03:00
f884de3b3a Convert vanila javascript to vite+react 2025-06-05 03:49:43 +03:00
31 changed files with 3128 additions and 1 deletions

View File

@ -0,0 +1,6 @@
Start the Server:
Just start the main.py using vscode or pyhotn path-to-main.py
Start the Client:
cd frontend
npm run dev

Binary file not shown.

33
backend/main.py Normal file
View File

@ -0,0 +1,33 @@
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
import json
from pathlib import Path
app = FastAPI()
# CORS configuration
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Load data once
data_file = Path(__file__).resolve().parent.parent / "data" / "families.json"
with data_file.open("r", encoding="utf-8") as f:
families = json.load(f)
@app.get("/search")
def search_families(family: str = Query(..., min_length=1)):
query = family.lower()
matches = [f for f in families if query in f["family"].lower()]
return matches
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

24
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

12
frontend/README.md Normal file
View File

@ -0,0 +1,12 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

33
frontend/eslint.config.js Normal file
View File

@ -0,0 +1,33 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]

13
frontend/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/oramap-logo.svg" />
<title>Ora</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

2601
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
frontend/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"leaflet": "^1.9.4",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"vite": "^6.3.5"
}
}

View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4671C6;}
.st1{fill:#F9CFCF;}
.st2{fill:#F9A7A7;}
.st3{fill:#A4C9FF;}
.st4{fill:#3762CC;}
.st5{fill:#E0EBFC;}
.st6{fill:#6BDDDD;}
.st7{fill:#B9BEFC;}
.st8{fill:#FFEA92;}
.st9{fill:#EAA97D;}
.st10{fill:#FFEA94;}
.st11{fill:#FFE164;}
.st12{fill:#FFDC85;}
.st13{fill:#FFFFFF;}
.st14{fill:#383838;}
</style>
<g>
<g>
<path class="st3" d="M8,34.61v164.383c0,1.758,1.148,3.31,2.829,3.825L88,226.451V53.701L13.171,30.785
C10.6,29.998,8,31.921,8,34.61z"/>
</g>
<g>
<path class="st3" d="M242.829,224.868c2.571,0.787,5.171-1.136,5.171-3.825V56.66c0-1.758-1.148-3.31-2.829-3.825
C164.251,28.053,169.733,29.568,168,29.568v172.567C168.933,202.135,164.12,200.763,242.829,224.868z"/>
</g>
<g>
<path class="st3" d="M168,29.568c-1.732,0,4.024-1.599-80,24.133v172.75c84.284-25.812,79.068-24.317,80-24.317V29.568z"/>
</g>
<g>
<path class="st0" d="M32.871,49.368L32.871,49.368l-10.285-3.15C21.3,45.825,20,46.786,20,48.131v143.467
c0,0.879,0.574,1.655,1.414,1.912c64.095,19.629,51.098,15.61,66.586,20.525V65.701L32.871,49.368z"/>
</g>
<g>
<path class="st0" d="M223.129,205.65L223.129,205.65l10.285,3.13c1.286,0.391,2.586-0.564,2.586-1.9V64.329
c0-2.097,2.474-0.717-56-18.51l-12-3.784v147.386L223.129,205.65z"/>
</g>
<g>
<polygon class="st0" points="156,45.427 168,42.035 168,189.368 88,214.035 88,65.701 "/>
</g>
<g>
<path class="st6" d="M69.667,75.376c0-1.987-1.284-3.751-3.183-4.358l-8.957-2.859c-2.056-0.656-4.291,0.214-5.362,2.088
c-1.705,2.983-1.921,3.604-2.921,4.354c-0.228,0.171-12.492,9.378-12.724,9.526c-8.879,5.65-10.852,6.189-10.852,9.417
c0,8.441-0.271,9.132,0.928,10.714c6.196,8.179,6.74,9.775,9.679,9.775c2.709,0,5.495-0.441,7.012,2.446
c3.369,6.41,4.001,8.97,7.478,8.97c2.031,0,3.696,1.263,4.331,3.011l0.81,2.228c0.657,1.808,2.375,3.011,4.299,3.011
c2.98,0,6.614,0.565,7.82-3.167c2.391-7.398,4.658-3.842,7.755-5.778c2.563-1.602,4.456-3.275,7.196-1.874
c3.357,1.717,4.615,2.702,6.824,1.991c7.07-2.272,9.751-2.456,10.316-5.937c1.518-9.358,1.442-9.292,1.734-10.029l3.85-9.738
c0.91-2.302-0.176-4.911-2.452-5.886c-1.307-0.56-2.813-1.015-3.634-2.93l-1.581-3.69c-0.954-2.225-3.476-3.322-5.754-2.502
c-1.819,0.655-3.943,1.787-6.245,0.082c-5.322-3.942-6.444-5.335-9.053-4.776l-1.78,0.381
C72.352,80.459,69.667,78.289,69.667,75.376z"/>
</g>
<g>
<path class="st6" d="M233.414,208.78c1.286,0.391,2.586-0.564,2.586-1.9v-76.696c-0.084-0.186-0.18-0.367-0.294-0.536
c-6.446-9.534-4.425-7.037-9.964-12.345c-1.723-1.651-4.543-1.226-5.702,0.861c-1.333,2.398-3.195,1.873-11.06,1.873
c-1.328,0-0.39-0.327-16.362,8.918c-3.742,2.166-6.709-3.501-10.047-3.501c-3.182,0-6.074-0.508-7.053,2.521
c-2.164,6.695-1.21,5.162-4.954,8.466c-1.442,1.272-3.624,1.204-4.983-0.156l-4.932-4.932c-1.681-1.681-4.495-1.326-5.707,0.719
c-3.733,6.3-1.768,3.85-7.944,9.717c-1.018,0.967-1.389,2.432-0.954,3.767c4.431,13.609,3.132,11.783,7.557,13.75
c2.94,1.306,2.086,4.285,1.776,7.496c-0.204,2.107-2.156,3.597-4.242,3.237c-7.619-1.314-3.639-1.554-17.397,1.966
c-3.159,0.808-2.739,4.055-2.739,5.31c0,6.051-11.333,2.731-11.333,8.782c0,11.849-1.384,9.309,9.483,14.287
c1.745,0.799-0.494,1.12,38.85-11.011v0.053C231.483,208.109,220.104,204.729,233.414,208.78z"/>
</g>
<g>
<circle class="st8" cx="62.419" cy="111.87" r="6.606"/>
</g>
<g>
<path class="st5" d="M64.263,126.919l-1.293-4.83c-0.512-1.914,2.384-2.69,2.898-0.775l1.293,4.83
C67.674,128.056,64.778,128.837,64.263,126.919z"/>
</g>
<g>
<path class="st5" d="M92.24,162.988c0.131-0.712,0.75-1.229,1.473-1.229c0.013,0,1.24,0.202,2.222,0.202
c0.208,0,0.417-0.006,0.627-0.018c0,0,6.745-0.398,6.746-0.398c2.068,0,2.071,2.882,0.173,2.995
C97.066,164.919,91.729,165.773,92.24,162.988z M111.387,161.479c-0.484-0.674-0.331-1.609,0.343-2.093
c1.262-0.907,1.399-1.179,6.408-6.815c1.312-1.477,3.561,0.512,2.242,1.993C115.742,159.783,112.979,163.694,111.387,161.479z
M84.564,158.932c-0.992-1.466-1.092-1.91-4.103-9.104c-0.319-0.763,0.042-1.644,0.805-1.963c0.767-0.319,1.643,0.042,1.963,0.805
c3.059,7.308,3.048,7.442,3.82,8.582C88.155,158.883,85.68,160.579,84.564,158.932z M163.494,148.906
c0.067-0.769,0.726-1.371,1.5-1.371c0.042,0,0.084,0.002,0.126,0.005c3.237,0.279,2.595,0.281,9.121-1.12
c1.968-0.419,2.542,2.522,0.629,2.934C171.545,150.068,163.206,152.264,163.494,148.906z M145.528,148.866
c-1.963-0.17-1.726-3.143,0.256-2.989l9.667,0.832c1.887,0.164,1.769,2.995-0.125,2.995
C155.219,149.703,145.634,148.875,145.528,148.866z M125.845,145.819c3.726-1.619,6.297-1.116,10.273-0.774
c1.957,0.17,1.734,3.164-0.256,2.989c-3.54-0.305-5.745-0.801-8.822,0.536C125.219,149.36,124.035,146.603,125.845,145.819z
M193.281,147.593c-4.528-0.996-4.63-1.193-8.924-0.273c-0.105,0.023-0.212,0.034-0.317,0.034c-1.779,0-2.059-2.59-0.312-2.967
c4.896-1.048,5.529-0.75,10.196,0.277C195.839,145.084,195.233,148.032,193.281,147.593z M75.479,142.787
c-0.533-0.344-1.098-0.641-1.681-0.885c-2.961-1.239-8.512-5.805-5.868-7.457c2.252-1.402,1.902,2.547,7.024,4.689
c1.682,0.704,3.591,1.668,2.598,3.206C77.105,143.032,76.179,143.239,75.479,142.787z"/>
</g>
<g>
<path class="st2" d="M220.241,140.217L220.241,140.217c-0.781-1.445-2.585-1.984-4.031-1.203l-9.24,4.991l-4.991-9.24
c-0.781-1.445-2.585-1.984-4.031-1.204h0c-1.445,0.781-1.984,2.585-1.204,4.031l4.991,9.24l-9.24,4.991
c-1.445,0.781-1.984,2.585-1.203,4.031v0c0.781,1.445,2.585,1.984,4.031,1.203l9.24-4.991l4.991,9.24
c0.781,1.445,2.585,1.984,4.031,1.204l0,0c1.445-0.781,1.984-2.585,1.204-4.031l-4.991-9.24l9.24-4.991
C220.483,143.467,221.022,141.662,220.241,140.217z"/>
</g>
<g>
<path class="st4" d="M88,228.452c-0.196,0-0.394-0.029-0.585-0.088L10.243,204.73C7.705,203.952,6,201.647,6,198.993V34.61
c0-1.923,0.887-3.681,2.433-4.824c1.545-1.143,3.485-1.476,5.325-0.913L88.585,51.79C89.426,52.047,90,52.823,90,53.702v172.75
c0,0.634-0.301,1.231-0.811,1.608C88.841,228.317,88.423,228.452,88,228.452z M11.994,32.608c-0.421,0-0.833,0.134-1.183,0.394
C10.295,33.383,10,33.969,10,34.61v164.383c0,0.885,0.568,1.653,1.415,1.913L86,223.748V55.181L12.586,32.697
C12.39,32.638,12.191,32.608,11.994,32.608z"/>
</g>
<g>
<path class="st4" d="M244.018,227.048c-0.591,0-1.188-0.089-1.775-0.269c-21.08-6.456-36.168-11.084-46.981-14.401
c-20.53-6.297-26.108-8.008-27.129-8.247c-0.04,0.002-0.083,0.003-0.132,0.003c-1.104,0-2-0.896-2-2V29.568c0-1.104,0.896-2,2-2
l0.114-0.005c1.218-0.084,1.214-0.083,33.118,9.708c10.75,3.299,25.165,7.723,44.525,13.652c2.538,0.778,4.243,3.083,4.243,5.737
v164.383c0,1.923-0.887,3.681-2.433,4.824C246.515,226.646,245.28,227.048,244.018,227.048z M170,200.53
c2.671,0.735,9.202,2.739,26.434,8.025c10.813,3.316,25.901,7.945,46.98,14.4c0,0,0,0,0,0c0.613,0.186,1.259,0.076,1.774-0.304
c0.516-0.381,0.811-0.967,0.811-1.608V56.66c0-0.885-0.568-1.653-1.415-1.913c-19.361-5.929-33.776-10.353-44.527-13.652
c-19.055-5.848-27.015-8.291-30.059-9.149V200.53z"/>
</g>
<g>
<path class="st4" d="M88,228.452c-0.423,0-0.841-0.134-1.189-0.392c-0.51-0.377-0.811-0.974-0.811-1.608V53.702
c0-0.879,0.574-1.655,1.415-1.912c20.108-6.159,35.075-10.751,46.233-14.175c33.044-10.141,33.043-10.146,34.25-10.051L168,27.568
c1.104,0,2,0.896,2,2v172.566c0,1.104-0.896,2-2,2c-0.048,0-0.091-0.001-0.131-0.003c-1.085,0.258-7.074,2.095-29.002,8.821
c-11.57,3.549-27.719,8.501-50.281,15.411C88.394,228.423,88.196,228.452,88,228.452z M90,55.181v168.567
c21.216-6.498,36.571-11.208,47.694-14.619c18.561-5.692,25.508-7.823,28.306-8.593V31.948c-3.113,0.877-11.343,3.403-31.179,9.49
C123.925,44.782,109.4,49.239,90,55.181z"/>
</g>
<g>
<path class="st4" d="M88,216.035c-0.203,0-0.407-0.031-0.605-0.094l-4.925-1.566c-6.611-2.106-6.611-2.106-32.834-10.133
l-28.807-8.82c-1.692-0.519-2.829-2.056-2.829-3.825V48.131c0-1.282,0.591-2.454,1.622-3.216c1.031-0.761,2.325-0.985,3.549-0.608
l10.278,3.147l55.12,16.331C89.417,64.036,90,64.816,90,65.702v148.333c0,0.638-0.304,1.237-0.819,1.614
C88.834,215.902,88.419,216.035,88,216.035z M22,48.13v143.467l28.807,8.82c26.244,8.033,26.244,8.033,32.877,10.146L86,211.3
V67.195L32.303,51.286c-0.006-0.001-0.011-0.003-0.017-0.005L22,48.13z"/>
</g>
<g>
<path class="st4" d="M234.012,210.87c-0.393,0-0.79-0.058-1.18-0.177l-10.279-3.128l-55.118-16.226
c-0.851-0.25-1.435-1.031-1.435-1.918V42.035c0-0.637,0.304-1.236,0.817-1.613c0.514-0.377,1.175-0.487,1.784-0.294l12,3.784
c13.574,4.13,23.873,7.23,31.674,9.578c12.206,3.674,18.333,5.518,21.419,6.621c2.763,0.987,4.432,1.754,4.311,4.08L238,64.329
v142.55c0,1.274-0.588,2.44-1.614,3.2C235.683,210.601,234.856,210.87,234.012,210.87z M170,187.925l53.694,15.807
c0.006,0.001,0.012,0.003,0.017,0.005l10.286,3.13L234,64.519c-1.813-0.858-8.533-2.881-22.877-7.198
c-7.805-2.349-18.107-5.45-31.705-9.588L170,44.762V187.925z"/>
</g>
<g>
<path class="st4" d="M88,216.035c-0.422,0-0.839-0.134-1.188-0.391c-0.511-0.377-0.813-0.975-0.813-1.609V65.702
c0-0.884,0.581-1.664,1.429-1.917l68-20.275c0.009-0.003,0.018-0.005,0.027-0.008l12-3.392c0.601-0.169,1.251-0.048,1.75,0.33
c0.5,0.378,0.793,0.969,0.793,1.595v147.333c0,0.877-0.572,1.653-1.411,1.911l-80,24.667
C88.396,216.005,88.198,216.035,88,216.035z M90,67.192v144.133l76-23.433V44.678l-9.443,2.669L90,67.192z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

1
frontend/public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

57
frontend/src/App.css Normal file
View File

@ -0,0 +1,57 @@
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.app-container {
display: flex;
flex-direction: column;
height: 100%;
}
header {
background-color: #2c3e50;
padding: 20px;
text-align: center;
color: white;
}
header h1 {
margin-bottom: 10px;
font-size: 2rem;
}
.search-bar {
margin-top: 10px;
}
.search-bar input {
padding: 10px;
width: 250px;
border: none;
border-radius: 5px;
margin-right: 8px;
}
.search-bar button {
padding: 10px 15px;
border: none;
border-radius: 5px;
background-color: #3498db;
color: white;
cursor: pointer;
font-weight: bold;
}
main {
flex: 1;
display: flex;
}
.map-container {
flex: 1;
min-height: 500px;
width: 100%;
}

26
frontend/src/App.jsx Normal file
View File

@ -0,0 +1,26 @@
import { useState } from 'react';
import './App.css';
import Header from './components/Header';
import MapView from './components/MapView';
function App() {
const [query, setQuery] = useState('');
const [families, setFamilies] = useState([]);
const handleSearch = async () => {
const res = await fetch(`http://localhost:8000/search?family=${encodeURIComponent(query)}`);
const data = await res.json();
setFamilies(data);
};
return (
<div className="app-container">
<Header query={query} setQuery={setQuery} onSearch={handleSearch} />
<main>
<MapView families={families} />
</main>
</div>
);
}
export default App;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,21 @@
function Header({ query, setQuery, onSearch }) {
return (
<header>
<h1>
<img src="../public/oramap-logo.svg" alt="Logo" style={{ height: '40px', marginRight: '10px', verticalAlign: 'middle' }} />
Ora
</h1>
<div className="search-bar">
<input
type="text"
placeholder="Enter family name..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button onClick={onSearch}>Search</button>
</div>
</header>
);
}
export default Header;

View File

@ -0,0 +1,43 @@
import { useEffect, useRef } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
function MapView({ families }) {
const mapRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
if (!mapRef.current && containerRef.current) {
const map = L.map(containerRef.current).setView([15.3545, 44.2064], 7);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
mapRef.current = map;
}
}, []);
useEffect(() => {
const map = mapRef.current;
if (!map) return;
// Remove old markers
map.eachLayer((layer) => {
if (layer instanceof L.Marker) map.removeLayer(layer);
});
families.forEach((fam) => {
L.marker([fam.lat, fam.lng]).addTo(map)
.bindPopup(`<strong>${fam.family}</strong><br/>${fam.city}`);
});
if (families.length > 0) {
map.setView([families[0].lat, families[0].lng], 8);
}
}, [families]);
return <div ref={containerRef} className="map-container"></div>;
}
export default MapView;

68
frontend/src/index.css Normal file
View File

@ -0,0 +1,68 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

10
frontend/src/main.jsx Normal file
View File

@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import './App.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

7
frontend/vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})

0
old-project/README.md Normal file
View File

View File

Before

Width:  |  Height:  |  Size: 975 KiB

After

Width:  |  Height:  |  Size: 975 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,6 +1,6 @@
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const families = require('./data/families.json'); const families = require('../data/families.json');
app.use(express.static('public')); app.use(express.static('public'));