diff --git a/city-manual/backend/db.sqlite3 b/city-manual/backend/db.sqlite3 index d5e9151..7f75e48 100644 Binary files a/city-manual/backend/db.sqlite3 and b/city-manual/backend/db.sqlite3 differ diff --git a/city-manual/backend/regions/management/__init__.py b/city-manual/backend/regions/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/city-manual/backend/regions/management/__pycache__/__init__.cpython-312.pyc b/city-manual/backend/regions/management/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b2674d7 Binary files /dev/null and b/city-manual/backend/regions/management/__pycache__/__init__.cpython-312.pyc differ diff --git a/city-manual/backend/regions/management/commands/__init__.py b/city-manual/backend/regions/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/city-manual/backend/regions/management/commands/__pycache__/__init__.cpython-312.pyc b/city-manual/backend/regions/management/commands/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ccae356 Binary files /dev/null and b/city-manual/backend/regions/management/commands/__pycache__/__init__.cpython-312.pyc differ diff --git a/city-manual/backend/regions/management/commands/__pycache__/seed_data.cpython-312.pyc b/city-manual/backend/regions/management/commands/__pycache__/seed_data.cpython-312.pyc new file mode 100644 index 0000000..0723956 Binary files /dev/null and b/city-manual/backend/regions/management/commands/__pycache__/seed_data.cpython-312.pyc differ diff --git a/city-manual/backend/regions/management/commands/seed_data.py b/city-manual/backend/regions/management/commands/seed_data.py new file mode 100644 index 0000000..3995ee8 --- /dev/null +++ b/city-manual/backend/regions/management/commands/seed_data.py @@ -0,0 +1,225 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone +from datetime import timedelta +from regions.models import Region, ModeratorApplication +from users.models import User +from content.models import Article +from services.models import FeaturedService + + +class Command(BaseCommand): + help = '导入示例城市数据' + + def handle(self, *args, **options): + self.stdout.write('开始导入示例数据...') + + # 创建示例用户 + user, _ = User.objects.get_or_create( + username='demo', + defaults={ + 'email': 'demo@citymanual.com', + 'is_verified': True, + } + ) + user.set_password('demo123') + user.save() + + # 创建省级区域 + provinces_data = [ + {'name': '北京市', 'code': '110000', 'description': '中华人民共和国首都,全国政治中心、文化中心、国际交往中心、科技创新中心。'}, + {'name': '上海市', 'code': '310000', 'description': '中国最大的经济中心和港口城市,国际金融中心,国际贸易中心。'}, + {'name': '广东省', 'code': '440000', 'description': '中国经济最发达的省份之一,改革开放的前沿阵地。'}, + {'name': '浙江省', 'code': '330000', 'description': '中国东南沿海省份,经济发达,民营经济活跃。'}, + {'name': '四川省', 'code': '510000', 'description': '中国西南地区重要省份,天府之国,美食之都。'}, + {'name': '陕西省', 'code': '610000', 'description': '中华文明重要发祥地,十三朝古都所在地,历史文化底蕴深厚。'}, + ] + + provinces = [] + for prov_data in provinces_data: + province, created = Region.objects.get_or_create( + name=prov_data['name'], + defaults={ + 'level': 'province', + 'code': prov_data['code'], + 'description': prov_data['description'], + 'is_active': True, + } + ) + provinces.append(province) + if created: + self.stdout.write(f'✓ 创建省份:{province.name}') + + # 创建市级区域 + cities_data = [ + {'name': '广州市', 'parent': '广东省', 'code': '440100', 'description': '广东省省会,国家中心城市,国际商贸中心。'}, + {'name': '深圳市', 'parent': '广东省', 'code': '440300', 'description': '经济特区,科技创新中心,中国硅谷。'}, + {'name': '杭州市', 'parent': '浙江省', 'code': '330100', 'description': '浙江省省会,电子商务之都,风景秀丽。'}, + {'name': '宁波市', 'parent': '浙江省', 'code': '330200', 'description': '副省级市,计划单列市,重要的港口城市。'}, + {'name': '成都市', 'parent': '四川省', 'code': '510100', 'description': '四川省省会,天府之国核心,美食之都。'}, + {'name': '绵阳市', 'parent': '四川省', 'code': '510700', 'description': '四川省第二大城市,中国科技城。'}, + {'name': '西安市', 'parent': '陕西省', 'code': '610100', 'description': '陕西省省会,十三朝古都,世界历史名城。'}, + {'name': '咸阳市', 'parent': '陕西省', 'code': '610400', 'description': '中国第一个封建帝国秦朝的都城所在地。'}, + ] + + cities = [] + for city_data in cities_data: + parent = Region.objects.filter(name=city_data['parent']).first() + if parent: + city, created = Region.objects.get_or_create( + name=city_data['name'], + defaults={ + 'level': 'city', + 'parent': parent, + 'code': city_data['code'], + 'description': city_data['description'], + 'is_active': True, + } + ) + cities.append(city) + if created: + self.stdout.write(f'✓ 创建城市:{city.name}') + + # 创建示例文章 + articles_data = [ + { + 'title': '北京故宫游览完全攻略', + 'content': '故宫,旧称紫禁城,是中国明清两代的皇家宫殿,位于北京中轴线的中心。故宫以三大殿为中心,占地面积约 72 万平方米,建筑面积约 15 万平方米,有大小宫殿七十多座,房屋九千余间。是世界上现存规模最大、保存最为完整的木质结构古建筑之一。\n\n游览建议:\n1. 最佳游览时间:春秋两季\n2. 建议游览时长:3-4 小时\n3. 必游景点:太和殿、乾清宫、御花园\n4. 门票:旺季 60 元,淡季 40 元', + 'region_name': '北京市', + 'content_type': 'tourism', + }, + { + 'title': '上海外滩历史与现状', + 'content': '外滩位于上海市黄浦区的黄浦江畔,即外黄浦滩,为中国历史文化街区。1844 年(清道光廿四年)起,外滩这一带被划为英国租界,成为上海十里洋场的真实写照,也是旧上海租界区以及整个上海近代城市开始的起点。\n\n外滩全长 1.5 千米,南起延安东路,北至苏州河上的外白渡桥,东面即黄浦江,西面是历史风格建筑群。', + 'region_name': '上海市', + 'content_type': 'history', + }, + { + 'title': '广州早茶文化指南', + 'content': '广州早茶是广州饮食文化的重要组成部分,也是岭南文化的重要体现。广州人饮早茶,不仅是品尝美食,更是一种社交方式和生活方式。\n\n经典茶点:\n- 虾饺:晶莹剔透,鲜美弹牙\n- 烧卖:皮薄馅足,肉香四溢\n- 肠粉:滑嫩爽口,酱汁鲜美\n- 叉烧包:甜咸适中,松软可口\n\n推荐茶楼:陶陶居、广州酒家、莲香楼', + 'region_name': '广州市', + 'content_type': 'culture', + }, + { + 'title': '深圳科技创新发展报告', + 'content': '深圳作为中国改革开放的窗口和经济特区,40 多年来从一个小渔村发展成为国际化创新型城市。深圳拥有华为、腾讯、大疆等众多知名科技企业,被誉为"中国硅谷"。\n\n2025 年深圳 PCT 国际专利申请量连续多年居全国城市首位,战略性新兴产业增加值占 GDP 比重超过 40%。', + 'region_name': '深圳市', + 'content_type': 'city_info', + }, + { + 'title': '杭州西湖十景详解', + 'content': '西湖十景是杭州西湖最具代表性的十个景点,形成于南宋时期。每个景点都有其独特的自然风光和文化内涵。\n\n十景包括:\n1. 苏堤春晓\n2. 曲院风荷\n3. 平湖秋月\n4. 断桥残雪\n5. 花港观鱼\n6. 柳浪闻莺\n7. 三潭印月\n8. 双峰插云\n9. 雷峰夕照\n10. 南屏晚钟', + 'region_name': '杭州市', + 'content_type': 'tourism', + }, + { + 'title': '成都美食地图', + 'content': '成都,被联合国教科文组织授予"美食之都"称号,是中国乃至世界的美食天堂。川菜以其麻、辣、鲜、香的特色闻名世界。\n\n必吃美食:\n- 火锅:麻辣鲜香,回味无穷\n- 串串香:成都特色,街头美食\n- 麻婆豆腐:经典川菜,麻辣鲜香\n- 夫妻肺片:凉拌菜经典\n- 担担面:传统面食\n\n美食街区:锦里、宽窄巷子、春熙路', + 'region_name': '成都市', + 'content_type': 'life', + }, + { + 'title': '西安兵马俑参观指南', + 'content': '秦始皇兵马俑博物馆位于西安市临潼区,是中国第一个封建皇帝秦始皇的陵墓陪葬坑,被誉为"世界第八大奇迹"。\n\n参观信息:\n- 开放时间:8:30-18:00\n- 门票:120 元\n- 建议游览:2-3 小时\n- 最佳季节:春秋两季\n\n兵马俑坑共有三个,其中一号坑最大,展示了完整的军阵布局。', + 'region_name': '西安市', + 'content_type': 'tourism', + }, + ] + + for article_data in articles_data: + region = Region.objects.filter(name=article_data['region_name']).first() + if region: + article, created = Article.objects.get_or_create( + title=article_data['title'], + defaults={ + 'content': article_data['content'], + 'region': region, + 'content_type': article_data['content_type'], + 'author': user, + 'moderator_status': 'approved', + 'moderator_reviewed_at': timezone.now(), + 'ai_status': 'approved', + 'ai_reviewed_at': timezone.now(), + 'publish_status': 'published', + } + ) + if created: + self.stdout.write(f'✓ 创建文章:{article.title}') + + # 创建示例特色服务 + services_data = [ + { + 'name': '全聚德烤鸭店 (北京)', + 'description': '中华老字号,创建于 1864 年,以挂炉烤鸭闻名。全聚德烤鸭以其色泽红润、肉质肥而不腻、外脆里嫩的特点著称。', + 'region_name': '北京市', + 'category': 'food', + 'address': '北京市东城区前门大街 32 号', + 'price_range': '人均 150-300 元', + }, + { + 'name': '上海东方明珠塔', + 'description': '上海标志性建筑,高 468 米,集观光、餐饮、娱乐、购物于一体。登上观光层可俯瞰整个上海市区。', + 'region_name': '上海市', + 'category': 'tourism', + 'address': '上海市浦东新区陆家嘴世纪大道 1 号', + 'price_range': '门票 199 元起', + }, + { + 'name': '广州塔 (小蛮腰)', + 'description': '广州地标建筑,高 600 米,中国第一高塔。设有观光层、摩天轮、极速云霄等游乐项目。', + 'region_name': '广州市', + 'category': 'tourism', + 'address': '广州市海珠区阅江西路 222 号', + 'price_range': '门票 150 元起', + }, + { + 'name': '杭州楼外楼', + 'description': '百年老字号餐厅,创建于 1848 年,以杭帮菜闻名。招牌菜包括西湖醋鱼、龙井虾仁、叫花童鸡等。', + 'region_name': '杭州市', + 'category': 'food', + 'address': '杭州市西湖区孤山路 30 号', + 'price_range': '人均 200-400 元', + }, + { + 'name': '成都宽窄巷子', + 'description': '成都著名历史文化街区,由宽巷子、窄巷子、井巷子组成。集美食、购物、文化体验于一体。', + 'region_name': '成都市', + 'category': 'tourism', + 'address': '成都市青羊区长顺上街 127 号', + 'price_range': '免费开放', + }, + { + 'name': '西安大唐不夜城', + 'description': '以盛唐文化为背景的主题街区,夜景璀璨,有各种表演、美食、文创店铺,是西安夜生活的代表。', + 'region_name': '西安市', + 'category': 'entertainment', + 'address': '西安市雁塔区曲江新区', + 'price_range': '免费开放', + }, + ] + + for service_data in services_data: + region = Region.objects.filter(name=service_data['region_name']).first() + if region: + service, created = FeaturedService.objects.get_or_create( + name=service_data['name'], + defaults={ + 'description': service_data['description'], + 'region': region, + 'category': service_data['category'], + 'address': service_data.get('address', ''), + 'price_range': service_data.get('price_range', ''), + 'submitter': user, + 'moderator_status': 'approved', + 'moderator_reviewed_at': timezone.now(), + 'ai_status': 'approved', + 'ai_reviewed_at': timezone.now(), + 'publish_status': 'published', + } + ) + if created: + self.stdout.write(f'✓ 创建服务:{service.name}') + + self.stdout.write(self.style.SUCCESS('\n✅ 示例数据导入完成!')) + self.stdout.write('\n测试账号:') + self.stdout.write(' 用户名:demo') + self.stdout.write(' 密码:demo123') diff --git a/city-manual/backend/seed.sh b/city-manual/backend/seed.sh new file mode 100644 index 0000000..6b28764 --- /dev/null +++ b/city-manual/backend/seed.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd /root/.openclaw/workspace/city-manual/backend +python3 manage.py seed_data diff --git a/city-manual/frontend/dev.sh b/city-manual/frontend/dev.sh new file mode 100644 index 0000000..a3529c1 --- /dev/null +++ b/city-manual/frontend/dev.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd /root/.openclaw/workspace/city-manual/frontend +npm run dev -- --host 0.0.0.0 --port 3000 2>&1 diff --git a/city-manual/frontend/install.sh b/city-manual/frontend/install.sh new file mode 100644 index 0000000..a181ce0 --- /dev/null +++ b/city-manual/frontend/install.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd /root/.openclaw/workspace/city-manual/frontend +npm install --legacy-peer-deps 2>&1 diff --git a/city-manual/frontend/package-lock.json b/city-manual/frontend/package-lock.json new file mode 100644 index 0000000..d5a767e --- /dev/null +++ b/city-manual/frontend/package-lock.json @@ -0,0 +1,2630 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "axios": "^1.15.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router-dom": "^7.14.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "vite": "^8.0.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.17", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001787", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.334", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.9", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.5", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.5", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.5" + } + }, + "node_modules/react-router": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/city-manual/frontend/package.json b/city-manual/frontend/package.json index 2576dba..2cb299e 100644 --- a/city-manual/frontend/package.json +++ b/city-manual/frontend/package.json @@ -10,8 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.15.0", "react": "^19.2.4", - "react-dom": "^19.2.4" + "react-dom": "^19.2.4", + "react-router-dom": "^7.14.0" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/city-manual/frontend/src/App.jsx b/city-manual/frontend/src/App.jsx index b2bf2e8..88e7b71 100644 --- a/city-manual/frontend/src/App.jsx +++ b/city-manual/frontend/src/App.jsx @@ -1,121 +1,68 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import './App.css' +import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; +import Home from './pages/Home'; +import Cities from './pages/Cities'; +import RegionDetail from './pages/RegionDetail'; +import './App.css'; function App() { - const [count, setCount] = useState(0) - return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.jsx and save to test HMR -

-
- -
+ +
+ {/* Navigation */} + -
+ {/* Main Content */} +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
-
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) + {/* Footer */} + +
+
+ ); } -export default App +// 占位组件 - 后续完善 +function ArticleDetail() { + return

文章详情页

开发中...

; +} + +function ServiceDetail() { + return

服务详情页

开发中...

; +} + +function Login() { + return

登录

开发中...

; +} + +function Register() { + return

注册

开发中...

; +} + +export default App; diff --git a/city-manual/frontend/src/api.js b/city-manual/frontend/src/api.js new file mode 100644 index 0000000..b811d00 --- /dev/null +++ b/city-manual/frontend/src/api.js @@ -0,0 +1,68 @@ +import axios from 'axios'; + +const API_BASE = 'http://localhost:8000/api'; + +const api = axios.create({ + baseURL: API_BASE, + headers: { + 'Content-Type': 'application/json', + }, +}); + +// 请求拦截器 - 添加 token +api.interceptors.request.use((config) => { + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// 响应拦截器 - 处理错误 +api.interceptors.response.use( + (response) => response, + (error) => { + if (error.response?.status === 401) { + localStorage.removeItem('token'); + window.location.href = '/login'; + } + return Promise.reject(error); + } +); + +export const regionsApi = { + getList: () => api.get('/regions/'), + getDetail: (id) => api.get(`/regions/${id}/`), + getProvinces: () => api.get('/regions/provinces/'), + getChildren: (id) => api.get(`/regions/${id}/children/`), +}; + +export const articlesApi = { + getList: (params) => api.get('/articles/', { params }), + getDetail: (id) => api.get(`/articles/${id}/`), + create: (data) => api.post('/articles/', data), +}; + +export const servicesApi = { + getList: (params) => api.get('/services/', { params }), + getDetail: (id) => api.get(`/services/${id}/`), + create: (data) => api.post('/services/', data), +}; + +export const usersApi = { + register: (data) => api.post('/register/', data), + login: (data) => api.post('/token/', data), + getMe: () => api.get('/users/me/'), +}; + +export const commentsApi = { + getList: () => api.get('/comments/'), + create: (data) => api.post('/comments/', data), +}; + +export const ratingsApi = { + getList: () => api.get('/ratings/'), + create: (data) => api.post('/ratings/', data), +}; + +export default api; diff --git a/city-manual/frontend/src/pages/Cities.css b/city-manual/frontend/src/pages/Cities.css new file mode 100644 index 0000000..906139d --- /dev/null +++ b/city-manual/frontend/src/pages/Cities.css @@ -0,0 +1,171 @@ +.cities-page { + min-height: 100vh; + padding: 40px 20px; + background: #f8f9fa; +} + +.container { + max-width: 1200px; + margin: 0 auto; +} + +/* Breadcrumb */ +.breadcrumb { + margin-bottom: 30px; + font-size: 0.95rem; +} + +.breadcrumb a { + color: #667eea; + text-decoration: none; +} + +.breadcrumb a:hover { + text-decoration: underline; +} + +.breadcrumb .separator { + margin: 0 10px; + color: #999; +} + +/* Region Header */ +.region-header { + background: white; + padding: 40px; + border-radius: 12px; + margin-bottom: 30px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.region-header h1 { + font-size: 2.5rem; + color: #333; + margin-bottom: 15px; +} + +.region-description { + color: #666; + font-size: 1.1rem; + line-height: 1.6; + margin-bottom: 20px; +} + +.region-stats { + display: flex; + gap: 30px; +} + +.stat { + color: #667eea; + font-weight: bold; + font-size: 1rem; +} + +/* Region Grid */ +.region-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 20px; +} + +.region-card { + background: white; + padding: 30px 25px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + text-align: center; + transition: transform 0.3s, box-shadow 0.3s; +} + +.region-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); +} + +.region-icon { + font-size: 3rem; + margin-bottom: 15px; +} + +.region-card h3 { + font-size: 1.3rem; + margin-bottom: 10px; + color: #333; +} + +.region-level { + color: #667eea; + font-size: 0.9rem; + margin-bottom: 8px; +} + +.region-children { + color: #999; + font-size: 0.85rem; +} + +/* Empty State */ +.empty-state { + background: white; + padding: 60px 40px; + border-radius: 12px; + text-align: center; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.empty-state p { + color: #666; + font-size: 1.1rem; + margin-bottom: 20px; +} + +.btn-link { + color: #667eea; + text-decoration: none; + font-weight: bold; +} + +.btn-link:hover { + text-decoration: underline; +} + +/* Loading */ +.loading { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + font-size: 1.5rem; + color: #666; +} + +/* Responsive */ +@media (max-width: 768px) { + .cities-page { + padding: 20px 15px; + } + + .region-header { + padding: 25px 20px; + } + + .region-header h1 { + font-size: 1.8rem; + } + + .region-grid { + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 15px; + } + + .region-card { + padding: 20px 15px; + } + + .region-icon { + font-size: 2.5rem; + } +} diff --git a/city-manual/frontend/src/pages/Cities.jsx b/city-manual/frontend/src/pages/Cities.jsx new file mode 100644 index 0000000..4d50285 --- /dev/null +++ b/city-manual/frontend/src/pages/Cities.jsx @@ -0,0 +1,135 @@ +import { useState, useEffect } from 'react'; +import { Link, useParams } from 'react-router-dom'; +import { regionsApi } from '../api'; +import './Cities.css'; + +function Cities() { + const { provinceId } = useParams(); + const [regions, setRegions] = useState([]); + const [currentRegion, setCurrentRegion] = useState(null); + const [loading, setLoading] = useState(true); + const [breadcrumb, setBreadcrumb] = useState([]); + + useEffect(() => { + loadRegions(); + }, [provinceId]); + + async function loadRegions() { + setLoading(true); + try { + if (provinceId) { + // 加载省份详情和子区域 + const [detailRes, childrenRes] = await Promise.all([ + regionsApi.getDetail(provinceId), + regionsApi.getChildren(provinceId), + ]); + setCurrentRegion(detailRes.data); + setRegions(childrenRes.data); + setBreadcrumb([ + { id: null, name: '全部省份' }, + { id: provinceId, name: detailRes.data.name }, + ]); + } else { + // 加载所有省份 + const res = await regionsApi.getProvinces(); + setRegions(res.data); + setCurrentRegion(null); + setBreadcrumb([{ id: null, name: '全部省份' }]); + } + } catch (error) { + console.error('加载区域失败:', error); + } finally { + setLoading(false); + } + } + + if (loading) { + return
加载中...
; + } + + return ( +
+
+ {/* 面包屑导航 */} + + + {/* 当前区域信息 */} + {currentRegion && ( +
+

{currentRegion.name}

+ {currentRegion.description && ( +

{currentRegion.description}

+ )} +
+ + 📝 {currentRegion.articles_count || 0} 篇文章 + + + 🏪 {currentRegion.services_count || 0} 个服务 + +
+
+ )} + + {/* 子区域列表 */} + {regions.length > 0 ? ( +
+ {regions.map((region) => ( + +
+ {region.level === 'province' && '🏔️'} + {region.level === 'city' && '🏙️'} + {region.level === 'county' && '🏘️'} + {region.level === 'town' && '🏡'} + {region.level === 'village' && '🏠'} +
+

{region.name}

+

{getLevelName(region.level)}

+ {region.children_count > 0 && ( +

{region.children_count} 个下级区域

+ )} + + ))} +
+ ) : ( +
+

暂无下级区域

+ + 查看该区域的文章 → + +
+ )} +
+
+ ); +} + +function getLevelName(level) { + const map = { + province: '省级', + city: '市级', + county: '县级', + town: '镇级', + village: '村级', + }; + return map[level] || level; +} + +export default Cities; diff --git a/city-manual/frontend/src/pages/Home.css b/city-manual/frontend/src/pages/Home.css new file mode 100644 index 0000000..4b491c0 --- /dev/null +++ b/city-manual/frontend/src/pages/Home.css @@ -0,0 +1,228 @@ +.home { + min-height: 100vh; +} + +.loading { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + font-size: 1.5rem; + color: #666; +} + +/* Hero Section */ +.hero { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 120px 20px; + text-align: center; +} + +.hero-content h1 { + font-size: 3.5rem; + margin-bottom: 20px; + font-weight: bold; +} + +.hero-content p { + font-size: 1.3rem; + margin-bottom: 30px; + opacity: 0.9; +} + +.btn-primary { + display: inline-block; + background: white; + color: #667eea; + padding: 15px 40px; + border-radius: 30px; + text-decoration: none; + font-weight: bold; + font-size: 1.1rem; + transition: transform 0.3s, box-shadow 0.3s; +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); +} + +/* Section */ +.section { + padding: 80px 20px; +} + +.section-alt { + background: #f8f9fa; +} + +.container { + max-width: 1200px; + margin: 0 auto; +} + +.section-title { + text-align: center; + font-size: 2rem; + margin-bottom: 50px; + color: #333; + position: relative; +} + +.section-title::after { + content: ''; + display: block; + width: 60px; + height: 4px; + background: #667eea; + margin: 15px auto 0; + border-radius: 2px; +} + +/* Province Grid */ +.province-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 20px; +} + +.province-card { + background: white; + padding: 30px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + transition: transform 0.3s, box-shadow 0.3s; + border: 2px solid transparent; +} + +.province-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); + border-color: #667eea; +} + +.province-card h3 { + font-size: 1.5rem; + margin-bottom: 10px; + color: #667eea; +} + +.province-card p { + color: #666; + font-size: 0.95rem; +} + +/* Article Grid */ +.article-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 25px; +} + +.article-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + transition: transform 0.3s, box-shadow 0.3s; +} + +.article-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); +} + +.article-card h3 { + font-size: 1.3rem; + margin-bottom: 10px; + color: #333; +} + +.article-meta { + color: #667eea; + font-size: 0.9rem; + margin-bottom: 15px; +} + +.article-excerpt { + color: #666; + line-height: 1.6; + font-size: 0.95rem; +} + +/* Service Grid */ +.service-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 20px; +} + +.service-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + transition: transform 0.3s, box-shadow 0.3s; + position: relative; + overflow: hidden; +} + +.service-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); +} + +.service-category { + position: absolute; + top: 15px; + right: 15px; + background: #667eea; + color: white; + padding: 5px 12px; + border-radius: 20px; + font-size: 0.8rem; +} + +.service-card h3 { + font-size: 1.2rem; + margin-bottom: 10px; + color: #333; +} + +.service-location { + color: #666; + font-size: 0.9rem; + margin-bottom: 10px; +} + +.service-rating { + color: #f5a623; + font-weight: bold; + font-size: 0.95rem; +} + +/* Responsive */ +@media (max-width: 768px) { + .hero-content h1 { + font-size: 2.5rem; + } + + .hero-content p { + font-size: 1.1rem; + } + + .section { + padding: 50px 15px; + } + + .section-title { + font-size: 1.6rem; + } +} diff --git a/city-manual/frontend/src/pages/Home.jsx b/city-manual/frontend/src/pages/Home.jsx new file mode 100644 index 0000000..31bfe5e --- /dev/null +++ b/city-manual/frontend/src/pages/Home.jsx @@ -0,0 +1,101 @@ +import { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { regionsApi, articlesApi, servicesApi } from '../api'; +import './Home.css'; + +function Home() { + const [provinces, setProvinces] = useState([]); + const [featuredArticles, setFeaturedArticles] = useState([]); + const [featuredServices, setFeaturedServices] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + loadData(); + }, []); + + async function loadData() { + try { + const [provincesRes, articlesRes, servicesRes] = await Promise.all([ + regionsApi.getProvinces(), + articlesApi.getList({ limit: 6 }), + servicesApi.getList({ limit: 6 }), + ]); + setProvinces(provincesRes.data.slice(0, 6)); + setFeaturedArticles(articlesRes.data.results || articlesRes.data.slice(0, 6)); + setFeaturedServices(servicesRes.data.results || servicesRes.data.slice(0, 6)); + } catch (error) { + console.error('加载数据失败:', error); + } finally { + setLoading(false); + } + } + + if (loading) { + return
加载中...
; + } + + return ( +
+ {/* Hero Section */} +
+
+

城市手册

+

探索每座城市的独特魅力

+ 开始探索 +
+
+ + {/* 省份导航 */} +
+
+

热门省份

+
+ {provinces.map((province) => ( + +

{province.name}

+

{province.level === 'province' ? '省级行政区' : province.level}

+ + ))} +
+
+
+ + {/* 精选文章 */} +
+
+

精选内容

+
+ {featuredArticles.map((article) => ( + +

{article.title}

+

{article.region?.name} · {article.content_type}

+

{article.content?.substring(0, 100)}...

+ + ))} +
+
+
+ + {/* 特色服务 */} +
+
+

特色服务

+
+ {featuredServices.map((service) => ( + +
{service.category}
+

{service.name}

+

{service.region?.name}

+ {service.rating_average > 0 && ( +
⭐ {service.rating_average.toFixed(1)}
+ )} + + ))} +
+
+
+
+ ); +} + +export default Home; diff --git a/city-manual/frontend/src/pages/RegionDetail.css b/city-manual/frontend/src/pages/RegionDetail.css new file mode 100644 index 0000000..07d9e0c --- /dev/null +++ b/city-manual/frontend/src/pages/RegionDetail.css @@ -0,0 +1,335 @@ +.region-detail { + min-height: 100vh; + background: #f8f9fa; +} + +/* Header */ +.region-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 60px 20px 40px; +} + +.region-header .container { + max-width: 1200px; + margin: 0 auto; +} + +.breadcrumb { + margin-bottom: 20px; + opacity: 0.9; +} + +.breadcrumb a { + color: white; + text-decoration: none; +} + +.breadcrumb a:hover { + text-decoration: underline; +} + +.breadcrumb .separator { + margin: 0 10px; +} + +.region-header h1 { + font-size: 3rem; + margin-bottom: 15px; + font-weight: bold; +} + +.description { + font-size: 1.2rem; + line-height: 1.6; + margin-bottom: 20px; + opacity: 0.95; +} + +.tags { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.tag { + background: rgba(255, 255, 255, 0.2); + padding: 6px 15px; + border-radius: 20px; + font-size: 0.9rem; +} + +/* Tabs */ +.tabs-container { + background: white; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + position: sticky; + top: 0; + z-index: 100; +} + +.tabs { + display: flex; + gap: 5px; + overflow-x: auto; +} + +.tab { + padding: 18px 30px; + border: none; + background: none; + font-size: 1rem; + color: #666; + cursor: pointer; + position: relative; + transition: color 0.3s; + white-space: nowrap; +} + +.tab:hover { + color: #667eea; +} + +.tab.active { + color: #667eea; + font-weight: bold; +} + +.tab.active::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background: #667eea; +} + +/* Content */ +.content { + padding: 40px 20px; +} + +/* Overview */ +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + margin-bottom: 40px; +} + +.stat-card { + background: white; + padding: 30px; + border-radius: 12px; + text-align: center; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.stat-number { + font-size: 3rem; + font-weight: bold; + color: #667eea; + margin-bottom: 10px; +} + +.stat-label { + color: #666; + font-size: 1rem; +} + +.children-section { + background: white; + padding: 30px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.children-section h2 { + margin-bottom: 25px; + color: #333; +} + +.region-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 20px; +} + +.region-card { + background: #f8f9fa; + padding: 25px; + border-radius: 12px; + text-decoration: none; + color: #333; + text-align: center; + transition: transform 0.3s, box-shadow 0.3s; +} + +.region-card:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); + background: white; +} + +.region-card h3 { + font-size: 1.2rem; + margin-bottom: 8px; + color: #667eea; +} + +.region-card p { + color: #666; + font-size: 0.9rem; +} + +/* Articles & Services List */ +.articles-list, +.services-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; +} + +.article-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + transition: transform 0.3s, box-shadow 0.3s; +} + +.article-card:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); +} + +.article-card h3 { + font-size: 1.2rem; + margin-bottom: 12px; + color: #333; +} + +.meta { + display: flex; + justify-content: space-between; + font-size: 0.85rem; +} + +.type { + color: #667eea; + background: #f0f2ff; + padding: 4px 10px; + border-radius: 4px; +} + +.date { + color: #999; +} + +.service-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: #333; + transition: transform 0.3s, box-shadow 0.3s; +} + +.service-card:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); +} + +.service-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} + +.service-header h3 { + font-size: 1.2rem; + color: #333; +} + +.category { + background: #667eea; + color: white; + padding: 4px 12px; + border-radius: 20px; + font-size: 0.85rem; +} + +.service-description { + color: #666; + line-height: 1.6; + margin-bottom: 15px; +} + +.rating { + color: #f5a623; + font-weight: bold; + font-size: 0.95rem; +} + +/* Empty State */ +.empty { + background: white; + padding: 60px 40px; + border-radius: 12px; + text-align: center; + color: #666; + font-size: 1.1rem; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +/* Loading */ +.loading { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + font-size: 1.5rem; + color: #666; +} + +/* Responsive */ +@media (max-width: 768px) { + .region-header { + padding: 40px 15px 30px; + } + + .region-header h1 { + font-size: 2rem; + } + + .description { + font-size: 1rem; + } + + .tabs { + padding: 0 15px; + } + + .tab { + padding: 15px 20px; + font-size: 0.95rem; + } + + .content { + padding: 25px 15px; + } + + .stats-grid { + grid-template-columns: 1fr; + } + + .articles-list, + .services-list { + grid-template-columns: 1fr; + } +} diff --git a/city-manual/frontend/src/pages/RegionDetail.jsx b/city-manual/frontend/src/pages/RegionDetail.jsx new file mode 100644 index 0000000..a574f11 --- /dev/null +++ b/city-manual/frontend/src/pages/RegionDetail.jsx @@ -0,0 +1,224 @@ +import { useState, useEffect } from 'react'; +import { useParams, Link } from 'react-router-dom'; +import { regionsApi, articlesApi, servicesApi } from '../api'; +import './RegionDetail.css'; + +function RegionDetail() { + const { id } = useParams(); + const [region, setRegion] = useState(null); + const [articles, setArticles] = useState([]); + const [services, setServices] = useState([]); + const [children, setChildren] = useState([]); + const [loading, setLoading] = useState(true); + const [activeTab, setActiveTab] = useState('overview'); + + useEffect(() => { + loadData(); + }, [id]); + + async function loadData() { + setLoading(true); + try { + const [regionRes, articlesRes, servicesRes, childrenRes] = await Promise.all([ + regionsApi.getDetail(id), + articlesApi.getList({ region: id, limit: 10 }), + servicesApi.getList({ region: id, limit: 10 }), + regionsApi.getChildren(id), + ]); + setRegion(regionRes.data); + setArticles(articlesRes.data.results || articlesRes.data); + setServices(servicesRes.data.results || servicesRes.data); + setChildren(childrenRes.data); + } catch (error) { + console.error('加载数据失败:', error); + } finally { + setLoading(false); + } + } + + if (loading || !region) { + return
加载中...
; + } + + return ( +
+ {/* Header */} +
+
+ +

{region.name}

+ {region.description &&

{region.description}

} +
+ {getLevelName(region.level)} + {region.code && 代码:{region.code}} +
+
+
+ + {/* Tabs */} +
+
+
+ + + + +
+
+
+ + {/* Content */} +
+ {activeTab === 'overview' && ( +
+
+
+
{region.articles_count || 0}
+
文章
+
+
+
{region.services_count || 0}
+
服务
+
+
+
{children.length}
+
下级区域
+
+
+ + {children.length > 0 && ( +
+

下级区域

+
+ {children.slice(0, 6).map((child) => ( + +

{child.name}

+

{getLevelName(child.level)}

+ + ))} +
+
+ )} +
+ )} + + {activeTab === 'articles' && ( +
+ {articles.length > 0 ? ( + articles.map((article) => ( + +

{article.title}

+
+ {getContentTypeName(article.content_type)} + {new Date(article.created_at).toLocaleDateString('zh-CN')} +
+ + )) + ) : ( +
暂无文章
+ )} +
+ )} + + {activeTab === 'services' && ( +
+ {services.length > 0 ? ( + services.map((service) => ( + +
+

{service.name}

+ {getCategoryName(service.category)} +
+

{service.description?.substring(0, 100)}...

+ {service.rating_average > 0 && ( +
⭐ {service.rating_average.toFixed(1)} ({service.rating_count}条评价)
+ )} + + )) + ) : ( +
暂无服务
+ )} +
+ )} + + {activeTab === 'subregions' && ( +
+ {children.length > 0 ? ( +
+ {children.map((child) => ( + +

{child.name}

+

{getLevelName(child.level)}

+ + ))} +
+ ) : ( +
暂无下级区域
+ )} +
+ )} +
+
+ ); +} + +function getLevelName(level) { + const map = { + province: '省级', + city: '市级', + county: '县级', + town: '镇级', + village: '村级', + }; + return map[level] || level; +} + +function getContentTypeName(type) { + const map = { + city_info: '城市信息', + history: '历史', + culture: '文化', + practical: '实用信息', + life: '生活指南', + }; + return map[type] || type; +} + +function getCategoryName(category) { + const map = { + clothing: '衣', + food: '食', + housing: '住', + transportation: '行', + entertainment: '娱乐', + tourism: '旅游', + culture: '文化', + }; + return map[category] || category; +} + +export default RegionDetail;