RAHUL-13 commited on
Commit
0a90806
·
unverified ·
1 Parent(s): 2d1e5fc

Delete Downloads/hackathon/civicai directory

Browse files
Downloads/hackathon/civicai/.env.example DELETED
@@ -1,10 +0,0 @@
1
- # Copy this file to .env.local and fill in real values (do NOT commit .env.local)
2
-
3
- ANTHROPIC_API_KEY=sk-your-anthropic-key-here
4
- OPENAI_API_KEY=sk-your-openai-key-here
5
- NEXT_PUBLIC_SUPABASE_URL=https://your-supabase-url.supabase.co
6
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key-here
7
- SENDGRID_API_KEY=SG.your-sendgrid-key-here
8
- ALERT_EMAIL=authority@yourcity.gov.in
9
- LOCATIONIQ_KEY=pk.your-locationiq-key-here
10
- NEXT_PUBLIC_BASE_URL=http://localhost:3000
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/.gitignore DELETED
@@ -1,21 +0,0 @@
1
- # Dependencies
2
- node_modules/
3
- .pnp
4
- .pnp.js
5
-
6
- # Environment variables — NEVER commit these
7
- .env.local
8
- .env.development.local
9
- .env.test.local
10
- .env.production.local
11
-
12
- # Next.js build output
13
- .next/
14
- out/
15
-
16
- # Misc
17
- .DS_Store
18
- *.pem
19
- npm-debug.log*
20
- yarn-debug.log*
21
- yarn-error.log*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/README.md DELETED
@@ -1,292 +0,0 @@
1
- # CitizenCare 🏛️
2
-
3
- An intelligent AI-powered citizen grievance classification and tracking portal that enables seamless complaint submission, real-time status tracking, and efficient authority dashboard management.
4
-
5
- ## 🎯 Features
6
-
7
- - **Multi-Language Support**: Submit complaints in 10 Indian languages (English, Hindi, Telugu, Tamil, Kannada, Marathi, Bengali, Gujarati, Punjabi, Odia)
8
- - **Voice & Text Input**: Record complaints directly or type them in
9
- - **Smart Classification**: AI-powered keyword parser automatically categorizes complaints and assigns departments
10
- - **Urgency Detection**: System intelligently identifies critical issues and triggers instant alerts
11
- - **Real-Time Tracking**: Citizens can track complaint status with unique complaint IDs
12
- - **Authority Dashboard**: Comprehensive dashboard for government officials to manage and resolve complaints
13
- - **Visual Analytics**: Charts and filters to analyze complaint patterns by department and urgency
14
- - **Secure Authentication**: Role-based access control with Supabase Auth
15
- - **Responsive Design**: Beautiful, mobile-friendly UI with vibrant color scheme
16
-
17
- ## 🛠️ Tech Stack
18
-
19
- - **Frontend**: Next.js, React, Tailwind CSS
20
- - **Backend**: Next.js API Routes
21
- - **Database**: Supabase (PostgreSQL) with real-time subscriptions
22
- - **Authentication**: Supabase Auth
23
- - **Voice Recognition**: Browser Web Speech API + OpenAI Whisper fallback
24
- - **Email Alerts**: SendGrid (for critical complaints)
25
- - **Charts**: Chart.js + react-chartjs-2
26
- - **Hosting**: Vercel (recommended)
27
-
28
- ## 📋 System Architecture
29
-
30
- ```
31
- ┌─────────────────┐
32
- │ Citizen Portal │ (index.jsx, support.jsx)
33
- ├─────────────────┤
34
- │ Voice/Text │
35
- │ Input Handler │
36
- └────────┬────────┘
37
-
38
-
39
- ┌─────────────────┐
40
- │ API Processor │ (api/process.js)
41
- │ - Keywords │
42
- │ - Scoring │
43
- │ - Department │
44
- │ - Urgency │
45
- └────────┬────────┘
46
-
47
-
48
- ┌─────────────────┐
49
- │ Database │ (Supabase)
50
- │ - Store │
51
- │ - Realtime │
52
- └────────┬────────┘
53
-
54
-
55
- ┌─────────────────┐
56
- │ Authority │ (dashboard.jsx)
57
- │ Dashboard │
58
- │ - View │
59
- │ - Filter │
60
- │ - Update │
61
- └─────────────────┘
62
- ```
63
-
64
- ## 🚀 Getting Started
65
-
66
- ### Prerequisites
67
- - Node.js 16+ and npm
68
- - Supabase account
69
- - SendGrid account (for alerts)
70
- - Git
71
-
72
- ### Installation
73
-
74
- 1. **Clone the repository**
75
- ```bash
76
- git clone https://github.com/SAI-RAHUL-ROKKAM/CitizenCare.git
77
- cd CitizenCare
78
- ```
79
-
80
- 2. **Install dependencies**
81
- ```bash
82
- npm install
83
- ```
84
-
85
- 3. **Create environment file**
86
- ```bash
87
- cp .env.example .env.local
88
- ```
89
-
90
- 4. **Fill in your environment variables in `.env.local`**
91
- ```
92
- ANTHROPIC_API_KEY=your_key_here
93
- OPENAI_API_KEY=your_key_here
94
- NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
95
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_key
96
- SENDGRID_API_KEY=your_sendgrid_key
97
- ALERT_EMAIL=authority@yourcity.gov.in
98
- LOCATIONIQ_KEY=your_locationiq_key
99
- NEXT_PUBLIC_BASE_URL=http://localhost:3000
100
- ```
101
-
102
- 5. **Run the development server**
103
- ```bash
104
- npm run dev
105
- ```
106
-
107
- 6. **Open in browser**
108
- ```
109
- http://localhost:3000
110
- ```
111
-
112
- ## 📱 Usage
113
-
114
- ### For Citizens
115
- 1. Visit `http://localhost:3000`
116
- 2. Type or record a complaint in any supported language
117
- 3. Click "Submit complaint"
118
- 4. View generated complaint summary, department, and urgency
119
- 5. Copy complaint ID to track status
120
- 6. Use "Track complaint" tab to check real-time updates
121
-
122
- ### For Authorities
123
- 1. Visit `http://localhost:3000/login`
124
- 2. Sign up or use test credentials:
125
- - Email: `authority-test@citizencare.test`
126
- - Password: `Test@1234`
127
- 3. View dashboard with:
128
- - Statistics (total, pending, resolved, critical)
129
- - Charts by department and urgency
130
- - Complaint list with filters
131
- - Click any complaint to view details and update status
132
-
133
- ## 🔐 Security
134
-
135
- - ✅ Environment variables protected in `.env.local` (excluded from Git)
136
- - ✅ `.env.example` contains only dummy keys
137
- - ✅ Supabase Row Level Security (RLS) for database access control
138
- - ✅ Role-based authentication for authority dashboard
139
- - ✅ No API keys exposed in source code
140
- - ✅ SendGrid alerts only for critical complaints
141
-
142
- ## 📁 Project Structure
143
-
144
- ```
145
- CitizenCare/
146
- ├── pages/
147
- │ ├── _app.jsx # App wrapper
148
- │ ├── index.jsx # Citizen portal (submit/track)
149
- │ ├── dashboard.jsx # Authority dashboard
150
- │ ├── login.jsx # Authentication
151
- │ ├── support.jsx # FAQ & help center
152
- │ └── api/
153
- │ ├── process.js # Complaint parser & classifier
154
- │ ├── status.js # Track complaint status
155
- │ ├── alert.js # Send critical alerts
156
- │ └── transcribe.js # Voice transcription
157
- ├── lib/
158
- │ └── supabase.js # Supabase client config
159
- ├── styles/
160
- │ └── globals.css # Global styles & animations
161
- ├── .env.example # Environment template
162
- ├── .gitignore # Git ignore rules
163
- ├── package.json # Dependencies
164
- ├── tailwind.config.js # Tailwind configuration
165
- └── postcss.config.js # PostCSS configuration
166
- ```
167
-
168
- ## 🧠 How It Works
169
-
170
- ### Complaint Processing Flow
171
-
172
- 1. **Input**: Citizen submits text/voice in any language
173
- 2. **Detection**: System detects language from Unicode script
174
- 3. **Scoring**: Multi-keyword scoring system finds best department match
175
- 4. **Rules**: Override rules for critical medical/safety terms
176
- 5. **Extraction**: Location parsing using regex patterns
177
- 6. **Classification**: Department, urgency, and action items assigned
178
- 7. **Storage**: Complaint saved to Supabase with `status: pending`
179
- 8. **Alerts**: If critical → SendGrid email sent to authority
180
- 9. **Tracking**: Citizen gets unique ID to track status
181
-
182
- ### Department Keywords
183
-
184
- System supports keywords in 10 languages for:
185
- - Health, Hospital
186
- - Police
187
- - Sanitation
188
- - Roads, Water, Electricity
189
- - Education
190
- - Transport
191
- - Municipal
192
- - Environment
193
- - Rescue
194
- - Other (fallback)
195
-
196
- ## 🎓 Example Test Cases
197
-
198
- ### Test 1: Health Emergency ✅
199
- **Input**: "my teacher got a heart attack our school is at lalitha nagar , rajahmundry"
200
- **Result**:
201
- - Department: Health
202
- - Urgency: Critical
203
- - ETA: 12 hours
204
-
205
- ### Test 2: Road Issue ✅
206
- **Input**: "large pothole near Ramaiah School on MG Road. Two bikes fell last week"
207
- **Result**:
208
- - Department: Roads
209
- - Urgency: High
210
- - ETA: 24-48 hours
211
-
212
- ### Test 3: Sanitation ✅
213
- **Input**: "गंदगी और कचरे के ढेर हमारे इलाके में"
214
- **Result**:
215
- - Department: Sanitation
216
- - Urgency: Medium
217
- - ETA: 3-5 days
218
-
219
- ## 🚀 Deployment
220
-
221
- ### Deploy to Vercel (Recommended)
222
-
223
- 1. Push code to GitHub
224
- 2. Connect repo to Vercel
225
- 3. Add environment variables in Vercel dashboard
226
- 4. Deploy automatically on push
227
-
228
- ```bash
229
- git push origin main
230
- # Vercel auto-deploys
231
- ```
232
-
233
- ### Environment Variables for Production
234
- Set these in your hosting platform (Vercel, Netlify, etc.):
235
- - `ANTHROPIC_API_KEY`
236
- - `OPENAI_API_KEY`
237
- - `NEXT_PUBLIC_SUPABASE_URL`
238
- - `NEXT_PUBLIC_SUPABASE_ANON_KEY`
239
- - `SENDGRID_API_KEY`
240
- - `ALERT_EMAIL`
241
- - `LOCATIONIQ_KEY`
242
- - `NEXT_PUBLIC_BASE_URL` (your domain)
243
-
244
- ## 📊 Performance
245
-
246
- - **Processing Time**: < 3 seconds for complaint classification
247
- - **Database**: Real-time sync using Supabase subscriptions
248
- - **Voice Input**: Supports 10 languages with offline fallback
249
- - **Responsive**: Mobile, tablet, and desktop optimized
250
-
251
- ## 🔮 Future Enhancements
252
-
253
- - [ ] ML/NLP model for intent classification
254
- - [ ] Map-based geo-visualization of complaints
255
- - [ ] SMS/WhatsApp citizen notifications
256
- - [ ] Advanced admin dashboard with SLA tracking
257
- - [ ] Automated translation to English
258
- - [ ] Chat support interface
259
- - [ ] Gamification (badges/credits for citizens)
260
- - [ ] Photo/video attachments
261
- - [ ] Integration with government databases
262
- - [ ] Analytics and reporting tools
263
-
264
- ## 🤝 Contributing
265
-
266
- Contributions are welcome! Please:
267
- 1. Fork the repository
268
- 2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
269
- 3. Commit changes (`git commit -m 'Add AmazingFeature'`)
270
- 4. Push to branch (`git push origin feature/AmazingFeature`)
271
- 5. Open a Pull Request
272
-
273
- ## 📝 License
274
-
275
- This project is open source and available under the MIT License.
276
-
277
- ## 📞 Support
278
-
279
- For issues or questions:
280
- - Create an issue on GitHub
281
- - Check the FAQ in `/support` page
282
- - Visit help center at `http://localhost:3000/support`
283
-
284
- ## 👨‍💻 Authors
285
-
286
- - **SAI RAHUL ROKKAM** - Initial development
287
-
288
- Built for the National Hackathon 2026
289
-
290
- ---
291
-
292
- **Made with ❤️ for better citizen-government communication**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/lib/supabase.js DELETED
@@ -1,6 +0,0 @@
1
- import { createClient } from '@supabase/supabase-js'
2
-
3
- export const supabase = createClient(
4
- process.env.NEXT_PUBLIC_SUPABASE_URL,
5
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
6
- )
 
 
 
 
 
 
 
Downloads/hackathon/civicai/package-lock.json DELETED
@@ -1,2130 +0,0 @@
1
- {
2
- "name": "civicai",
3
- "version": "0.1.0",
4
- "lockfileVersion": 3,
5
- "requires": true,
6
- "packages": {
7
- "": {
8
- "name": "civicai",
9
- "version": "0.1.0",
10
- "dependencies": {
11
- "@sendgrid/mail": "^8.1.6",
12
- "@supabase/supabase-js": "^2.100.1",
13
- "chart.js": "^4.5.1",
14
- "form-data": "^4.0.0",
15
- "formidable": "^3.5.1",
16
- "git": "^0.1.5",
17
- "next": "^14.0.0",
18
- "react": "^18.0.0",
19
- "react-chartjs-2": "^5.3.1",
20
- "react-dom": "^18.0.0",
21
- "uuid": "^9.0.1"
22
- },
23
- "devDependencies": {
24
- "autoprefixer": "^10.4.16",
25
- "postcss": "^8.4.32",
26
- "tailwindcss": "^3.4.0"
27
- }
28
- },
29
- "node_modules/@alloc/quick-lru": {
30
- "version": "5.2.0",
31
- "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
32
- "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
33
- "dev": true,
34
- "license": "MIT",
35
- "engines": {
36
- "node": ">=10"
37
- },
38
- "funding": {
39
- "url": "https://github.com/sponsors/sindresorhus"
40
- }
41
- },
42
- "node_modules/@jridgewell/gen-mapping": {
43
- "version": "0.3.13",
44
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
45
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
46
- "dev": true,
47
- "license": "MIT",
48
- "dependencies": {
49
- "@jridgewell/sourcemap-codec": "^1.5.0",
50
- "@jridgewell/trace-mapping": "^0.3.24"
51
- }
52
- },
53
- "node_modules/@jridgewell/resolve-uri": {
54
- "version": "3.1.2",
55
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
56
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
57
- "dev": true,
58
- "license": "MIT",
59
- "engines": {
60
- "node": ">=6.0.0"
61
- }
62
- },
63
- "node_modules/@jridgewell/sourcemap-codec": {
64
- "version": "1.5.5",
65
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
66
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
67
- "dev": true,
68
- "license": "MIT"
69
- },
70
- "node_modules/@jridgewell/trace-mapping": {
71
- "version": "0.3.31",
72
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
73
- "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
74
- "dev": true,
75
- "license": "MIT",
76
- "dependencies": {
77
- "@jridgewell/resolve-uri": "^3.1.0",
78
- "@jridgewell/sourcemap-codec": "^1.4.14"
79
- }
80
- },
81
- "node_modules/@kurkle/color": {
82
- "version": "0.3.4",
83
- "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
84
- "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
85
- "license": "MIT"
86
- },
87
- "node_modules/@next/env": {
88
- "version": "14.2.35",
89
- "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz",
90
- "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==",
91
- "license": "MIT"
92
- },
93
- "node_modules/@next/swc-darwin-arm64": {
94
- "version": "14.2.33",
95
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz",
96
- "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==",
97
- "cpu": [
98
- "arm64"
99
- ],
100
- "license": "MIT",
101
- "optional": true,
102
- "os": [
103
- "darwin"
104
- ],
105
- "engines": {
106
- "node": ">= 10"
107
- }
108
- },
109
- "node_modules/@next/swc-darwin-x64": {
110
- "version": "14.2.33",
111
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz",
112
- "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==",
113
- "cpu": [
114
- "x64"
115
- ],
116
- "license": "MIT",
117
- "optional": true,
118
- "os": [
119
- "darwin"
120
- ],
121
- "engines": {
122
- "node": ">= 10"
123
- }
124
- },
125
- "node_modules/@next/swc-linux-arm64-gnu": {
126
- "version": "14.2.33",
127
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz",
128
- "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==",
129
- "cpu": [
130
- "arm64"
131
- ],
132
- "license": "MIT",
133
- "optional": true,
134
- "os": [
135
- "linux"
136
- ],
137
- "engines": {
138
- "node": ">= 10"
139
- }
140
- },
141
- "node_modules/@next/swc-linux-arm64-musl": {
142
- "version": "14.2.33",
143
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz",
144
- "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==",
145
- "cpu": [
146
- "arm64"
147
- ],
148
- "license": "MIT",
149
- "optional": true,
150
- "os": [
151
- "linux"
152
- ],
153
- "engines": {
154
- "node": ">= 10"
155
- }
156
- },
157
- "node_modules/@next/swc-linux-x64-gnu": {
158
- "version": "14.2.33",
159
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz",
160
- "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==",
161
- "cpu": [
162
- "x64"
163
- ],
164
- "license": "MIT",
165
- "optional": true,
166
- "os": [
167
- "linux"
168
- ],
169
- "engines": {
170
- "node": ">= 10"
171
- }
172
- },
173
- "node_modules/@next/swc-linux-x64-musl": {
174
- "version": "14.2.33",
175
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz",
176
- "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==",
177
- "cpu": [
178
- "x64"
179
- ],
180
- "license": "MIT",
181
- "optional": true,
182
- "os": [
183
- "linux"
184
- ],
185
- "engines": {
186
- "node": ">= 10"
187
- }
188
- },
189
- "node_modules/@next/swc-win32-arm64-msvc": {
190
- "version": "14.2.33",
191
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz",
192
- "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==",
193
- "cpu": [
194
- "arm64"
195
- ],
196
- "license": "MIT",
197
- "optional": true,
198
- "os": [
199
- "win32"
200
- ],
201
- "engines": {
202
- "node": ">= 10"
203
- }
204
- },
205
- "node_modules/@next/swc-win32-ia32-msvc": {
206
- "version": "14.2.33",
207
- "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz",
208
- "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==",
209
- "cpu": [
210
- "ia32"
211
- ],
212
- "license": "MIT",
213
- "optional": true,
214
- "os": [
215
- "win32"
216
- ],
217
- "engines": {
218
- "node": ">= 10"
219
- }
220
- },
221
- "node_modules/@next/swc-win32-x64-msvc": {
222
- "version": "14.2.33",
223
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz",
224
- "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==",
225
- "cpu": [
226
- "x64"
227
- ],
228
- "license": "MIT",
229
- "optional": true,
230
- "os": [
231
- "win32"
232
- ],
233
- "engines": {
234
- "node": ">= 10"
235
- }
236
- },
237
- "node_modules/@noble/hashes": {
238
- "version": "1.8.0",
239
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
240
- "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
241
- "license": "MIT",
242
- "engines": {
243
- "node": "^14.21.3 || >=16"
244
- },
245
- "funding": {
246
- "url": "https://paulmillr.com/funding/"
247
- }
248
- },
249
- "node_modules/@nodelib/fs.scandir": {
250
- "version": "2.1.5",
251
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
252
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
253
- "dev": true,
254
- "license": "MIT",
255
- "dependencies": {
256
- "@nodelib/fs.stat": "2.0.5",
257
- "run-parallel": "^1.1.9"
258
- },
259
- "engines": {
260
- "node": ">= 8"
261
- }
262
- },
263
- "node_modules/@nodelib/fs.stat": {
264
- "version": "2.0.5",
265
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
266
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
267
- "dev": true,
268
- "license": "MIT",
269
- "engines": {
270
- "node": ">= 8"
271
- }
272
- },
273
- "node_modules/@nodelib/fs.walk": {
274
- "version": "1.2.8",
275
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
276
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
277
- "dev": true,
278
- "license": "MIT",
279
- "dependencies": {
280
- "@nodelib/fs.scandir": "2.1.5",
281
- "fastq": "^1.6.0"
282
- },
283
- "engines": {
284
- "node": ">= 8"
285
- }
286
- },
287
- "node_modules/@paralleldrive/cuid2": {
288
- "version": "2.3.1",
289
- "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
290
- "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
291
- "license": "MIT",
292
- "dependencies": {
293
- "@noble/hashes": "^1.1.5"
294
- }
295
- },
296
- "node_modules/@sendgrid/client": {
297
- "version": "8.1.6",
298
- "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.6.tgz",
299
- "integrity": "sha512-/BHu0hqwXNHr2aLhcXU7RmmlVqrdfrbY9KpaNj00KZHlVOVoRxRVrpOCabIB+91ISXJ6+mLM9vpaVUhK6TwBWA==",
300
- "license": "MIT",
301
- "dependencies": {
302
- "@sendgrid/helpers": "^8.0.0",
303
- "axios": "^1.12.0"
304
- },
305
- "engines": {
306
- "node": ">=12.*"
307
- }
308
- },
309
- "node_modules/@sendgrid/helpers": {
310
- "version": "8.0.0",
311
- "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz",
312
- "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==",
313
- "license": "MIT",
314
- "dependencies": {
315
- "deepmerge": "^4.2.2"
316
- },
317
- "engines": {
318
- "node": ">= 12.0.0"
319
- }
320
- },
321
- "node_modules/@sendgrid/mail": {
322
- "version": "8.1.6",
323
- "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.6.tgz",
324
- "integrity": "sha512-/ZqxUvKeEztU9drOoPC/8opEPOk+jLlB2q4+xpx6HVLq6aFu3pMpalkTpAQz8XfRfpLp8O25bh6pGPcHDCYpqg==",
325
- "license": "MIT",
326
- "dependencies": {
327
- "@sendgrid/client": "^8.1.5",
328
- "@sendgrid/helpers": "^8.0.0"
329
- },
330
- "engines": {
331
- "node": ">=12.*"
332
- }
333
- },
334
- "node_modules/@supabase/auth-js": {
335
- "version": "2.100.1",
336
- "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.100.1.tgz",
337
- "integrity": "sha512-c5FB4nrG7cs1mLSzFGuIVl2iR2YO5XkSJ96uF4zubYm8YDn71XOi2emE9sBm/avfGCj61jaRBLOvxEAVnpys0Q==",
338
- "license": "MIT",
339
- "dependencies": {
340
- "tslib": "2.8.1"
341
- },
342
- "engines": {
343
- "node": ">=20.0.0"
344
- }
345
- },
346
- "node_modules/@supabase/functions-js": {
347
- "version": "2.100.1",
348
- "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.100.1.tgz",
349
- "integrity": "sha512-mo8QheoV4KR+wSubtyEWhZUxWnCM7YZ23TncccMAlbWAHb8YTDqRGRm9IalWCAswniKyud6buZCk9snRqI86KA==",
350
- "license": "MIT",
351
- "dependencies": {
352
- "tslib": "2.8.1"
353
- },
354
- "engines": {
355
- "node": ">=20.0.0"
356
- }
357
- },
358
- "node_modules/@supabase/phoenix": {
359
- "version": "0.4.0",
360
- "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.0.tgz",
361
- "integrity": "sha512-RHSx8bHS02xwfHdAbX5Lpbo6PXbgyf7lTaXTlwtFDPwOIw64NnVRwFAXGojHhjtVYI+PEPNSWwkL90f4agN3bw==",
362
- "license": "MIT"
363
- },
364
- "node_modules/@supabase/postgrest-js": {
365
- "version": "2.100.1",
366
- "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.100.1.tgz",
367
- "integrity": "sha512-OIh4mOSo2LdqF2kox76OAPDtcSs+PwKABJOjc6plUV4/LXhFEsI2uwdEEIs7K7fd141qehWEVl/Y+Ts0fNvYsw==",
368
- "license": "MIT",
369
- "dependencies": {
370
- "tslib": "2.8.1"
371
- },
372
- "engines": {
373
- "node": ">=20.0.0"
374
- }
375
- },
376
- "node_modules/@supabase/realtime-js": {
377
- "version": "2.100.1",
378
- "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.100.1.tgz",
379
- "integrity": "sha512-FHuRWPX4qZQ4x+0Q+ZrKaBZnOiVGiwsgiAUJM98pYRib1yeaE/fOM1lZ1ozd+4gA8Udw23OyaD8SxKS5mT5NYw==",
380
- "license": "MIT",
381
- "dependencies": {
382
- "@supabase/phoenix": "^0.4.0",
383
- "@types/ws": "^8.18.1",
384
- "tslib": "2.8.1",
385
- "ws": "^8.18.2"
386
- },
387
- "engines": {
388
- "node": ">=20.0.0"
389
- }
390
- },
391
- "node_modules/@supabase/storage-js": {
392
- "version": "2.100.1",
393
- "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.100.1.tgz",
394
- "integrity": "sha512-x9xpEIoWM4xKiAlwfWTgHPSN6N4Y0aS4FVU4F6ZPbq7Gayw08SrtC6/YH/gOr8CjXQr0HxXYXDop2xGTSjubYA==",
395
- "license": "MIT",
396
- "dependencies": {
397
- "iceberg-js": "^0.8.1",
398
- "tslib": "2.8.1"
399
- },
400
- "engines": {
401
- "node": ">=20.0.0"
402
- }
403
- },
404
- "node_modules/@supabase/supabase-js": {
405
- "version": "2.100.1",
406
- "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.100.1.tgz",
407
- "integrity": "sha512-CAeFm5sfX8sbTzxoxRafhohreIzl9a7R6qHTck3MrgTqm5M5g/u0IHfEKYzI9w/17r8NINl8UZrw2i08wrO7Iw==",
408
- "license": "MIT",
409
- "dependencies": {
410
- "@supabase/auth-js": "2.100.1",
411
- "@supabase/functions-js": "2.100.1",
412
- "@supabase/postgrest-js": "2.100.1",
413
- "@supabase/realtime-js": "2.100.1",
414
- "@supabase/storage-js": "2.100.1"
415
- },
416
- "engines": {
417
- "node": ">=20.0.0"
418
- }
419
- },
420
- "node_modules/@swc/counter": {
421
- "version": "0.1.3",
422
- "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
423
- "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
424
- "license": "Apache-2.0"
425
- },
426
- "node_modules/@swc/helpers": {
427
- "version": "0.5.5",
428
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
429
- "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
430
- "license": "Apache-2.0",
431
- "dependencies": {
432
- "@swc/counter": "^0.1.3",
433
- "tslib": "^2.4.0"
434
- }
435
- },
436
- "node_modules/@types/node": {
437
- "version": "25.5.0",
438
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
439
- "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
440
- "license": "MIT",
441
- "dependencies": {
442
- "undici-types": "~7.18.0"
443
- }
444
- },
445
- "node_modules/@types/ws": {
446
- "version": "8.18.1",
447
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
448
- "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
449
- "license": "MIT",
450
- "dependencies": {
451
- "@types/node": "*"
452
- }
453
- },
454
- "node_modules/any-promise": {
455
- "version": "1.3.0",
456
- "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
457
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
458
- "dev": true,
459
- "license": "MIT"
460
- },
461
- "node_modules/anymatch": {
462
- "version": "3.1.3",
463
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
464
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
465
- "dev": true,
466
- "license": "ISC",
467
- "dependencies": {
468
- "normalize-path": "^3.0.0",
469
- "picomatch": "^2.0.4"
470
- },
471
- "engines": {
472
- "node": ">= 8"
473
- }
474
- },
475
- "node_modules/arg": {
476
- "version": "5.0.2",
477
- "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
478
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
479
- "dev": true,
480
- "license": "MIT"
481
- },
482
- "node_modules/asap": {
483
- "version": "2.0.6",
484
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
485
- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
486
- "license": "MIT"
487
- },
488
- "node_modules/asynckit": {
489
- "version": "0.4.0",
490
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
491
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
492
- "license": "MIT"
493
- },
494
- "node_modules/autoprefixer": {
495
- "version": "10.4.27",
496
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
497
- "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
498
- "dev": true,
499
- "funding": [
500
- {
501
- "type": "opencollective",
502
- "url": "https://opencollective.com/postcss/"
503
- },
504
- {
505
- "type": "tidelift",
506
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
507
- },
508
- {
509
- "type": "github",
510
- "url": "https://github.com/sponsors/ai"
511
- }
512
- ],
513
- "license": "MIT",
514
- "dependencies": {
515
- "browserslist": "^4.28.1",
516
- "caniuse-lite": "^1.0.30001774",
517
- "fraction.js": "^5.3.4",
518
- "picocolors": "^1.1.1",
519
- "postcss-value-parser": "^4.2.0"
520
- },
521
- "bin": {
522
- "autoprefixer": "bin/autoprefixer"
523
- },
524
- "engines": {
525
- "node": "^10 || ^12 || >=14"
526
- },
527
- "peerDependencies": {
528
- "postcss": "^8.1.0"
529
- }
530
- },
531
- "node_modules/axios": {
532
- "version": "1.14.0",
533
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz",
534
- "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==",
535
- "license": "MIT",
536
- "dependencies": {
537
- "follow-redirects": "^1.15.11",
538
- "form-data": "^4.0.5",
539
- "proxy-from-env": "^2.1.0"
540
- }
541
- },
542
- "node_modules/baseline-browser-mapping": {
543
- "version": "2.10.11",
544
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz",
545
- "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==",
546
- "dev": true,
547
- "license": "Apache-2.0",
548
- "bin": {
549
- "baseline-browser-mapping": "dist/cli.cjs"
550
- },
551
- "engines": {
552
- "node": ">=6.0.0"
553
- }
554
- },
555
- "node_modules/binary-extensions": {
556
- "version": "2.3.0",
557
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
558
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
559
- "dev": true,
560
- "license": "MIT",
561
- "engines": {
562
- "node": ">=8"
563
- },
564
- "funding": {
565
- "url": "https://github.com/sponsors/sindresorhus"
566
- }
567
- },
568
- "node_modules/braces": {
569
- "version": "3.0.3",
570
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
571
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
572
- "dev": true,
573
- "license": "MIT",
574
- "dependencies": {
575
- "fill-range": "^7.1.1"
576
- },
577
- "engines": {
578
- "node": ">=8"
579
- }
580
- },
581
- "node_modules/browserslist": {
582
- "version": "4.28.1",
583
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
584
- "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
585
- "dev": true,
586
- "funding": [
587
- {
588
- "type": "opencollective",
589
- "url": "https://opencollective.com/browserslist"
590
- },
591
- {
592
- "type": "tidelift",
593
- "url": "https://tidelift.com/funding/github/npm/browserslist"
594
- },
595
- {
596
- "type": "github",
597
- "url": "https://github.com/sponsors/ai"
598
- }
599
- ],
600
- "license": "MIT",
601
- "dependencies": {
602
- "baseline-browser-mapping": "^2.9.0",
603
- "caniuse-lite": "^1.0.30001759",
604
- "electron-to-chromium": "^1.5.263",
605
- "node-releases": "^2.0.27",
606
- "update-browserslist-db": "^1.2.0"
607
- },
608
- "bin": {
609
- "browserslist": "cli.js"
610
- },
611
- "engines": {
612
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
613
- }
614
- },
615
- "node_modules/busboy": {
616
- "version": "1.6.0",
617
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
618
- "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
619
- "dependencies": {
620
- "streamsearch": "^1.1.0"
621
- },
622
- "engines": {
623
- "node": ">=10.16.0"
624
- }
625
- },
626
- "node_modules/call-bind-apply-helpers": {
627
- "version": "1.0.2",
628
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
629
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
630
- "license": "MIT",
631
- "dependencies": {
632
- "es-errors": "^1.3.0",
633
- "function-bind": "^1.1.2"
634
- },
635
- "engines": {
636
- "node": ">= 0.4"
637
- }
638
- },
639
- "node_modules/camelcase-css": {
640
- "version": "2.0.1",
641
- "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
642
- "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
643
- "dev": true,
644
- "license": "MIT",
645
- "engines": {
646
- "node": ">= 6"
647
- }
648
- },
649
- "node_modules/caniuse-lite": {
650
- "version": "1.0.30001781",
651
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz",
652
- "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==",
653
- "funding": [
654
- {
655
- "type": "opencollective",
656
- "url": "https://opencollective.com/browserslist"
657
- },
658
- {
659
- "type": "tidelift",
660
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
661
- },
662
- {
663
- "type": "github",
664
- "url": "https://github.com/sponsors/ai"
665
- }
666
- ],
667
- "license": "CC-BY-4.0"
668
- },
669
- "node_modules/chart.js": {
670
- "version": "4.5.1",
671
- "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
672
- "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
673
- "license": "MIT",
674
- "dependencies": {
675
- "@kurkle/color": "^0.3.0"
676
- },
677
- "engines": {
678
- "pnpm": ">=8"
679
- }
680
- },
681
- "node_modules/chokidar": {
682
- "version": "3.6.0",
683
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
684
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
685
- "dev": true,
686
- "license": "MIT",
687
- "dependencies": {
688
- "anymatch": "~3.1.2",
689
- "braces": "~3.0.2",
690
- "glob-parent": "~5.1.2",
691
- "is-binary-path": "~2.1.0",
692
- "is-glob": "~4.0.1",
693
- "normalize-path": "~3.0.0",
694
- "readdirp": "~3.6.0"
695
- },
696
- "engines": {
697
- "node": ">= 8.10.0"
698
- },
699
- "funding": {
700
- "url": "https://paulmillr.com/funding/"
701
- },
702
- "optionalDependencies": {
703
- "fsevents": "~2.3.2"
704
- }
705
- },
706
- "node_modules/chokidar/node_modules/glob-parent": {
707
- "version": "5.1.2",
708
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
709
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
710
- "dev": true,
711
- "license": "ISC",
712
- "dependencies": {
713
- "is-glob": "^4.0.1"
714
- },
715
- "engines": {
716
- "node": ">= 6"
717
- }
718
- },
719
- "node_modules/client-only": {
720
- "version": "0.0.1",
721
- "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
722
- "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
723
- "license": "MIT"
724
- },
725
- "node_modules/combined-stream": {
726
- "version": "1.0.8",
727
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
728
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
729
- "license": "MIT",
730
- "dependencies": {
731
- "delayed-stream": "~1.0.0"
732
- },
733
- "engines": {
734
- "node": ">= 0.8"
735
- }
736
- },
737
- "node_modules/commander": {
738
- "version": "4.1.1",
739
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
740
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
741
- "dev": true,
742
- "license": "MIT",
743
- "engines": {
744
- "node": ">= 6"
745
- }
746
- },
747
- "node_modules/cssesc": {
748
- "version": "3.0.0",
749
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
750
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
751
- "dev": true,
752
- "license": "MIT",
753
- "bin": {
754
- "cssesc": "bin/cssesc"
755
- },
756
- "engines": {
757
- "node": ">=4"
758
- }
759
- },
760
- "node_modules/deepmerge": {
761
- "version": "4.3.1",
762
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
763
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
764
- "license": "MIT",
765
- "engines": {
766
- "node": ">=0.10.0"
767
- }
768
- },
769
- "node_modules/delayed-stream": {
770
- "version": "1.0.0",
771
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
772
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
773
- "license": "MIT",
774
- "engines": {
775
- "node": ">=0.4.0"
776
- }
777
- },
778
- "node_modules/dezalgo": {
779
- "version": "1.0.4",
780
- "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
781
- "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
782
- "license": "ISC",
783
- "dependencies": {
784
- "asap": "^2.0.0",
785
- "wrappy": "1"
786
- }
787
- },
788
- "node_modules/didyoumean": {
789
- "version": "1.2.2",
790
- "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
791
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
792
- "dev": true,
793
- "license": "Apache-2.0"
794
- },
795
- "node_modules/dlv": {
796
- "version": "1.1.3",
797
- "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
798
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
799
- "dev": true,
800
- "license": "MIT"
801
- },
802
- "node_modules/dunder-proto": {
803
- "version": "1.0.1",
804
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
805
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
806
- "license": "MIT",
807
- "dependencies": {
808
- "call-bind-apply-helpers": "^1.0.1",
809
- "es-errors": "^1.3.0",
810
- "gopd": "^1.2.0"
811
- },
812
- "engines": {
813
- "node": ">= 0.4"
814
- }
815
- },
816
- "node_modules/electron-to-chromium": {
817
- "version": "1.5.328",
818
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz",
819
- "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==",
820
- "dev": true,
821
- "license": "ISC"
822
- },
823
- "node_modules/es-define-property": {
824
- "version": "1.0.1",
825
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
826
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
827
- "license": "MIT",
828
- "engines": {
829
- "node": ">= 0.4"
830
- }
831
- },
832
- "node_modules/es-errors": {
833
- "version": "1.3.0",
834
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
835
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
836
- "license": "MIT",
837
- "engines": {
838
- "node": ">= 0.4"
839
- }
840
- },
841
- "node_modules/es-object-atoms": {
842
- "version": "1.1.1",
843
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
844
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
845
- "license": "MIT",
846
- "dependencies": {
847
- "es-errors": "^1.3.0"
848
- },
849
- "engines": {
850
- "node": ">= 0.4"
851
- }
852
- },
853
- "node_modules/es-set-tostringtag": {
854
- "version": "2.1.0",
855
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
856
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
857
- "license": "MIT",
858
- "dependencies": {
859
- "es-errors": "^1.3.0",
860
- "get-intrinsic": "^1.2.6",
861
- "has-tostringtag": "^1.0.2",
862
- "hasown": "^2.0.2"
863
- },
864
- "engines": {
865
- "node": ">= 0.4"
866
- }
867
- },
868
- "node_modules/escalade": {
869
- "version": "3.2.0",
870
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
871
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
872
- "dev": true,
873
- "license": "MIT",
874
- "engines": {
875
- "node": ">=6"
876
- }
877
- },
878
- "node_modules/fast-glob": {
879
- "version": "3.3.3",
880
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
881
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
882
- "dev": true,
883
- "license": "MIT",
884
- "dependencies": {
885
- "@nodelib/fs.stat": "^2.0.2",
886
- "@nodelib/fs.walk": "^1.2.3",
887
- "glob-parent": "^5.1.2",
888
- "merge2": "^1.3.0",
889
- "micromatch": "^4.0.8"
890
- },
891
- "engines": {
892
- "node": ">=8.6.0"
893
- }
894
- },
895
- "node_modules/fast-glob/node_modules/glob-parent": {
896
- "version": "5.1.2",
897
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
898
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
899
- "dev": true,
900
- "license": "ISC",
901
- "dependencies": {
902
- "is-glob": "^4.0.1"
903
- },
904
- "engines": {
905
- "node": ">= 6"
906
- }
907
- },
908
- "node_modules/fastq": {
909
- "version": "1.20.1",
910
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
911
- "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
912
- "dev": true,
913
- "license": "ISC",
914
- "dependencies": {
915
- "reusify": "^1.0.4"
916
- }
917
- },
918
- "node_modules/fill-range": {
919
- "version": "7.1.1",
920
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
921
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
922
- "dev": true,
923
- "license": "MIT",
924
- "dependencies": {
925
- "to-regex-range": "^5.0.1"
926
- },
927
- "engines": {
928
- "node": ">=8"
929
- }
930
- },
931
- "node_modules/follow-redirects": {
932
- "version": "1.15.11",
933
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
934
- "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
935
- "funding": [
936
- {
937
- "type": "individual",
938
- "url": "https://github.com/sponsors/RubenVerborgh"
939
- }
940
- ],
941
- "license": "MIT",
942
- "engines": {
943
- "node": ">=4.0"
944
- },
945
- "peerDependenciesMeta": {
946
- "debug": {
947
- "optional": true
948
- }
949
- }
950
- },
951
- "node_modules/form-data": {
952
- "version": "4.0.5",
953
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
954
- "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
955
- "license": "MIT",
956
- "dependencies": {
957
- "asynckit": "^0.4.0",
958
- "combined-stream": "^1.0.8",
959
- "es-set-tostringtag": "^2.1.0",
960
- "hasown": "^2.0.2",
961
- "mime-types": "^2.1.12"
962
- },
963
- "engines": {
964
- "node": ">= 6"
965
- }
966
- },
967
- "node_modules/formidable": {
968
- "version": "3.5.4",
969
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
970
- "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
971
- "license": "MIT",
972
- "dependencies": {
973
- "@paralleldrive/cuid2": "^2.2.2",
974
- "dezalgo": "^1.0.4",
975
- "once": "^1.4.0"
976
- },
977
- "engines": {
978
- "node": ">=14.0.0"
979
- },
980
- "funding": {
981
- "url": "https://ko-fi.com/tunnckoCore/commissions"
982
- }
983
- },
984
- "node_modules/fraction.js": {
985
- "version": "5.3.4",
986
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
987
- "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
988
- "dev": true,
989
- "license": "MIT",
990
- "engines": {
991
- "node": "*"
992
- },
993
- "funding": {
994
- "type": "github",
995
- "url": "https://github.com/sponsors/rawify"
996
- }
997
- },
998
- "node_modules/fsevents": {
999
- "version": "2.3.3",
1000
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1001
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1002
- "dev": true,
1003
- "hasInstallScript": true,
1004
- "license": "MIT",
1005
- "optional": true,
1006
- "os": [
1007
- "darwin"
1008
- ],
1009
- "engines": {
1010
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1011
- }
1012
- },
1013
- "node_modules/function-bind": {
1014
- "version": "1.1.2",
1015
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1016
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1017
- "license": "MIT",
1018
- "funding": {
1019
- "url": "https://github.com/sponsors/ljharb"
1020
- }
1021
- },
1022
- "node_modules/get-intrinsic": {
1023
- "version": "1.3.0",
1024
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
1025
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
1026
- "license": "MIT",
1027
- "dependencies": {
1028
- "call-bind-apply-helpers": "^1.0.2",
1029
- "es-define-property": "^1.0.1",
1030
- "es-errors": "^1.3.0",
1031
- "es-object-atoms": "^1.1.1",
1032
- "function-bind": "^1.1.2",
1033
- "get-proto": "^1.0.1",
1034
- "gopd": "^1.2.0",
1035
- "has-symbols": "^1.1.0",
1036
- "hasown": "^2.0.2",
1037
- "math-intrinsics": "^1.1.0"
1038
- },
1039
- "engines": {
1040
- "node": ">= 0.4"
1041
- },
1042
- "funding": {
1043
- "url": "https://github.com/sponsors/ljharb"
1044
- }
1045
- },
1046
- "node_modules/get-proto": {
1047
- "version": "1.0.1",
1048
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
1049
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1050
- "license": "MIT",
1051
- "dependencies": {
1052
- "dunder-proto": "^1.0.1",
1053
- "es-object-atoms": "^1.0.0"
1054
- },
1055
- "engines": {
1056
- "node": ">= 0.4"
1057
- }
1058
- },
1059
- "node_modules/git": {
1060
- "version": "0.1.5",
1061
- "resolved": "https://registry.npmjs.org/git/-/git-0.1.5.tgz",
1062
- "integrity": "sha512-N+bfOrXyKMU/fQtCj6D/U9MQOEN0DAA8TLHSLdUQRSWBOkeRvsjJHdrdkvcq05xO7GSDKWc3nDEGoTZ4DfCCSg==",
1063
- "dependencies": {
1064
- "mime": "1.2.9"
1065
- },
1066
- "engines": {
1067
- "node": ">=0.4.0"
1068
- }
1069
- },
1070
- "node_modules/glob-parent": {
1071
- "version": "6.0.2",
1072
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
1073
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
1074
- "dev": true,
1075
- "license": "ISC",
1076
- "dependencies": {
1077
- "is-glob": "^4.0.3"
1078
- },
1079
- "engines": {
1080
- "node": ">=10.13.0"
1081
- }
1082
- },
1083
- "node_modules/gopd": {
1084
- "version": "1.2.0",
1085
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
1086
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
1087
- "license": "MIT",
1088
- "engines": {
1089
- "node": ">= 0.4"
1090
- },
1091
- "funding": {
1092
- "url": "https://github.com/sponsors/ljharb"
1093
- }
1094
- },
1095
- "node_modules/graceful-fs": {
1096
- "version": "4.2.11",
1097
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
1098
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
1099
- "license": "ISC"
1100
- },
1101
- "node_modules/has-symbols": {
1102
- "version": "1.1.0",
1103
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
1104
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
1105
- "license": "MIT",
1106
- "engines": {
1107
- "node": ">= 0.4"
1108
- },
1109
- "funding": {
1110
- "url": "https://github.com/sponsors/ljharb"
1111
- }
1112
- },
1113
- "node_modules/has-tostringtag": {
1114
- "version": "1.0.2",
1115
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
1116
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
1117
- "license": "MIT",
1118
- "dependencies": {
1119
- "has-symbols": "^1.0.3"
1120
- },
1121
- "engines": {
1122
- "node": ">= 0.4"
1123
- },
1124
- "funding": {
1125
- "url": "https://github.com/sponsors/ljharb"
1126
- }
1127
- },
1128
- "node_modules/hasown": {
1129
- "version": "2.0.2",
1130
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
1131
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1132
- "license": "MIT",
1133
- "dependencies": {
1134
- "function-bind": "^1.1.2"
1135
- },
1136
- "engines": {
1137
- "node": ">= 0.4"
1138
- }
1139
- },
1140
- "node_modules/iceberg-js": {
1141
- "version": "0.8.1",
1142
- "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
1143
- "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
1144
- "license": "MIT",
1145
- "engines": {
1146
- "node": ">=20.0.0"
1147
- }
1148
- },
1149
- "node_modules/is-binary-path": {
1150
- "version": "2.1.0",
1151
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1152
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1153
- "dev": true,
1154
- "license": "MIT",
1155
- "dependencies": {
1156
- "binary-extensions": "^2.0.0"
1157
- },
1158
- "engines": {
1159
- "node": ">=8"
1160
- }
1161
- },
1162
- "node_modules/is-core-module": {
1163
- "version": "2.16.1",
1164
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
1165
- "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
1166
- "dev": true,
1167
- "license": "MIT",
1168
- "dependencies": {
1169
- "hasown": "^2.0.2"
1170
- },
1171
- "engines": {
1172
- "node": ">= 0.4"
1173
- },
1174
- "funding": {
1175
- "url": "https://github.com/sponsors/ljharb"
1176
- }
1177
- },
1178
- "node_modules/is-extglob": {
1179
- "version": "2.1.1",
1180
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1181
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1182
- "dev": true,
1183
- "license": "MIT",
1184
- "engines": {
1185
- "node": ">=0.10.0"
1186
- }
1187
- },
1188
- "node_modules/is-glob": {
1189
- "version": "4.0.3",
1190
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1191
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1192
- "dev": true,
1193
- "license": "MIT",
1194
- "dependencies": {
1195
- "is-extglob": "^2.1.1"
1196
- },
1197
- "engines": {
1198
- "node": ">=0.10.0"
1199
- }
1200
- },
1201
- "node_modules/is-number": {
1202
- "version": "7.0.0",
1203
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1204
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1205
- "dev": true,
1206
- "license": "MIT",
1207
- "engines": {
1208
- "node": ">=0.12.0"
1209
- }
1210
- },
1211
- "node_modules/jiti": {
1212
- "version": "1.21.7",
1213
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
1214
- "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
1215
- "dev": true,
1216
- "license": "MIT",
1217
- "bin": {
1218
- "jiti": "bin/jiti.js"
1219
- }
1220
- },
1221
- "node_modules/js-tokens": {
1222
- "version": "4.0.0",
1223
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1224
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1225
- "license": "MIT"
1226
- },
1227
- "node_modules/lilconfig": {
1228
- "version": "3.1.3",
1229
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
1230
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
1231
- "dev": true,
1232
- "license": "MIT",
1233
- "engines": {
1234
- "node": ">=14"
1235
- },
1236
- "funding": {
1237
- "url": "https://github.com/sponsors/antonk52"
1238
- }
1239
- },
1240
- "node_modules/lines-and-columns": {
1241
- "version": "1.2.4",
1242
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
1243
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
1244
- "dev": true,
1245
- "license": "MIT"
1246
- },
1247
- "node_modules/loose-envify": {
1248
- "version": "1.4.0",
1249
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1250
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1251
- "license": "MIT",
1252
- "dependencies": {
1253
- "js-tokens": "^3.0.0 || ^4.0.0"
1254
- },
1255
- "bin": {
1256
- "loose-envify": "cli.js"
1257
- }
1258
- },
1259
- "node_modules/math-intrinsics": {
1260
- "version": "1.1.0",
1261
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1262
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1263
- "license": "MIT",
1264
- "engines": {
1265
- "node": ">= 0.4"
1266
- }
1267
- },
1268
- "node_modules/merge2": {
1269
- "version": "1.4.1",
1270
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
1271
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
1272
- "dev": true,
1273
- "license": "MIT",
1274
- "engines": {
1275
- "node": ">= 8"
1276
- }
1277
- },
1278
- "node_modules/micromatch": {
1279
- "version": "4.0.8",
1280
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
1281
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
1282
- "dev": true,
1283
- "license": "MIT",
1284
- "dependencies": {
1285
- "braces": "^3.0.3",
1286
- "picomatch": "^2.3.1"
1287
- },
1288
- "engines": {
1289
- "node": ">=8.6"
1290
- }
1291
- },
1292
- "node_modules/mime": {
1293
- "version": "1.2.9",
1294
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.9.tgz",
1295
- "integrity": "sha512-WiLgbHTIq5AYUvU/Luli4mZ1bUcHpGNHyCsbl+KPMg4zt+XUDpQehWjuBjdLaEvDTinvKj/FgfQt3fPoT7j08g=="
1296
- },
1297
- "node_modules/mime-db": {
1298
- "version": "1.52.0",
1299
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1300
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1301
- "license": "MIT",
1302
- "engines": {
1303
- "node": ">= 0.6"
1304
- }
1305
- },
1306
- "node_modules/mime-types": {
1307
- "version": "2.1.35",
1308
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1309
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1310
- "license": "MIT",
1311
- "dependencies": {
1312
- "mime-db": "1.52.0"
1313
- },
1314
- "engines": {
1315
- "node": ">= 0.6"
1316
- }
1317
- },
1318
- "node_modules/mz": {
1319
- "version": "2.7.0",
1320
- "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
1321
- "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
1322
- "dev": true,
1323
- "license": "MIT",
1324
- "dependencies": {
1325
- "any-promise": "^1.0.0",
1326
- "object-assign": "^4.0.1",
1327
- "thenify-all": "^1.0.0"
1328
- }
1329
- },
1330
- "node_modules/nanoid": {
1331
- "version": "3.3.11",
1332
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1333
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1334
- "funding": [
1335
- {
1336
- "type": "github",
1337
- "url": "https://github.com/sponsors/ai"
1338
- }
1339
- ],
1340
- "license": "MIT",
1341
- "bin": {
1342
- "nanoid": "bin/nanoid.cjs"
1343
- },
1344
- "engines": {
1345
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1346
- }
1347
- },
1348
- "node_modules/next": {
1349
- "version": "14.2.35",
1350
- "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz",
1351
- "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==",
1352
- "license": "MIT",
1353
- "dependencies": {
1354
- "@next/env": "14.2.35",
1355
- "@swc/helpers": "0.5.5",
1356
- "busboy": "1.6.0",
1357
- "caniuse-lite": "^1.0.30001579",
1358
- "graceful-fs": "^4.2.11",
1359
- "postcss": "8.4.31",
1360
- "styled-jsx": "5.1.1"
1361
- },
1362
- "bin": {
1363
- "next": "dist/bin/next"
1364
- },
1365
- "engines": {
1366
- "node": ">=18.17.0"
1367
- },
1368
- "optionalDependencies": {
1369
- "@next/swc-darwin-arm64": "14.2.33",
1370
- "@next/swc-darwin-x64": "14.2.33",
1371
- "@next/swc-linux-arm64-gnu": "14.2.33",
1372
- "@next/swc-linux-arm64-musl": "14.2.33",
1373
- "@next/swc-linux-x64-gnu": "14.2.33",
1374
- "@next/swc-linux-x64-musl": "14.2.33",
1375
- "@next/swc-win32-arm64-msvc": "14.2.33",
1376
- "@next/swc-win32-ia32-msvc": "14.2.33",
1377
- "@next/swc-win32-x64-msvc": "14.2.33"
1378
- },
1379
- "peerDependencies": {
1380
- "@opentelemetry/api": "^1.1.0",
1381
- "@playwright/test": "^1.41.2",
1382
- "react": "^18.2.0",
1383
- "react-dom": "^18.2.0",
1384
- "sass": "^1.3.0"
1385
- },
1386
- "peerDependenciesMeta": {
1387
- "@opentelemetry/api": {
1388
- "optional": true
1389
- },
1390
- "@playwright/test": {
1391
- "optional": true
1392
- },
1393
- "sass": {
1394
- "optional": true
1395
- }
1396
- }
1397
- },
1398
- "node_modules/next/node_modules/postcss": {
1399
- "version": "8.4.31",
1400
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
1401
- "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
1402
- "funding": [
1403
- {
1404
- "type": "opencollective",
1405
- "url": "https://opencollective.com/postcss/"
1406
- },
1407
- {
1408
- "type": "tidelift",
1409
- "url": "https://tidelift.com/funding/github/npm/postcss"
1410
- },
1411
- {
1412
- "type": "github",
1413
- "url": "https://github.com/sponsors/ai"
1414
- }
1415
- ],
1416
- "license": "MIT",
1417
- "dependencies": {
1418
- "nanoid": "^3.3.6",
1419
- "picocolors": "^1.0.0",
1420
- "source-map-js": "^1.0.2"
1421
- },
1422
- "engines": {
1423
- "node": "^10 || ^12 || >=14"
1424
- }
1425
- },
1426
- "node_modules/node-releases": {
1427
- "version": "2.0.36",
1428
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
1429
- "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
1430
- "dev": true,
1431
- "license": "MIT"
1432
- },
1433
- "node_modules/normalize-path": {
1434
- "version": "3.0.0",
1435
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1436
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1437
- "dev": true,
1438
- "license": "MIT",
1439
- "engines": {
1440
- "node": ">=0.10.0"
1441
- }
1442
- },
1443
- "node_modules/object-assign": {
1444
- "version": "4.1.1",
1445
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1446
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1447
- "dev": true,
1448
- "license": "MIT",
1449
- "engines": {
1450
- "node": ">=0.10.0"
1451
- }
1452
- },
1453
- "node_modules/object-hash": {
1454
- "version": "3.0.0",
1455
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
1456
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
1457
- "dev": true,
1458
- "license": "MIT",
1459
- "engines": {
1460
- "node": ">= 6"
1461
- }
1462
- },
1463
- "node_modules/once": {
1464
- "version": "1.4.0",
1465
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1466
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1467
- "license": "ISC",
1468
- "dependencies": {
1469
- "wrappy": "1"
1470
- }
1471
- },
1472
- "node_modules/path-parse": {
1473
- "version": "1.0.7",
1474
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1475
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1476
- "dev": true,
1477
- "license": "MIT"
1478
- },
1479
- "node_modules/picocolors": {
1480
- "version": "1.1.1",
1481
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1482
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1483
- "license": "ISC"
1484
- },
1485
- "node_modules/picomatch": {
1486
- "version": "2.3.2",
1487
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
1488
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
1489
- "dev": true,
1490
- "license": "MIT",
1491
- "engines": {
1492
- "node": ">=8.6"
1493
- },
1494
- "funding": {
1495
- "url": "https://github.com/sponsors/jonschlinkert"
1496
- }
1497
- },
1498
- "node_modules/pify": {
1499
- "version": "2.3.0",
1500
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
1501
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
1502
- "dev": true,
1503
- "license": "MIT",
1504
- "engines": {
1505
- "node": ">=0.10.0"
1506
- }
1507
- },
1508
- "node_modules/pirates": {
1509
- "version": "4.0.7",
1510
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
1511
- "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
1512
- "dev": true,
1513
- "license": "MIT",
1514
- "engines": {
1515
- "node": ">= 6"
1516
- }
1517
- },
1518
- "node_modules/postcss": {
1519
- "version": "8.5.8",
1520
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
1521
- "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
1522
- "dev": true,
1523
- "funding": [
1524
- {
1525
- "type": "opencollective",
1526
- "url": "https://opencollective.com/postcss/"
1527
- },
1528
- {
1529
- "type": "tidelift",
1530
- "url": "https://tidelift.com/funding/github/npm/postcss"
1531
- },
1532
- {
1533
- "type": "github",
1534
- "url": "https://github.com/sponsors/ai"
1535
- }
1536
- ],
1537
- "license": "MIT",
1538
- "dependencies": {
1539
- "nanoid": "^3.3.11",
1540
- "picocolors": "^1.1.1",
1541
- "source-map-js": "^1.2.1"
1542
- },
1543
- "engines": {
1544
- "node": "^10 || ^12 || >=14"
1545
- }
1546
- },
1547
- "node_modules/postcss-import": {
1548
- "version": "15.1.0",
1549
- "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
1550
- "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
1551
- "dev": true,
1552
- "license": "MIT",
1553
- "dependencies": {
1554
- "postcss-value-parser": "^4.0.0",
1555
- "read-cache": "^1.0.0",
1556
- "resolve": "^1.1.7"
1557
- },
1558
- "engines": {
1559
- "node": ">=14.0.0"
1560
- },
1561
- "peerDependencies": {
1562
- "postcss": "^8.0.0"
1563
- }
1564
- },
1565
- "node_modules/postcss-js": {
1566
- "version": "4.1.0",
1567
- "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
1568
- "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
1569
- "dev": true,
1570
- "funding": [
1571
- {
1572
- "type": "opencollective",
1573
- "url": "https://opencollective.com/postcss/"
1574
- },
1575
- {
1576
- "type": "github",
1577
- "url": "https://github.com/sponsors/ai"
1578
- }
1579
- ],
1580
- "license": "MIT",
1581
- "dependencies": {
1582
- "camelcase-css": "^2.0.1"
1583
- },
1584
- "engines": {
1585
- "node": "^12 || ^14 || >= 16"
1586
- },
1587
- "peerDependencies": {
1588
- "postcss": "^8.4.21"
1589
- }
1590
- },
1591
- "node_modules/postcss-load-config": {
1592
- "version": "6.0.1",
1593
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
1594
- "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
1595
- "dev": true,
1596
- "funding": [
1597
- {
1598
- "type": "opencollective",
1599
- "url": "https://opencollective.com/postcss/"
1600
- },
1601
- {
1602
- "type": "github",
1603
- "url": "https://github.com/sponsors/ai"
1604
- }
1605
- ],
1606
- "license": "MIT",
1607
- "dependencies": {
1608
- "lilconfig": "^3.1.1"
1609
- },
1610
- "engines": {
1611
- "node": ">= 18"
1612
- },
1613
- "peerDependencies": {
1614
- "jiti": ">=1.21.0",
1615
- "postcss": ">=8.0.9",
1616
- "tsx": "^4.8.1",
1617
- "yaml": "^2.4.2"
1618
- },
1619
- "peerDependenciesMeta": {
1620
- "jiti": {
1621
- "optional": true
1622
- },
1623
- "postcss": {
1624
- "optional": true
1625
- },
1626
- "tsx": {
1627
- "optional": true
1628
- },
1629
- "yaml": {
1630
- "optional": true
1631
- }
1632
- }
1633
- },
1634
- "node_modules/postcss-nested": {
1635
- "version": "6.2.0",
1636
- "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
1637
- "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
1638
- "dev": true,
1639
- "funding": [
1640
- {
1641
- "type": "opencollective",
1642
- "url": "https://opencollective.com/postcss/"
1643
- },
1644
- {
1645
- "type": "github",
1646
- "url": "https://github.com/sponsors/ai"
1647
- }
1648
- ],
1649
- "license": "MIT",
1650
- "dependencies": {
1651
- "postcss-selector-parser": "^6.1.1"
1652
- },
1653
- "engines": {
1654
- "node": ">=12.0"
1655
- },
1656
- "peerDependencies": {
1657
- "postcss": "^8.2.14"
1658
- }
1659
- },
1660
- "node_modules/postcss-selector-parser": {
1661
- "version": "6.1.2",
1662
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
1663
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
1664
- "dev": true,
1665
- "license": "MIT",
1666
- "dependencies": {
1667
- "cssesc": "^3.0.0",
1668
- "util-deprecate": "^1.0.2"
1669
- },
1670
- "engines": {
1671
- "node": ">=4"
1672
- }
1673
- },
1674
- "node_modules/postcss-value-parser": {
1675
- "version": "4.2.0",
1676
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
1677
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
1678
- "dev": true,
1679
- "license": "MIT"
1680
- },
1681
- "node_modules/proxy-from-env": {
1682
- "version": "2.1.0",
1683
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
1684
- "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
1685
- "license": "MIT",
1686
- "engines": {
1687
- "node": ">=10"
1688
- }
1689
- },
1690
- "node_modules/queue-microtask": {
1691
- "version": "1.2.3",
1692
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
1693
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
1694
- "dev": true,
1695
- "funding": [
1696
- {
1697
- "type": "github",
1698
- "url": "https://github.com/sponsors/feross"
1699
- },
1700
- {
1701
- "type": "patreon",
1702
- "url": "https://www.patreon.com/feross"
1703
- },
1704
- {
1705
- "type": "consulting",
1706
- "url": "https://feross.org/support"
1707
- }
1708
- ],
1709
- "license": "MIT"
1710
- },
1711
- "node_modules/react": {
1712
- "version": "18.3.1",
1713
- "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
1714
- "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
1715
- "license": "MIT",
1716
- "dependencies": {
1717
- "loose-envify": "^1.1.0"
1718
- },
1719
- "engines": {
1720
- "node": ">=0.10.0"
1721
- }
1722
- },
1723
- "node_modules/react-chartjs-2": {
1724
- "version": "5.3.1",
1725
- "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.1.tgz",
1726
- "integrity": "sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==",
1727
- "license": "MIT",
1728
- "peerDependencies": {
1729
- "chart.js": "^4.1.1",
1730
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1731
- }
1732
- },
1733
- "node_modules/react-dom": {
1734
- "version": "18.3.1",
1735
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
1736
- "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
1737
- "license": "MIT",
1738
- "dependencies": {
1739
- "loose-envify": "^1.1.0",
1740
- "scheduler": "^0.23.2"
1741
- },
1742
- "peerDependencies": {
1743
- "react": "^18.3.1"
1744
- }
1745
- },
1746
- "node_modules/read-cache": {
1747
- "version": "1.0.0",
1748
- "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
1749
- "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
1750
- "dev": true,
1751
- "license": "MIT",
1752
- "dependencies": {
1753
- "pify": "^2.3.0"
1754
- }
1755
- },
1756
- "node_modules/readdirp": {
1757
- "version": "3.6.0",
1758
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1759
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1760
- "dev": true,
1761
- "license": "MIT",
1762
- "dependencies": {
1763
- "picomatch": "^2.2.1"
1764
- },
1765
- "engines": {
1766
- "node": ">=8.10.0"
1767
- }
1768
- },
1769
- "node_modules/resolve": {
1770
- "version": "1.22.11",
1771
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
1772
- "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
1773
- "dev": true,
1774
- "license": "MIT",
1775
- "dependencies": {
1776
- "is-core-module": "^2.16.1",
1777
- "path-parse": "^1.0.7",
1778
- "supports-preserve-symlinks-flag": "^1.0.0"
1779
- },
1780
- "bin": {
1781
- "resolve": "bin/resolve"
1782
- },
1783
- "engines": {
1784
- "node": ">= 0.4"
1785
- },
1786
- "funding": {
1787
- "url": "https://github.com/sponsors/ljharb"
1788
- }
1789
- },
1790
- "node_modules/reusify": {
1791
- "version": "1.1.0",
1792
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
1793
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
1794
- "dev": true,
1795
- "license": "MIT",
1796
- "engines": {
1797
- "iojs": ">=1.0.0",
1798
- "node": ">=0.10.0"
1799
- }
1800
- },
1801
- "node_modules/run-parallel": {
1802
- "version": "1.2.0",
1803
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
1804
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
1805
- "dev": true,
1806
- "funding": [
1807
- {
1808
- "type": "github",
1809
- "url": "https://github.com/sponsors/feross"
1810
- },
1811
- {
1812
- "type": "patreon",
1813
- "url": "https://www.patreon.com/feross"
1814
- },
1815
- {
1816
- "type": "consulting",
1817
- "url": "https://feross.org/support"
1818
- }
1819
- ],
1820
- "license": "MIT",
1821
- "dependencies": {
1822
- "queue-microtask": "^1.2.2"
1823
- }
1824
- },
1825
- "node_modules/scheduler": {
1826
- "version": "0.23.2",
1827
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
1828
- "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
1829
- "license": "MIT",
1830
- "dependencies": {
1831
- "loose-envify": "^1.1.0"
1832
- }
1833
- },
1834
- "node_modules/source-map-js": {
1835
- "version": "1.2.1",
1836
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1837
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1838
- "license": "BSD-3-Clause",
1839
- "engines": {
1840
- "node": ">=0.10.0"
1841
- }
1842
- },
1843
- "node_modules/streamsearch": {
1844
- "version": "1.1.0",
1845
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
1846
- "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
1847
- "engines": {
1848
- "node": ">=10.0.0"
1849
- }
1850
- },
1851
- "node_modules/styled-jsx": {
1852
- "version": "5.1.1",
1853
- "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
1854
- "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
1855
- "license": "MIT",
1856
- "dependencies": {
1857
- "client-only": "0.0.1"
1858
- },
1859
- "engines": {
1860
- "node": ">= 12.0.0"
1861
- },
1862
- "peerDependencies": {
1863
- "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
1864
- },
1865
- "peerDependenciesMeta": {
1866
- "@babel/core": {
1867
- "optional": true
1868
- },
1869
- "babel-plugin-macros": {
1870
- "optional": true
1871
- }
1872
- }
1873
- },
1874
- "node_modules/sucrase": {
1875
- "version": "3.35.1",
1876
- "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
1877
- "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
1878
- "dev": true,
1879
- "license": "MIT",
1880
- "dependencies": {
1881
- "@jridgewell/gen-mapping": "^0.3.2",
1882
- "commander": "^4.0.0",
1883
- "lines-and-columns": "^1.1.6",
1884
- "mz": "^2.7.0",
1885
- "pirates": "^4.0.1",
1886
- "tinyglobby": "^0.2.11",
1887
- "ts-interface-checker": "^0.1.9"
1888
- },
1889
- "bin": {
1890
- "sucrase": "bin/sucrase",
1891
- "sucrase-node": "bin/sucrase-node"
1892
- },
1893
- "engines": {
1894
- "node": ">=16 || 14 >=14.17"
1895
- }
1896
- },
1897
- "node_modules/supports-preserve-symlinks-flag": {
1898
- "version": "1.0.0",
1899
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1900
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1901
- "dev": true,
1902
- "license": "MIT",
1903
- "engines": {
1904
- "node": ">= 0.4"
1905
- },
1906
- "funding": {
1907
- "url": "https://github.com/sponsors/ljharb"
1908
- }
1909
- },
1910
- "node_modules/tailwindcss": {
1911
- "version": "3.4.19",
1912
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
1913
- "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
1914
- "dev": true,
1915
- "license": "MIT",
1916
- "dependencies": {
1917
- "@alloc/quick-lru": "^5.2.0",
1918
- "arg": "^5.0.2",
1919
- "chokidar": "^3.6.0",
1920
- "didyoumean": "^1.2.2",
1921
- "dlv": "^1.1.3",
1922
- "fast-glob": "^3.3.2",
1923
- "glob-parent": "^6.0.2",
1924
- "is-glob": "^4.0.3",
1925
- "jiti": "^1.21.7",
1926
- "lilconfig": "^3.1.3",
1927
- "micromatch": "^4.0.8",
1928
- "normalize-path": "^3.0.0",
1929
- "object-hash": "^3.0.0",
1930
- "picocolors": "^1.1.1",
1931
- "postcss": "^8.4.47",
1932
- "postcss-import": "^15.1.0",
1933
- "postcss-js": "^4.0.1",
1934
- "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
1935
- "postcss-nested": "^6.2.0",
1936
- "postcss-selector-parser": "^6.1.2",
1937
- "resolve": "^1.22.8",
1938
- "sucrase": "^3.35.0"
1939
- },
1940
- "bin": {
1941
- "tailwind": "lib/cli.js",
1942
- "tailwindcss": "lib/cli.js"
1943
- },
1944
- "engines": {
1945
- "node": ">=14.0.0"
1946
- }
1947
- },
1948
- "node_modules/thenify": {
1949
- "version": "3.3.1",
1950
- "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
1951
- "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
1952
- "dev": true,
1953
- "license": "MIT",
1954
- "dependencies": {
1955
- "any-promise": "^1.0.0"
1956
- }
1957
- },
1958
- "node_modules/thenify-all": {
1959
- "version": "1.6.0",
1960
- "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
1961
- "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
1962
- "dev": true,
1963
- "license": "MIT",
1964
- "dependencies": {
1965
- "thenify": ">= 3.1.0 < 4"
1966
- },
1967
- "engines": {
1968
- "node": ">=0.8"
1969
- }
1970
- },
1971
- "node_modules/tinyglobby": {
1972
- "version": "0.2.15",
1973
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
1974
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1975
- "dev": true,
1976
- "license": "MIT",
1977
- "dependencies": {
1978
- "fdir": "^6.5.0",
1979
- "picomatch": "^4.0.3"
1980
- },
1981
- "engines": {
1982
- "node": ">=12.0.0"
1983
- },
1984
- "funding": {
1985
- "url": "https://github.com/sponsors/SuperchupuDev"
1986
- }
1987
- },
1988
- "node_modules/tinyglobby/node_modules/fdir": {
1989
- "version": "6.5.0",
1990
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1991
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1992
- "dev": true,
1993
- "license": "MIT",
1994
- "engines": {
1995
- "node": ">=12.0.0"
1996
- },
1997
- "peerDependencies": {
1998
- "picomatch": "^3 || ^4"
1999
- },
2000
- "peerDependenciesMeta": {
2001
- "picomatch": {
2002
- "optional": true
2003
- }
2004
- }
2005
- },
2006
- "node_modules/tinyglobby/node_modules/picomatch": {
2007
- "version": "4.0.4",
2008
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
2009
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
2010
- "dev": true,
2011
- "license": "MIT",
2012
- "engines": {
2013
- "node": ">=12"
2014
- },
2015
- "funding": {
2016
- "url": "https://github.com/sponsors/jonschlinkert"
2017
- }
2018
- },
2019
- "node_modules/to-regex-range": {
2020
- "version": "5.0.1",
2021
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
2022
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
2023
- "dev": true,
2024
- "license": "MIT",
2025
- "dependencies": {
2026
- "is-number": "^7.0.0"
2027
- },
2028
- "engines": {
2029
- "node": ">=8.0"
2030
- }
2031
- },
2032
- "node_modules/ts-interface-checker": {
2033
- "version": "0.1.13",
2034
- "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
2035
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
2036
- "dev": true,
2037
- "license": "Apache-2.0"
2038
- },
2039
- "node_modules/tslib": {
2040
- "version": "2.8.1",
2041
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
2042
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
2043
- "license": "0BSD"
2044
- },
2045
- "node_modules/undici-types": {
2046
- "version": "7.18.2",
2047
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
2048
- "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
2049
- "license": "MIT"
2050
- },
2051
- "node_modules/update-browserslist-db": {
2052
- "version": "1.2.3",
2053
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
2054
- "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
2055
- "dev": true,
2056
- "funding": [
2057
- {
2058
- "type": "opencollective",
2059
- "url": "https://opencollective.com/browserslist"
2060
- },
2061
- {
2062
- "type": "tidelift",
2063
- "url": "https://tidelift.com/funding/github/npm/browserslist"
2064
- },
2065
- {
2066
- "type": "github",
2067
- "url": "https://github.com/sponsors/ai"
2068
- }
2069
- ],
2070
- "license": "MIT",
2071
- "dependencies": {
2072
- "escalade": "^3.2.0",
2073
- "picocolors": "^1.1.1"
2074
- },
2075
- "bin": {
2076
- "update-browserslist-db": "cli.js"
2077
- },
2078
- "peerDependencies": {
2079
- "browserslist": ">= 4.21.0"
2080
- }
2081
- },
2082
- "node_modules/util-deprecate": {
2083
- "version": "1.0.2",
2084
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
2085
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
2086
- "dev": true,
2087
- "license": "MIT"
2088
- },
2089
- "node_modules/uuid": {
2090
- "version": "9.0.1",
2091
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
2092
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
2093
- "funding": [
2094
- "https://github.com/sponsors/broofa",
2095
- "https://github.com/sponsors/ctavan"
2096
- ],
2097
- "license": "MIT",
2098
- "bin": {
2099
- "uuid": "dist/bin/uuid"
2100
- }
2101
- },
2102
- "node_modules/wrappy": {
2103
- "version": "1.0.2",
2104
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2105
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
2106
- "license": "ISC"
2107
- },
2108
- "node_modules/ws": {
2109
- "version": "8.20.0",
2110
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
2111
- "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
2112
- "license": "MIT",
2113
- "engines": {
2114
- "node": ">=10.0.0"
2115
- },
2116
- "peerDependencies": {
2117
- "bufferutil": "^4.0.1",
2118
- "utf-8-validate": ">=5.0.2"
2119
- },
2120
- "peerDependenciesMeta": {
2121
- "bufferutil": {
2122
- "optional": true
2123
- },
2124
- "utf-8-validate": {
2125
- "optional": true
2126
- }
2127
- }
2128
- }
2129
- }
2130
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/package.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "name": "civicai",
3
- "version": "0.1.0",
4
- "private": true,
5
- "scripts": {
6
- "dev": "next dev",
7
- "build": "next build",
8
- "start": "next start"
9
- },
10
- "dependencies": {
11
- "@sendgrid/mail": "^8.1.6",
12
- "@supabase/supabase-js": "^2.100.1",
13
- "chart.js": "^4.5.1",
14
- "form-data": "^4.0.0",
15
- "formidable": "^3.5.1",
16
- "git": "^0.1.5",
17
- "next": "^14.0.0",
18
- "react": "^18.0.0",
19
- "react-chartjs-2": "^5.3.1",
20
- "react-dom": "^18.0.0",
21
- "uuid": "^9.0.1"
22
- },
23
- "devDependencies": {
24
- "autoprefixer": "^10.4.16",
25
- "postcss": "^8.4.32",
26
- "tailwindcss": "^3.4.0"
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/_app.jsx DELETED
@@ -1,4 +0,0 @@
1
- import '../styles/globals.css'
2
- export default function App({ Component, pageProps }) {
3
- return <Component {...pageProps} />
4
- }
 
 
 
 
 
Downloads/hackathon/civicai/pages/api/alert.js DELETED
@@ -1,56 +0,0 @@
1
- import sgMail from '@sendgrid/mail'
2
- sgMail.setApiKey(process.env.SENDGRID_API_KEY)
3
-
4
- export default async function handler(req, res) {
5
- if (req.method !== 'POST') return res.status(405).end()
6
- const { complaint } = req.body
7
- if (!complaint) return res.status(400).json({ error: 'No complaint data' })
8
-
9
- const msg = {
10
- to: process.env.ALERT_EMAIL,
11
- from: 'citizencare-alerts@yourdomain.com',
12
- subject: `[CRITICAL] Complaint Alert — ${complaint.department} Department`,
13
- html: `
14
- <div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;">
15
- <div style="background:#DC2626;color:white;padding:20px;border-radius:8px 8px 0 0;">
16
- <h1 style="margin:0;font-size:20px;">🔴 Critical Complaint Alert</h1>
17
- <p style="margin:4px 0 0;opacity:.9;font-size:14px;">CitizenCare Automated Alert</p>
18
- </div>
19
- <div style="background:#fff;border:1px solid #E5E7EB;border-top:none;padding:24px;border-radius:0 0 8px 8px;">
20
- <table style="width:100%;border-collapse:collapse;font-size:14px;">
21
- <tr style="border-bottom:1px solid #F3F4F6;">
22
- <td style="padding:10px 0;color:#6B7280;width:160px;">Complaint ID</td>
23
- <td style="padding:10px 0;font-family:monospace;">${complaint.id}</td>
24
- </tr>
25
- <tr style="border-bottom:1px solid #F3F4F6;">
26
- <td style="padding:10px 0;color:#6B7280;">Department</td>
27
- <td style="padding:10px 0;font-weight:bold;">${complaint.department}</td>
28
- </tr>
29
- <tr style="border-bottom:1px solid #F3F4F6;">
30
- <td style="padding:10px 0;color:#6B7280;">Location</td>
31
- <td style="padding:10px 0;">${complaint.location || 'Not specified'}</td>
32
- </tr>
33
- <tr>
34
- <td style="padding:10px 0;color:#6B7280;">ETA</td>
35
- <td style="padding:10px 0;">${complaint.estimated_resolution}</td>
36
- </tr>
37
- </table>
38
- <div style="background:#FEF3C7;border-left:4px solid #F59E0B;padding:16px;margin:20px 0;border-radius:0 8px 8px 0;">
39
- <p style="margin:0;color:#78350F;font-size:14px;">${complaint.summary}</p>
40
- </div>
41
- <ul style="margin:0;padding-left:20px;color:#374151;font-size:14px;line-height:2;">
42
- ${(complaint.action_items||[]).map(a=>`<li>${a}</li>`).join('')}
43
- </ul>
44
- <p style="margin-top:24px;font-size:12px;color:#9CA3AF;">
45
- Logged at ${new Date().toLocaleString('en-IN',{timeZone:'Asia/Kolkata'})} IST
46
- </p>
47
- </div>
48
- </div>`
49
- }
50
- try {
51
- await sgMail.send(msg)
52
- res.status(200).json({ sent: true })
53
- } catch (err) {
54
- res.status(500).json({ error: 'Alert failed', detail: err.message })
55
- }
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/api/process.js DELETED
@@ -1,530 +0,0 @@
1
- export default async function handler(req, res) {
2
- if (req.method !== 'POST') return res.status(405).end()
3
- const { text, language } = req.body
4
- if (!text) return res.status(400).json({ error: 'No complaint text provided' })
5
-
6
- try {
7
- // ─── STEP 1: DETECT LANGUAGE LOCALLY (NO API NEEDED) ─────────────────
8
- // We detect the script from Unicode ranges and use the user-selected language.
9
- // Multilingual keywords in Step 2 handle matching directly — no translation needed.
10
-
11
- const detectScript = (t) => {
12
- if (/[\u0900-\u097F]/.test(t)) return 'Hindi' // Devanagari (Hindi/Marathi)
13
- if (/[\u0C00-\u0C7F]/.test(t)) return 'Telugu'
14
- if (/[\u0B80-\u0BFF]/.test(t)) return 'Tamil'
15
- if (/[\u0C80-\u0CFF]/.test(t)) return 'Kannada'
16
- if (/[\u0980-\u09FF]/.test(t)) return 'Bengali'
17
- if (/[\u0A80-\u0AFF]/.test(t)) return 'Gujarati'
18
- if (/[\u0A00-\u0A7F]/.test(t)) return 'Punjabi'
19
- if (/[\u0B00-\u0B7F]/.test(t)) return 'Odia'
20
- return 'English'
21
- }
22
-
23
- let detectedLanguage = language || detectScript(text) || 'Unknown'
24
- // If user selected a language, trust that; otherwise use script detection
25
- if (!language || language === 'English') {
26
- const scriptLang = detectScript(text)
27
- if (scriptLang !== 'English') detectedLanguage = scriptLang
28
- }
29
-
30
- // Use original text directly for keyword matching — multilingual keywords cover all languages
31
- const combinedText = text.toLowerCase()
32
-
33
- // ─── STEP 2: MULTILINGUAL KEYWORD MAPS ──────────────────────────────
34
- // Each department has keywords in English + Hindi + Telugu + Tamil + Kannada + Marathi + Bengali
35
-
36
- const departmentKeywords = {
37
- Hospital: [
38
- // English
39
- 'hospital', 'doctor', 'nurse', 'patient', 'ward', 'emergency room', 'icu', 'medical staff',
40
- 'ambulance', 'surgery', 'operation theatre', 'blood bank', 'pharmacy', 'casualty',
41
- // Hindi
42
- 'अस्पताल', 'डॉक्टर', 'नर्स', 'मरीज', 'एम्बुलेंस', 'ऑपरेशन', 'आईसीयू', 'फार्मेसी', 'खून',
43
- // Telugu
44
- 'ఆసుపత్రి', 'డాక్టర్', 'నర్సు', 'రోగి', 'అంబులెన్స్', 'శస్త్రచికిత్స',
45
- // Tamil
46
- 'மருத்துவமனை', 'மருத்துவர்', 'செவிலியர்', 'நோயாளி', 'அம்புலன்ஸ்',
47
- // Kannada
48
- 'ಆಸ್ಪತ್ರೆ', 'ವೈದ್ಯ', 'ನರ್ಸ್', 'ರೋಗಿ', 'ಆಂಬುಲೆನ್ಸ್',
49
- // Marathi
50
- 'रुग्णालय', 'डॉक्टर', 'परिचारिका', 'रुग्ण', 'रुग्णवाहिका',
51
- // Bengali
52
- 'হাসপাতাল', 'ডাক্তার', 'নার্স', 'রোগী', 'অ্যাম্বুলেন্স'
53
- ],
54
- Health: [
55
- // English
56
- 'health', 'disease', 'medical', 'clinic', 'vaccination', 'public health', 'epidemic',
57
- 'infection', 'fever', 'dengue', 'malaria', 'cholera', 'food poisoning', 'contamination',
58
- 'hygiene', 'mental health', 'medicine', 'drug', 'pandemic', 'heart', 'cardiac', 'attack', 'chest pain', 'stroke',
59
- // Hindi
60
- 'स्वास्थ्य', 'बीमारी', 'बुखार', 'डेंगू', 'मलेरिया', 'टीकाकरण', 'टीका', 'संक्रमण', 'चिकित्सा',
61
- 'दवाई', 'दवा', 'महामारी', 'खांसी', 'जुकाम', 'उल्टी', 'दस्त', 'विषाक्तता',
62
- // Telugu
63
- 'ఆరోగ్యం', 'వ్యాధి', 'జ్వరం', 'డెంగ్యూ', 'మలేరియా', 'టీకా', 'అంటువ్యాధి', 'మందులు',
64
- // Tamil
65
- 'சுகாதாரம்', 'நோய்', 'காய்ச்சல்', 'டெங்கு', 'மலேரியா', 'தடுப்பூசி', 'தொற்று', 'மருந்து',
66
- // Kannada
67
- 'ಆರೋಗ್ಯ', 'ರೋಗ', 'ಜ್ವರ', 'ಡೆಂಗ್ಯೂ', 'ಮಲೇರಿಯಾ', 'ಲಸಿಕೆ', 'ಸೋಂಕು',
68
- // Marathi
69
- 'आरोग्य', 'आजार', 'ताप', 'डेंग्यू', 'मलेरिया', 'लसीकरण', 'संसर्ग',
70
- // Bengali
71
- 'স্বাস্থ্য', 'রোগ', 'জ্বর', 'ডেঙ্গু', 'ম্যালেরিয়া', 'টিকা', 'সংক্রমণ'
72
- ],
73
- Police: [
74
- // English
75
- 'police', 'theft', 'robbery', 'assault', 'crime', 'harassment', 'violence', 'murder',
76
- 'kidnapping', 'burglary', 'stalking', 'domestic violence', 'eve teasing', 'chain snatching',
77
- 'fraud', 'scam', 'cybercrime', 'extortion', 'threat', 'drunk driving', 'accident',
78
- 'FIR', 'stolen', 'missing person', 'drug trafficking', 'gambling', 'fighting',
79
- // Hindi
80
- 'पुलिस', 'चोरी', 'लूट', 'डकैती', 'हमला', 'अपराध', 'उत्पीड़न', 'हत्या', 'हिंसा',
81
- 'अपहरण', 'धोखाधड़ी', 'छेड़छाड़', 'मारपीट', 'धमकी', 'चेन स्नैचिंग', 'नशा', 'शराब',
82
- 'गायब', 'लापता', 'गुंडागर्दी', 'एफआईआर',
83
- // Telugu
84
- 'పోలీసు', 'దొంగతనం', 'దోపిడీ', 'దాడి', 'నేరం', 'వేధింపు', 'హత్య', 'హింస',
85
- 'అపహరణ', 'మోసం', 'బెదిరింపు', 'తాగి డ్రైవింగ్',
86
- // Tamil
87
- 'காவல்', 'திருட்டு', 'கொள்ளை', 'தாக்குதல்', 'குற்றம்', 'தொல்லை', 'கொலை',
88
- 'கடத்தல்', 'மோசடி', 'மிரட்டல்', 'போலீஸ்',
89
- // Kannada
90
- 'ಪೊಲೀಸ್', 'ಕಳ್ಳತನ', 'ದರೋಡೆ', 'ಹಲ್ಲೆ', 'ಅಪರಾಧ', 'ಕಿರುಕುಳ', 'ಕೊಲೆ',
91
- 'ಅಪಹರಣ', 'ವಂಚನೆ', 'ಬೆದರಿಕೆ',
92
- // Marathi
93
- 'पोलीस', 'चोरी', 'दरोडा', 'हल्ला', 'गुन्हा', 'छळ', 'खून', 'अपहरण', 'फसवणूक',
94
- // Bengali
95
- 'পুলিশ', 'চুরি', 'ডাকাতি', 'হামলা', 'অপরাধ', 'হয়রানি', 'খুন', 'অপহরণ', 'প্রতারণা'
96
- ],
97
- Sanitation: [
98
- // English
99
- 'garbage', 'trash', 'dustbin', 'sewage', 'drainage', 'smell', 'litter', 'sanitation',
100
- 'manhole', 'gutter', 'waste', 'dump', 'rubbish', 'compost', 'recycle', 'toilet',
101
- 'public toilet', 'open defecation', 'cleaning', 'sweeping', 'stinking', 'dirty',
102
- 'clogged drain', 'overflowing', 'mosquito breeding', 'filth', 'unhygienic',
103
- // Hindi
104
- 'कचरा', 'कूड़ा', 'सफाई', 'नाली', 'गंदगी', 'बदबू', 'सीवर', 'मैनहोल', 'गटर',
105
- 'शौचालय', 'खुले में शौच', 'सड़ांध', 'गंदा', 'नालियाँ', 'डस्टबिन', 'मच्छर',
106
- // Telugu
107
- 'చెత్త', 'మురుగు', 'పారిశుధ్యం', 'డ్రైనేజ్', 'వాసన', 'మురికి', 'మ్యాన్‌హోల్',
108
- 'శుభ్రత', 'మరుగుదొడ్డి', 'దుర్గంధం',
109
- // Tamil
110
- 'குப்பை', 'கழிவு', 'சுகாதாரம்', 'வடிகால்', 'துர்நாற்றம்', 'கழிவுநீர்', 'கொசு',
111
- 'சுத்தம்', 'கழிப்பறை', 'அசுத்தம்',
112
- // Kannada
113
- 'ಕಸ', 'ತ್ಯಾಜ್ಯ', 'ನೈರ್ಮಲ್ಯ', 'ಒಳಚರಂಡಿ', 'ದುರ್ವಾಸನೆ', 'ಗಟಾರ', 'ಶೌಚಾಲಯ',
114
- // Marathi
115
- 'कचरा', 'सफाई', 'गटार', 'दुर्गंधी', 'मॅनहोल', 'शौचालय', 'स्वच्छता',
116
- // Bengali
117
- 'আবর্জনা', 'জঞ্জাল', 'পরিষ্কার', 'নর্দমা', 'দুর্গন্ধ', 'ম্যানহোল', 'শৌচাগার'
118
- ],
119
- Education: [
120
- // English
121
- 'school', 'college', 'teacher', 'student', 'class', 'education', 'university',
122
- 'exam', 'syllabus', 'principal', 'hostel', 'library', 'scholarship', 'tuition',
123
- 'midday meal', 'playground', 'campus', 'classroom', 'lab', 'professor',
124
- 'admission', 'dropout', 'infrastructure', 'sports facility',
125
- // Hindi
126
- 'स्कूल', 'विद्यालय', 'कॉलेज', 'शिक्षक', 'अध्यापक', 'छात्र', 'विद्यार्थी', 'शिक्षा',
127
- 'परीक्षा', 'पाठ्यक्रम', 'प्रधानाचार्य', 'पुस्तकालय', 'छात्रवृत्ति', 'मिड डे मील',
128
- 'कक्षा', 'प्रयोगशाला', 'खेल का मैदान', 'प्रवेश',
129
- // Telugu
130
- 'పాఠశాల', 'కళాశాల', 'ఉపాధ్యాయుడు', 'విద్యార్థి', 'విద్య', 'పరీక్ష', 'ప్రధానోపాధ్యాయుడు',
131
- 'గ్రంథాలయం', 'ఉపకారవేతనం', 'మధ్యాహ్న భోజన��',
132
- // Tamil
133
- 'பள்ளி', 'கல்லூரி', 'ஆசிரியர்', 'மாணவர்', 'கல்வி', 'தேர்வு', 'தலைமையாசிரியர்',
134
- 'நூலகம்', 'உதவித்தொகை', 'மதிய உணவு',
135
- // Kannada
136
- 'ಶಾಲೆ', 'ಕಾಲೇಜು', 'ಶಿಕ್ಷಕ', 'ವಿದ್ಯಾರ್ಥಿ', 'ಶಿಕ್ಷಣ', 'ಪರೀಕ್ಷೆ', 'ಗ್ರಂಥಾಲಯ',
137
- // Marathi
138
- 'शाळा', 'महाविद्यालय', 'शिक्षक', 'विद्यार्थी', 'शिक्षण', 'परीक्षा', 'ग्रंथालय',
139
- // Bengali
140
- 'স্কুল', 'কলেজ', 'শিক্ষক', 'ছাত্র', 'শিক্ষা', 'পরীক্ষা', 'গ্রন্থাগার'
141
- ],
142
- Roads: [
143
- // English
144
- 'pothole', 'road', 'street', 'traffic', 'asphalt', 'bridge', 'roads', 'speedbreaker',
145
- 'speed breaker', 'zebra crossing', 'signal', 'traffic light', 'footpath', 'divider',
146
- 'highway', 'flyover', 'pavement', 'crack in road', 'uneven road', 'construction',
147
- // Hindi
148
- 'गड्ढा', 'सड़क', 'रास्ता', 'ट्रैफिक', 'पुल', 'फ्लाईओवर', 'स्पीड ब्रेकर',
149
- 'ज़ेबरा क्रॉसिंग', 'सिग्नल', 'फुटपाथ', 'राजमार्ग', 'निर्माण',
150
- // Telugu
151
- 'గుంత', 'రోడ్డు', 'రహదారి', 'ట్రాఫిక్', 'వంతెన', 'ఫ్లైఓవర్', 'సిగ్నల్', 'ఫుట్‌పాత్',
152
- // Tamil
153
- 'குழி', 'சாலை', 'போக்குவரத்து', 'பாலம்', 'மேம்பாலம்', 'சிக்னல்', 'நடைபாதை',
154
- // Kannada
155
- 'ಗುಂಡಿ', 'ರಸ್ತೆ', 'ಸಂಚಾರ', 'ಸೇತುವೆ', 'ಮೇಲ್ಸೇತುವೆ', 'ಸಿಗ್ನಲ್',
156
- // Marathi
157
- 'खड्डा', 'रस्ता', 'वाहतूक', 'पूल', 'उड्डाणपूल', 'सिग्नल', 'पदपथ',
158
- // Bengali
159
- 'গর্ত', 'রাস্তা', 'যানজট', 'সেতু', 'ফ্লাইওভার', 'সিগন্যাল', 'ফুটপাথ'
160
- ],
161
- Water: [
162
- // English
163
- 'water', 'leak', 'tap', 'pipeline', 'drinking water', 'flooding', 'water scarcity',
164
- 'borewell', 'water supply', 'contaminated water', 'water tank', 'water pressure',
165
- 'water logging', 'rain water', 'water connection', 'water bill', 'dirty water',
166
- // Hindi
167
- 'पानी', 'नल', 'पाइपलाइन', 'जलापूर्ति', 'बोरवेल', 'बाढ़', 'जल', 'टंकी', 'रिसाव',
168
- 'पेयजल', 'गंदा पानी', 'पानी की कमी', 'जलभराव', 'पानी का बिल',
169
- // Telugu
170
- 'నీరు', 'కొళాయి', 'పైపులైన్', 'తాగునీరు', 'బోరుబావి', 'వరదలు', 'నీటి ట్యాంక్',
171
- // Tamil
172
- 'தண்ணீர்', 'குழாய்', 'குடிநீர்', 'ஆழ்துளை', 'வெள்ளம்', 'நீர் தொட்டி', 'நீர் வழங்கல்',
173
- // Kannada
174
- 'ನೀರು', 'ನಲ್ಲಿ', 'ಕುಡಿಯುವ ನೀರು', 'ಕೊಳವೆಬಾವಿ', 'ಪ್ರವಾಹ', 'ನೀರಿನ ಟ್ಯಾಂಕ್',
175
- // Marathi
176
- 'पाणी', 'नळ', 'पाइपलाइन', 'पिण्याचे पाणी', 'बोअरवेल', 'पूर', 'पाण्याची टाकी',
177
- // Bengali
178
- 'জল', 'পানি', 'কল', 'পাইপলাইন', 'পানীয় জল', 'বন্যা', 'জলের ট্যাঙ্ক'
179
- ],
180
- Electricity: [
181
- // English
182
- 'power', 'electricity', 'outage', 'streetlight', 'transformer', 'voltage', 'breaker',
183
- 'power cut', 'short circuit', 'electric pole', 'wire', 'meter', 'electric shock',
184
- 'billing', 'power supply', 'generator', 'inverter', 'load shedding', 'blackout',
185
- 'broken streetlight', 'dark street', 'no light', 'lamps',
186
- // Hindi
187
- 'बिजली', 'बत्ती', 'ट्रांसफार्मर', 'तार', 'स्ट्रीटलाइट', 'बिजली कटौती',
188
- 'शॉर्ट सर्किट', 'बिजली का खंभा', 'मीटर', 'बिजली का बिल', 'इन्वर्टर', 'अंधेरा',
189
- // Telugu
190
- 'విద్యుత్', 'కరెంట్', 'ట్రాన్స్‌ఫార్మర్', 'వైర్', 'స్ట్రీట్ లైట్', 'పవర్ కట్',
191
- 'షార్ట్ సర్క్యూట్', 'మీటర్', 'చీకటి',
192
- // Tamil
193
- 'மின்சாரம்', 'மின்கம்பி', 'மின்மாற்றி', 'தெரு விளக்கு', 'மின்தடை', 'மின்கட்டணம்',
194
- 'ஷார்ட் சர்க்யூட்', 'மீட்டர்', 'இருட்டு',
195
- // Kannada
196
- 'ವಿದ್ಯುತ್', 'ಕರೆಂಟ್', 'ಟ್ರಾನ್ಸ್‌ಫಾರ್ಮರ್', 'ಬೀದಿ ದೀಪ', 'ವಿದ್ಯುತ್ ಕಡಿತ', 'ಮೀಟರ್',
197
- // Marathi
198
- 'वीज', 'ट्रान्सफॉर्मर', 'पथदीप', 'वीज खंडित', 'शॉर्ट सर्किट', 'मीटर', 'अंधार',
199
- // Bengali
200
- 'বিদ্যুৎ', 'ট্রান্সফর্মার', 'রাস্তার বাতি', 'লোডশেডিং', 'শর্ট সার্কিট', 'মিটার', 'অন্ধকার'
201
- ],
202
- Rescue: [
203
- // English
204
- 'rescue', 'fire brigade', 'firefighter', 'airlift', 'lifeguard', 'disaster response',
205
- 'natural disaster', 'search and rescue', 'fire', 'earthquake', 'cyclone', 'landslide',
206
- 'building collapse', 'flood rescue', 'trapped', 'drowning', 'explosion', 'gas leak',
207
- // Hindi
208
- 'बचाव', 'आग', 'दमकल', 'भूकंप', 'तूफान', 'बाढ़ बचाव', 'फंसे', 'डूबना',
209
- 'विस्फोट', 'गैस लीक', 'इमारत गिरी', 'भूस्खलन', 'चक्रवात',
210
- // Telugu
211
- 'రక్షణ', 'అగ్ని', 'భూకంపం', 'తుఫాను', 'చిక్కుకుపోయారు', 'మునిగిపోవడం',
212
- 'పేలుడు', 'గ్యాస్ లీక్', 'భవనం కూలిపోయింది',
213
- // Tamil
214
- 'மீட்பு', 'தீ', 'நிலநடுக்கம்', 'புயல்', 'சிக்கியவர்', 'மூழ்கிய', 'வெடிப்பு',
215
- 'எரிவாயு கசிவு', 'கட்டிடம் இடிந்தது',
216
- // Kannada
217
- 'ರಕ್ಷಣೆ', 'ಬೆಂಕಿ', 'ಭೂಕಂಪ', 'ಚಂಡಮಾರುತ', 'ಸಿಲುಕಿದ', 'ಮುಳುಗಿದ', 'ಸ್ಫೋಟ',
218
- // Marathi
219
- 'बचाव', 'आग', 'भूकंप', 'वादळ', 'अडकले', 'बुडणे', 'स्फोट', 'गॅस गळती',
220
- // Bengali
221
- 'উদ্ধার', 'আগুন', 'ভূমিকম্প', 'ঝড়', 'আটকে পড়া', 'ডুবে যাওয়া', 'বিস্ফোরণ', 'গ্যাস লিক'
222
- ],
223
- Transport: [
224
- // English
225
- 'bus', 'train', 'metro', 'auto', 'rickshaw', 'cab', 'taxi', 'public transport',
226
- 'bus stop', 'railway station', 'train delay', 'bus route', 'ticket', 'conductor',
227
- 'overcrowded', 'rash driving', 'traffic jam',
228
- // Hindi
229
- 'बस', 'ट्रेन', 'मेट्रो', 'ऑटो', 'रिक्शा', 'टैक्सी', 'बस स्टॉप', 'रेलवे स्टेशन',
230
- 'ट्रेन लेट', 'भीड़भाड़', 'लापरवाह ड्राइविंग',
231
- // Telugu
232
- 'బస్సు', 'రైలు', 'మెట్రో', 'ఆటో', 'రిక్షా', 'బస్ స్టాప్', 'రైల్వే స్టేషన్',
233
- // Tamil
234
- 'பேருந்து', 'ரயில்', 'மெட்ரோ', 'ஆட்டோ', 'பேருந்து நிறுத்தம்', 'ரயில் நிலையம்',
235
- // Kannada
236
- 'ಬಸ್', 'ರೈಲು', 'ಮೆಟ್ರೋ', 'ಆಟೋ', 'ಬಸ್ ನಿಲ್ದಾಣ', 'ರೈಲ್ವೇ ನಿಲ್ದಾಣ',
237
- // Marathi
238
- 'बस', 'ट्रेन', 'मेट्रो', 'ऑटो', 'बस थांबा', 'रेल्वे स्टेशन',
239
- // Bengali
240
- 'বাস', 'ট্রেন', 'মেট্রো', 'অটো', 'বাস স্টপ', 'রেলস্টেশন'
241
- ],
242
- Municipal: [
243
- // English
244
- 'property', 'building', 'construction', 'encroachment', 'illegal construction',
245
- 'parking', 'noise', 'stray dogs', 'stray animals', 'tax', 'birth certificate',
246
- 'death certificate', 'trade license', 'zoning', 'municipal', 'corporation',
247
- 'hawkers', 'street vendor', 'advertisement', 'hoarding',
248
- // Hindi
249
- 'भवन', 'निर्माण', 'अतिक्रमण', 'अवैध निर्माण', 'पार्किंग', 'शोर', 'कुत्ते',
250
- 'आवारा जानवर', 'कर', 'जन्म प��रमाणपत्र', 'नगर निगम', 'नगरपालिका',
251
- // Telugu
252
- 'భవనం', 'నిర్మాణం', 'ఆక్రమణ', 'పార్కింగ్', 'శబ్దం', 'వీధి కుక్కలు', 'పన్ను',
253
- // Tamil
254
- 'கட்டிடம்', 'கட்டுமானம்', 'ஆக்கிரமிப்பு', 'பார்க்கிங்', 'சத்தம்', 'தெரு நாய்', 'வரி',
255
- // Kannada
256
- 'ಕಟ್ಟಡ', 'ನಿರ್ಮಾಣ', 'ಅತಿಕ್ರಮಣ', 'ಪಾರ್ಕಿಂಗ್', 'ಶಬ್ದ', 'ಬೀದಿ ನಾಯಿ', 'ತೆರಿಗೆ',
257
- // Marathi
258
- 'इमारत', 'बांधकाम', 'अतिक्रमण', 'पार्किंग', 'आवाज', 'भटके कुत्रे', 'कर',
259
- // Bengali
260
- 'ভবন', 'নির্মাণ', 'দখল', 'পার্কিং', 'শব্দ', 'পথকুকুর', 'কর'
261
- ],
262
- Environment: [
263
- // English
264
- 'pollution', 'air quality', 'tree', 'deforestation', 'factory emission', 'smoke',
265
- 'dust', 'noise pollution', 'river pollution', 'lake', 'park', 'garden', 'green cover',
266
- 'plastic ban', 'waste burning', 'industrial waste', 'chemical',
267
- // Hindi
268
- 'प्रदूषण', 'वायु प्रदूषण', 'पेड़', 'वनों की कटाई', 'धुआं', 'धूल', 'ध्वनि प्रदूषण',
269
- 'नदी प्रदूषण', 'झील', 'पार्क', 'बगीचा', 'प्लास्टिक', 'कचरा जलाना',
270
- // Telugu
271
- 'కాలుష్యం', 'చెట్టు', 'పొగ', 'దుమ్ము', 'నది కాలుష్యం', 'పార్కు', 'ప్లాస్టిక్',
272
- // Tamil
273
- 'மாசு', 'மரம்', 'புகை', 'தூசி', 'ஆற்று மாசு', 'பூங்கா', 'பிளாஸ்டிக்',
274
- // Kannada
275
- 'ಮಾಲಿನ್ಯ', 'ಮರ', 'ಹೊಗೆ', 'ಧೂಳು', 'ನದಿ ಮಾಲಿನ್ಯ', 'ಉದ್ಯಾನ', 'ಪ್ಲಾಸ್ಟಿಕ್',
276
- // Marathi
277
- 'प्रदूषण', 'झाड', 'धूर', 'धूळ', 'नदी प्रदूषण', 'उद्यान', 'प्लास्टिक',
278
- // Bengali
279
- 'দূষণ', 'গাছ', 'ধোঁয়া', 'ধুলো', 'নদী দূষণ', 'পার্ক', 'প্লাস্টিক'
280
- ]
281
- }
282
-
283
- const urgencyKeywords = {
284
- Critical: [
285
- // English
286
- 'death', 'dying', 'dead', 'life threatening', 'life', 'injury', 'injured', 'fire',
287
- 'gas leak', 'explosion', 'drowning', 'crime in progress', 'shooting', 'murder',
288
- 'collapsed', 'heart attack', 'bleeding', 'unconscious', 'electrocuted', 'trapped',
289
- // Hindi
290
- 'मौत', 'मरना', 'जान', 'खतरा', 'चोट', 'आग', 'गैस लीक', 'विस्फोट', 'डूबना',
291
- 'गोली', 'हत्या', 'दिल का दौरा', 'खून बह रहा', 'बेहोश', 'फंसे',
292
- // Telugu
293
- 'మరణం', 'ప్రాణం', 'ప్రమాదం', 'గాయం', 'అగ్ని', 'పేలుడు', 'గుండెపోటు',
294
- // Tamil
295
- 'மரணம்', 'உயிர்', 'ஆபத்து', 'காயம்', 'தீ', 'வெடிப்பு', 'மாரடைப்பு',
296
- // Kannada
297
- 'ಸಾವು', 'ಜೀವ', 'ಅಪಾಯ', 'ಗಾಯ', 'ಬೆಂಕಿ', 'ಸ್ಫೋಟ',
298
- // Bengali
299
- 'মৃত্যু', 'প্রাণ', 'বিপদ', 'আঘাত', 'আগুন', 'বিস্ফোরণ'
300
- ],
301
- High: [
302
- // English
303
- 'urgent', 'immediate', 'asap', 'emergency', 'danger', 'collapse', 'flood', 'blocked',
304
- 'severe', 'accident', 'broken', 'major', 'serious', 'critical condition', 'stranded',
305
- // Hindi
306
- 'तत्काल', 'आपातकालीन', 'खतरनाक', 'गंभीर', 'टूटा', 'बाढ़', 'दुर्घटना', 'अवरुद्ध',
307
- // Telugu
308
- 'అత్యవసరం', 'ప్రమాదకరం', 'తీవ్రమైన', 'విరిగింది', 'వరదలు', 'ప్రమాదం',
309
- // Tamil
310
- 'அவசரம்', 'ஆபத்தான', 'தீவிர', 'உடைந்த', 'வெள்ளம்', 'விபத்து',
311
- // Kannada
312
- 'ತುರ್ತು', 'ಅಪಾಯಕಾರಿ', 'ತೀವ್ರ', 'ಮುರಿದ', 'ಪ್ರವಾಹ', 'ಅಪಘಾತ',
313
- // Bengali
314
- 'জরুরি', 'বিপজ্জনক', 'গুরুতর', 'ভাঙা', 'বন্যা', 'দুর্ঘটনা'
315
- ],
316
- Medium: [
317
- // English
318
- 'days', 'soon', 'need attention', 'problem persists', 'ongoing', 'continued',
319
- 'recurring', 'frequent', 'regular', 'multiple complaints', 'no response',
320
- 'not fixed', 'pending', 'weeks', 'several',
321
- // Hindi
322
- 'कई दिन', 'जल्दी', 'ध्यान', 'समस्या बनी हुई', 'बार-बार', 'जवाब नहीं',
323
- 'ठीक नहीं', 'लंबित', 'हफ्ते',
324
- // Telugu
325
- 'రోజులు', 'త్వరగా', 'సమస్య', 'పదేపదే', 'జవాబు లేదు',
326
- // Tamil
327
- 'நாட்கள்', 'விரைவில்', 'பிரச்சனை', 'அடிக்கடி', 'பதில் இல்லை',
328
- // Bengali
329
- 'দিন', 'শীঘ্র', 'সমস্যা', 'বারবার', 'উত্তর নেই'
330
- ],
331
- Low: [
332
- // English
333
- 'routine', 'minor', 'small', 'slow', 'inconvenience', 'suggestion', 'request',
334
- 'general', 'information', 'inquiry', 'feedback',
335
- // Hindi
336
- 'छोटा', 'मामूली', 'सुझाव', 'जानकारी', 'अनुरोध', 'सामान्य',
337
- // Telugu
338
- 'చిన్న', 'సూచన', 'సమాచారం', 'అభ్యర్థన',
339
- // Tamil
340
- 'சிறிய', 'ஆலோசனை', 'தகவல்', 'கோரிக்கை',
341
- // Bengali
342
- 'ছোট', 'সামান্য', 'পরামর্শ', 'তথ্য', 'অনুরোধ'
343
- ]
344
- }
345
-
346
- // ─── STEP 3: KEYWORD MATCHING ON COMBINED TEXT ──────────────────────
347
- const scoreCategory = (keywordsMap, fallback) => {
348
- const scores = {}
349
- for (const [cat, keys] of Object.entries(keywordsMap)) {
350
- for (const key of keys) {
351
- if (combinedText.includes(key.toLowerCase())) {
352
- scores[cat] = (scores[cat] || 0) + 1
353
- }
354
- }
355
- }
356
- const top = Object.entries(scores).sort((a, b) => b[1] - a[1])[0]
357
- return top ? top[0] : fallback
358
- }
359
-
360
- let department = scoreCategory(departmentKeywords, 'Other')
361
- let urgency = scoreCategory(urgencyKeywords, 'Low')
362
-
363
- // Prefer Health for clear medical emergencies
364
- if (/heart attack|cardiac arrest|chest pain|stroke|severe bleeding|medical emergency|unconscious|life-threatening/i.test(combinedText)) {
365
- department = 'Health'
366
- urgency = 'Critical'
367
- }
368
-
369
- // Urgency overrides
370
- if (/accident|collapsed|severe|दुर्घटना|गंभीर/i.test(combinedText)) {
371
- urgency = 'High'
372
- }
373
-
374
- if (department === 'Police' && /hit and run|killed|shooting|murder|हत्या|गोली|খুন|கொலை|ಕೊಲೆ/i.test(combinedText)) {
375
- urgency = 'Critical'
376
- }
377
- if (department === 'Rescue') {
378
- urgency = urgency === 'Low' ? 'High' : urgency
379
- }
380
-
381
- // ─── STEP 4: EXTRACT LOCATION ──────────────────────────────────────
382
- const extractLocation = () => {
383
- // Try English patterns
384
- const enMatch = text.match(/(?:near|at|in|beside|outside|opposite)\s+([A-Za-z0-9\s',.-]{5,80})/i)
385
- if (enMatch) return enMatch[1].trim()
386
-
387
- // Try Hindi location patterns
388
- const hiMatch = text.match(/(?:के पास|में|के सामने|के बगल में|पर)\s+(.{5,60})/i)
389
- if (hiMatch) return hiMatch[1].trim()
390
-
391
- // Try Telugu location patterns
392
- const teMatch = text.match(/(?:దగ్గర|లో|వద్ద)\s+(.{5,60})/i)
393
- if (teMatch) return teMatch[1].trim()
394
-
395
- // Try Tamil location patterns
396
- const taMatch = text.match(/(?:அருகில்|இல்|பக்கத்தில்)\s+(.{5,60})/i)
397
- if (taMatch) return taMatch[1].trim()
398
-
399
- // Fallback: look for common location words in any language
400
- const spl = text.split(/\n|\.|,/)
401
- return spl.find(s => /road|street|colony|area|block|sector|park|market|nagar|puram|pally|gali|chowk|rasta|marg|गली|चौक|मार्ग|నగర్|వీధి|தெரு|சாலை|রাস্তা/i.test(s))?.trim() || null
402
- }
403
-
404
- const location = extractLocation() || null
405
-
406
- // ─── STEP 5: GENERATE SUMMARY ──────────────────────────────────────
407
- const summary = (() => {
408
- const core = text.split(/[\.?\!]\s*/).filter(Boolean).slice(0, 2).join('. ')
409
- if (core.length < 20) return text.slice(0, 180)
410
- return core.length > 200 ? `${core.slice(0, 197)}...` : core
411
- })()
412
-
413
- // ─── STEP 6: GENERATE ACTION ITEMS ─────────────────────────────────
414
- const defaultActions = [
415
- 'Inspect the issue on site',
416
- 'Assign a responsible team',
417
- 'Provide status update to the resident'
418
- ]
419
-
420
- const actionCandidates = []
421
-
422
- if (department === 'Roads') {
423
- actionCandidates.push('Repair or resurface the affected road stretch')
424
- actionCandidates.push('Install warning signs and barricade the hazard')
425
- }
426
- if (department === 'Water') {
427
- actionCandidates.push('Fix the water leakage and test supply pressure')
428
- actionCandidates.push('Clear blockages in the pipeline and drain')
429
- }
430
- if (department === 'Sanitation') {
431
- actionCandidates.push('Clean the garbage accumulation and service the drain')
432
- actionCandidates.push('Schedule frequent waste collection for the area')
433
- }
434
- if (department === 'Electricity') {
435
- actionCandidates.push('Inspect power infrastructure and repair fault')
436
- actionCandidates.push('Restore electricity and replace defective fittings')
437
- }
438
- if (department === 'Health') {
439
- actionCandidates.push('Arrange medical support and sanitation response')
440
- actionCandidates.push('Deploy health inspection team to assess public health risk')
441
- }
442
- if (department === 'Hospital') {
443
- actionCandidates.push('Dispatch medical team and ensure hospital bed availability')
444
- actionCandidates.push('Coordinate with nearby hospitals for emergency care')
445
- }
446
- if (department === 'Rescue') {
447
- actionCandidates.push('Mobilize rescue team and emergency services immediately')
448
- actionCandidates.push('Coordinate evacuation and search-and-rescue operations')
449
- }
450
- if (department === 'Police') {
451
- actionCandidates.push('Register the FIR and patrol the area immediately')
452
- actionCandidates.push('Dispatch nearest patrol unit and collect evidence')
453
- }
454
- if (department === 'Education') {
455
- actionCandidates.push('Notify the education officer and inspect the institution')
456
- actionCandidates.push('Ensure compliance with educational standards and student safety')
457
- }
458
- if (department === 'Transport') {
459
- actionCandidates.push('Inspect the transport service and take corrective action')
460
- actionCandidates.push('Coordinate with the transport authority for route/schedule fixes')
461
- }
462
- if (department === 'Municipal') {
463
- actionCandidates.push('Deploy municipal inspection team to verify the complaint')
464
- actionCandidates.push('Issue notice if violation found and initiate corrective action')
465
- }
466
- if (department === 'Environment') {
467
- actionCandidates.push('Conduct environmental survey and check pollution levels')
468
- actionCandidates.push('Issue notice to violators and enforce environmental norms')
469
- }
470
-
471
- const action_items = [...new Set([...actionCandidates, ...defaultActions])].slice(0, 3)
472
-
473
- // ─── STEP 7: RESOLUTION ETA ────────────────────────────────────────
474
- const estimated_resolution = urgency === 'Critical' ? '12 hours'
475
- : urgency === 'High' ? '24-48 hours'
476
- : urgency === 'Medium' ? '3-5 days'
477
- : '7-14 days'
478
-
479
- const language_detected = detectedLanguage || language || 'Unknown'
480
-
481
- const parsed = {
482
- summary,
483
- department,
484
- urgency,
485
- action_items,
486
- location,
487
- estimated_resolution,
488
- language_detected
489
- }
490
-
491
- // ─── STEP 8: SAVE TO DATABASE ──────────────────────────────────────
492
- const { supabase } = await import('../../lib/supabase')
493
- const { data: saved, error } = await supabase
494
- .from('complaints')
495
- .insert({
496
- raw_text: text,
497
- language: language || 'Unknown',
498
- summary: parsed.summary,
499
- department: parsed.department,
500
- urgency: parsed.urgency,
501
- action_items: parsed.action_items,
502
- location: parsed.location,
503
- estimated_resolution: parsed.estimated_resolution,
504
- language_detected: parsed.language_detected,
505
- status: 'pending'
506
- })
507
- .select()
508
- .single()
509
-
510
- if (error) {
511
- console.error('Supabase insert error:', JSON.stringify(error))
512
- throw new Error(`Database error: ${error.message}`)
513
- }
514
-
515
- // ─── STEP 9: CRITICAL ALERT ────────────────────────────────────────
516
- if (parsed.urgency === 'Critical') {
517
- const base = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'
518
- await fetch(`${base}/api/alert`, {
519
- method: 'POST',
520
- headers: { 'Content-Type': 'application/json' },
521
- body: JSON.stringify({ complaint: saved })
522
- }).catch(e => console.error('Alert failed:', e))
523
- }
524
-
525
- res.status(200).json(saved)
526
- } catch (err) {
527
- console.error('Processing error:', err)
528
- res.status(500).json({ error: 'Processing failed', detail: err.message })
529
- }
530
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/api/status.js DELETED
@@ -1,14 +0,0 @@
1
- import { supabase } from '../../lib/supabase'
2
-
3
- export default async function handler(req, res) {
4
- if (req.method !== 'GET') return res.status(405).end()
5
- const { id } = req.query
6
- if (!id) return res.status(400).json({ error: 'Complaint ID required' })
7
- const { data, error } = await supabase
8
- .from('complaints')
9
- .select('id,summary,department,urgency,status,estimated_resolution,created_at')
10
- .eq('id', id)
11
- .single()
12
- if (error) return res.status(404).json({ error: 'Complaint not found' })
13
- res.status(200).json(data)
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/api/transcribe.js DELETED
@@ -1,69 +0,0 @@
1
- import formidable from 'formidable'
2
- import fs from 'fs'
3
- import os from 'os'
4
-
5
- export const config = { api: { bodyParser: false } }
6
-
7
- export default async function handler(req, res) {
8
- if (req.method !== 'POST') return res.status(405).end()
9
-
10
- const form = formidable({ uploadDir: os.tmpdir(), keepExtensions: true })
11
-
12
- form.parse(req, async (err, fields, files) => {
13
- if (err) return res.status(500).json({ error: 'File parse failed' })
14
- try {
15
- const audioFile = Array.isArray(files.audio) ? files.audio[0] : files.audio
16
- if (!audioFile) return res.status(400).json({ error: 'No audio file received' })
17
-
18
- // Read file as buffer (fixes stream incompatibility with native fetch)
19
- const fileBuffer = fs.readFileSync(audioFile.filepath)
20
- const fileName = audioFile.originalFilename || 'audio.webm'
21
- const mimeType = audioFile.mimetype || 'audio/webm'
22
-
23
- // Use Web FormData (native to Node 18+) instead of the npm form-data package
24
- const formData = new FormData()
25
- const blob = new Blob([fileBuffer], { type: mimeType })
26
- formData.append('file', blob, fileName)
27
- formData.append('model', 'whisper-1')
28
-
29
- const apiKey = process.env.OPENAI_API_KEY
30
- if (!apiKey) {
31
- fs.unlink(audioFile.filepath, () => {})
32
- return res.status(500).json({
33
- error: 'No OpenAI API key configured',
34
- useBrowserFallback: true
35
- })
36
- }
37
-
38
- const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
39
- method: 'POST',
40
- headers: {
41
- Authorization: `Bearer ${apiKey}`
42
- },
43
- body: formData
44
- })
45
-
46
- const data = await response.json()
47
- fs.unlink(audioFile.filepath, () => {})
48
-
49
- if (!response.ok) {
50
- console.error('OpenAI Transcription error:', response.status, data)
51
- return res.status(500).json({
52
- error: 'Transcription service failed',
53
- detail: data.error?.message || JSON.stringify(data),
54
- useBrowserFallback: true
55
- })
56
- }
57
-
58
- if (!data.text) throw new Error('No transcription text returned')
59
- res.status(200).json({ text: data.text })
60
- } catch (err) {
61
- console.error('Transcription error:', err)
62
- res.status(500).json({
63
- error: 'Transcription failed',
64
- detail: err.message,
65
- useBrowserFallback: true
66
- })
67
- }
68
- })
69
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/dashboard.jsx DELETED
@@ -1,405 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
- import { supabase } from '../lib/supabase'
3
- import Link from 'next/link'
4
- import { useRouter } from 'next/router'
5
- import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, ArcElement, Tooltip, Legend } from 'chart.js'
6
- import { Bar, Doughnut } from 'react-chartjs-2'
7
-
8
- ChartJS.register(CategoryScale, LinearScale, BarElement, ArcElement, Tooltip, Legend)
9
-
10
- const URGENCY = {
11
- Low: { hex: '#10b981', badge: 'bg-emerald-100 text-emerald-700', row: 'hover:bg-emerald-50/30' },
12
- Medium: { hex: '#f59e0b', badge: 'bg-amber-100 text-amber-700', row: 'hover:bg-amber-50/30' },
13
- High: { hex: '#f97316', badge: 'bg-orange-100 text-orange-700', row: 'hover:bg-orange-50/30' },
14
- Critical: { hex: '#ef4444', badge: 'bg-red-100 text-red-700', row: 'hover:bg-red-50/30' }
15
- }
16
-
17
- const DEPT_COLORS = ['#6366f1','#8b5cf6','#06b6d4','#10b981','#f59e0b','#ef4444','#ec4899','#64748b','#22c55e','#f43f5e','#0ea5e9','#a855f7','#14b8a6']
18
- const DEPT_ICONS = { Health:'🏥', Hospital:'🏨', Police:'🚔', Sanitation:'🗑️', Roads:'🛣️', Water:'💧', Electricity:'⚡', Education:'🎓', Rescue:'🚒', Transport:'🚌', Municipal:'🏛️', Environment:'🌿', Other:'📋' }
19
-
20
- function StatCard({ label, value, color, sub }) {
21
- return (
22
- <div className="bg-white rounded-2xl border border-gray-200 p-5 shadow-sm">
23
- <p className="text-xs font-medium text-gray-400 uppercase tracking-wide mb-2">{label}</p>
24
- <p className={`text-3xl font-bold ${color}`}>{value}</p>
25
- {sub && <p className="text-xs text-gray-400 mt-1">{sub}</p>}
26
- </div>
27
- )
28
- }
29
-
30
- export default function Dashboard() {
31
- const [complaints, setComplaints] = useState([])
32
- const [loading, setLoading] = useState(true)
33
- const [user, setUser] = useState(null)
34
- const [authLoading, setAuthLoading] = useState(true)
35
- const [urgFilter, setUrgFilter] = useState('All')
36
- const [deptFilter, setDeptFilter] = useState('All')
37
- const [selected, setSelected] = useState(null)
38
- const [search, setSearch] = useState('')
39
- const router = useRouter()
40
-
41
- useEffect(() => {
42
- const checkAuth = async () => {
43
- const { data: { session } } = await supabase.auth.getSession()
44
- if (!session) {
45
- router.push('/login')
46
- return
47
- }
48
- setUser(session.user)
49
- setAuthLoading(false)
50
- }
51
- checkAuth()
52
-
53
- const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
54
- if (!session) {
55
- router.push('/login')
56
- } else {
57
- setUser(session.user)
58
- }
59
- })
60
-
61
- return () => subscription.unsubscribe()
62
- }, [router])
63
-
64
- useEffect(() => {
65
- supabase.from('complaints').select('*').order('created_at', { ascending: false })
66
- .then(({ data }) => { setComplaints(data || []); setLoading(false) })
67
-
68
- const sub = supabase.channel('realtime-complaints')
69
- .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'complaints' },
70
- p => setComplaints(prev => [p.new, ...prev]))
71
- .subscribe()
72
- return () => supabase.removeChannel(sub)
73
- }, [])
74
-
75
- const handleLogout = async () => {
76
- await supabase.auth.signOut()
77
- router.push('/login')
78
- }
79
-
80
- const departments = [...new Set(complaints.map(c => c.department).filter(Boolean))]
81
- const urgencies = ['Low','Medium','High','Critical']
82
-
83
- const filtered = complaints.filter(c => {
84
- const u = urgFilter === 'All' || c.urgency === urgFilter
85
- const d = deptFilter === 'All' || c.department === deptFilter
86
- const s = !search || c.summary?.toLowerCase().includes(search.toLowerCase()) || c.location?.toLowerCase().includes(search.toLowerCase())
87
- return u && d && s
88
- })
89
-
90
- const deptCounts = departments.map(d => complaints.filter(c => c.department === d).length)
91
- const urgencyCounts = urgencies.map(u => complaints.filter(c => c.urgency === u).length)
92
-
93
- const barData = {
94
- labels: departments,
95
- datasets: [{ label: 'Complaints', data: deptCounts, backgroundColor: DEPT_COLORS, borderRadius: 8, borderSkipped: false }]
96
- }
97
- const donutData = {
98
- labels: urgencies,
99
- datasets: [{ data: urgencyCounts, backgroundColor: urgencies.map(u => URGENCY[u].hex), borderWidth: 0, hoverOffset: 8 }]
100
- }
101
- const barOpts = {
102
- responsive: true,
103
- plugins: { legend: { display: false } },
104
- scales: {
105
- y: { beginAtZero: true, ticks: { stepSize: 1, color: '#9CA3AF' }, grid: { color: '#F3F4F6' }, border: { display: false } },
106
- x: { ticks: { color: '#6B7280' }, grid: { display: false }, border: { display: false } }
107
- }
108
- }
109
-
110
- return (
111
- <div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50">
112
-
113
- {/* ── NAVBAR ───────────────────────────────────────────────────────────── */}
114
- <nav className="bg-white border-b border-gray-200 sticky top-0 z-30">
115
- <div className="max-w-7xl mx-auto px-6 h-14 flex items-center justify-between">
116
- <div className="flex items-center gap-3">
117
- <div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center">
118
- <span className="text-white text-sm font-bold">C</span>
119
- </div>
120
- <div>
121
- <span className="font-semibold text-gray-900 text-sm">CitizenCare</span>
122
- <span className="text-gray-400 text-xs ml-2">Authority Dashboard</span>
123
- </div>
124
- </div>
125
- <div className="flex items-center gap-3">
126
- {complaints.some(c => c.urgency === 'Critical' && c.status === 'pending') && (
127
- <span className="flex items-center gap-1.5 text-xs text-red-600 bg-red-50 border border-red-200 px-2.5 py-1 rounded-full font-medium animate-pulse">
128
- <span className="w-1.5 h-1.5 bg-red-500 rounded-full"></span>
129
- {complaints.filter(c => c.urgency === 'Critical' && c.status === 'pending').length} critical pending
130
- </span>
131
- )}
132
- <span className="text-xs text-gray-500">{user?.email}</span>
133
- <button
134
- onClick={handleLogout}
135
- className="text-xs font-medium text-gray-600 border border-gray-200 px-3 py-1.5 rounded-lg hover:bg-gray-50 transition-all"
136
- >
137
- Logout
138
- </button>
139
- <Link href="/" className="text-xs font-medium text-indigo-600 border border-indigo-200 px-3 py-1.5 rounded-lg hover:bg-indigo-50 transition-all">
140
- ← Citizen portal
141
- </Link>
142
- </div>
143
- </div>
144
- </nav>
145
-
146
- <div className="max-w-7xl mx-auto px-6 py-6">
147
-
148
- {authLoading ? (
149
- <div className="flex items-center justify-center py-32 text-gray-300 text-sm gap-3">
150
- <svg className="animate-spin w-5 h-5 text-indigo-400" fill="none" viewBox="0 0 24 24">
151
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
152
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
153
- </svg>
154
- Checking authentication...
155
- </div>
156
- ) : loading ? (
157
- <div className="flex items-center justify-center py-32 text-gray-300 text-sm gap-3">
158
- <svg className="animate-spin w-5 h-5 text-indigo-400" fill="none" viewBox="0 0 24 24">
159
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
160
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
161
- </svg>
162
- Loading complaints...
163
- </div>
164
- ) : (
165
- <>
166
- {/* ── STAT CARDS ─────────────────────────────────────────────────── */}
167
- <div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mb-6">
168
- <StatCard label="Total" value={complaints.length} color="text-gray-900"
169
- sub={`${complaints.filter(c=>c.status==='pending').length} pending`} />
170
- <StatCard label="Critical" value={complaints.filter(c=>c.urgency==='Critical').length} color="text-red-600"
171
- sub="Require immediate action" />
172
- <StatCard label="Pending" value={complaints.filter(c=>c.status==='pending').length} color="text-amber-600"
173
- sub="Awaiting resolution" />
174
- <StatCard label="Resolved" value={complaints.filter(c=>c.status==='resolved').length} color="text-emerald-600"
175
- sub={complaints.length ? `${Math.round(complaints.filter(c=>c.status==='resolved').length/complaints.length*100)}% resolution rate` : '0%'} />
176
- </div>
177
-
178
- {/* ── CHARTS ─────────────────────────────────────────────────────── */}
179
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
180
- <div className="lg:col-span-2 bg-white rounded-2xl border border-gray-200 p-5 shadow-sm">
181
- <p className="text-sm font-semibold text-gray-700 mb-1">Complaints by department</p>
182
- <p className="text-xs text-gray-400 mb-4">Total volume per government department</p>
183
- {departments.length > 0 ? (
184
- <Bar data={barData} options={barOpts} height={120} />
185
- ) : (
186
- <div className="flex items-center justify-center h-36 text-gray-200 text-sm">No data yet — submit a complaint to see charts</div>
187
- )}
188
- </div>
189
-
190
- <div className="bg-white rounded-2xl border border-gray-200 p-5 shadow-sm">
191
- <p className="text-sm font-semibold text-gray-700 mb-1">Urgency breakdown</p>
192
- <p className="text-xs text-gray-400 mb-4">Distribution of complaint severity</p>
193
- {complaints.length > 0 ? (
194
- <div className="space-y-3">
195
- <Doughnut data={donutData} options={{ plugins: { legend: { display: false } }, cutout: '72%' }} />
196
- <div className="space-y-2 mt-2">
197
- {urgencies.map((u, i) => (
198
- <div key={u} className="flex items-center justify-between text-sm">
199
- <div className="flex items-center gap-2">
200
- <span className="w-2.5 h-2.5 rounded-full" style={{ background: URGENCY[u].hex }}></span>
201
- <span className="text-gray-600 text-xs">{u}</span>
202
- </div>
203
- <div className="flex items-center gap-2">
204
- <div className="w-16 bg-gray-100 rounded-full h-1.5">
205
- <div className="h-1.5 rounded-full transition-all" style={{
206
- width: complaints.length ? `${urgencyCounts[i]/complaints.length*100}%` : '0%',
207
- background: URGENCY[u].hex
208
- }}/>
209
- </div>
210
- <span className="text-xs font-medium text-gray-700 w-4 text-right">{urgencyCounts[i]}</span>
211
- </div>
212
- </div>
213
- ))}
214
- </div>
215
- </div>
216
- ) : (
217
- <div className="flex items-center justify-center h-36 text-gray-200 text-sm">No data yet</div>
218
- )}
219
- </div>
220
- </div>
221
-
222
- {/* ── TABLE ──────────────────────────────────────────────────────── */}
223
- <div className="bg-white rounded-2xl border border-gray-200 shadow-sm overflow-hidden">
224
- {/* Table toolbar */}
225
- <div className="p-4 border-b border-gray-100 flex flex-wrap items-center gap-3">
226
- <input value={search} onChange={e => setSearch(e.target.value)}
227
- placeholder="Search complaints..."
228
- className="border border-gray-200 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-200 w-48 bg-gray-50 focus:bg-white transition-all"/>
229
-
230
- <div className="flex gap-1.5 flex-wrap">
231
- {['All',...urgencies].map(f => (
232
- <button key={f} onClick={() => setUrgFilter(f)}
233
- className={`px-3 py-1 rounded-full text-xs font-medium transition-all ${urgFilter === f ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'}`}>
234
- {f}
235
- </button>
236
- ))}
237
- </div>
238
-
239
- <select value={deptFilter} onChange={e => setDeptFilter(e.target.value)}
240
- className="text-xs border border-gray-200 rounded-lg px-2 py-1.5 text-gray-600 bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-200">
241
- <option value="All">All departments</option>
242
- {departments.map(d => <option key={d}>{d}</option>)}
243
- </select>
244
-
245
- <span className="ml-auto text-xs text-gray-400 font-medium">{filtered.length} of {complaints.length}</span>
246
- </div>
247
-
248
- <div className="overflow-x-auto">
249
- <table className="w-full text-sm">
250
- <thead>
251
- <tr className="text-left text-xs font-semibold text-gray-400 bg-gray-50/80 border-b border-gray-100">
252
- <th className="px-5 py-3">ID</th>
253
- <th className="px-5 py-3">Summary</th>
254
- <th className="px-5 py-3">Dept.</th>
255
- <th className="px-5 py-3">Urgency</th>
256
- <th className="px-5 py-3">ETA</th>
257
- <th className="px-5 py-3">Status</th>
258
- <th className="px-5 py-3">Action</th>
259
- </tr>
260
- </thead>
261
- <tbody>
262
- {filtered.length === 0 ? (
263
- <tr><td colSpan={7} className="text-center py-16 text-gray-200 text-sm">No complaints found</td></tr>
264
- ) : filtered.map(c => (
265
- <tr key={c.id}
266
- className={`border-b border-gray-50 cursor-pointer transition-colors ${URGENCY[c.urgency]?.row || 'hover:bg-gray-50'}`}
267
- onClick={() => setSelected(c)}>
268
- <td className="px-5 py-3.5 font-mono text-xs text-gray-400">{c.id?.slice(0,8)}</td>
269
- <td className="px-5 py-3.5 max-w-xs">
270
- <p className="text-gray-700 truncate text-sm">{c.summary}</p>
271
- {c.location && <p className="text-xs text-gray-400 mt-0.5 truncate">{c.location}</p>}
272
- </td>
273
- <td className="px-5 py-3.5 whitespace-nowrap text-gray-600 text-sm">
274
- {DEPT_ICONS[c.department]||'📋'} {c.department}
275
- </td>
276
- <td className="px-5 py-3.5 whitespace-nowrap">
277
- <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${URGENCY[c.urgency]?.badge || 'bg-gray-100 text-gray-600'}`}>
278
- {c.urgency}
279
- </span>
280
- </td>
281
- <td className="px-5 py-3.5 text-gray-500 text-xs whitespace-nowrap">{c.estimated_resolution}</td>
282
- <td className="px-5 py-3.5">
283
- <span className={`text-xs font-medium flex items-center gap-1.5 ${c.status === 'resolved' ? 'text-emerald-600' : 'text-amber-600'}`}>
284
- <span className={`w-1.5 h-1.5 rounded-full ${c.status === 'resolved' ? 'bg-emerald-500' : 'bg-amber-400'}`}></span>
285
- {c.status === 'resolved' ? 'Resolved' : 'Pending'}
286
- </span>
287
- </td>
288
- <td className="px-5 py-3.5 whitespace-nowrap" onClick={e => e.stopPropagation()}>
289
- {c.status !== 'resolved' ? (
290
- <button onClick={() => updateStatus(c.id, 'resolved')}
291
- className="text-xs text-emerald-600 border border-emerald-200 px-2.5 py-1.5 rounded-lg hover:bg-emerald-50 transition-all font-medium">
292
- Resolve
293
- </button>
294
- ) : (
295
- <button onClick={() => updateStatus(c.id, 'pending')}
296
- className="text-xs text-gray-400 border border-gray-200 px-2.5 py-1.5 rounded-lg hover:bg-gray-50 transition-all">
297
- Reopen
298
- </button>
299
- )}
300
- </td>
301
- </tr>
302
- ))}
303
- </tbody>
304
- </table>
305
- </div>
306
- </div>
307
- </>
308
- )}
309
- </div>
310
-
311
- {/* ── DETAIL DRAWER ────────────────────────────────────────────────────── */}
312
- {selected && (
313
- <div className="fixed inset-0 z-40 flex justify-end animate-fade-in"
314
- style={{ background: 'rgba(0,0,0,0.25)' }}
315
- onClick={() => setSelected(null)}>
316
- <div className="w-full max-w-md bg-white h-full overflow-y-auto shadow-2xl animate-slide-up"
317
- onClick={e => e.stopPropagation()}>
318
- <div className="p-6 space-y-5">
319
- {/* Drawer header */}
320
- <div className="flex items-start justify-between">
321
- <div>
322
- <p className="font-mono text-xs text-gray-400 mb-2 break-all">{selected.id}</p>
323
- <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${URGENCY[selected.urgency]?.badge}`}>
324
- {selected.urgency} priority
325
- </span>
326
- </div>
327
- <button onClick={() => setSelected(null)}
328
- className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition-all text-xl leading-none">
329
- ×
330
- </button>
331
- </div>
332
-
333
- {/* Dept + date */}
334
- <div className="flex items-center justify-between text-sm">
335
- <span className="font-medium text-gray-700">
336
- {DEPT_ICONS[selected.department]||'📋'} {selected.department}
337
- </span>
338
- <span className="text-xs text-gray-400">{new Date(selected.created_at).toLocaleString('en-IN')}</span>
339
- </div>
340
-
341
- {/* Summary */}
342
- <div className={`rounded-xl p-4 ${URGENCY[selected.urgency]?.badge?.includes('red') ? 'bg-red-50' : URGENCY[selected.urgency]?.badge?.includes('orange') ? 'bg-orange-50' : URGENCY[selected.urgency]?.badge?.includes('amber') ? 'bg-amber-50' : 'bg-emerald-50'}`}>
343
- <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">AI Summary</p>
344
- <p className="text-sm text-gray-700 leading-relaxed">{selected.summary}</p>
345
- </div>
346
-
347
- {/* Meta */}
348
- <div className="grid grid-cols-2 gap-3">
349
- {[
350
- { label: 'ETA', value: selected.estimated_resolution },
351
- { label: 'Language', value: selected.language_detected },
352
- { label: 'Location', value: selected.location || 'Not specified' },
353
- { label: 'Status', value: selected.status === 'resolved' ? 'Resolved ✓' : 'Pending...' }
354
- ].map(item => (
355
- <div key={item.label} className="bg-gray-50 rounded-xl p-3">
356
- <p className="text-xs text-gray-400 mb-1">{item.label}</p>
357
- <p className="text-sm font-medium text-gray-800">{item.value}</p>
358
- </div>
359
- ))}
360
- </div>
361
-
362
- {/* Action items */}
363
- <div>
364
- <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">Action items</p>
365
- <div className="space-y-2">
366
- {selected.action_items?.map((a, i) => (
367
- <div key={i} className="flex items-start gap-3 p-3 bg-indigo-50 border border-indigo-100 rounded-xl">
368
- <span className="w-5 h-5 bg-indigo-600 text-white text-xs rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 font-semibold">{i+1}</span>
369
- <span className="text-sm text-indigo-900">{a}</span>
370
- </div>
371
- ))}
372
- </div>
373
- </div>
374
-
375
- {/* Raw complaint */}
376
- {selected.raw_text && (
377
- <details className="bg-gray-50 rounded-xl overflow-hidden">
378
- <summary className="px-4 py-3 text-xs text-gray-500 cursor-pointer font-medium hover:bg-gray-100 transition-all">
379
- View original complaint text
380
- </summary>
381
- <p className="px-4 pb-4 text-xs text-gray-600 leading-relaxed">{selected.raw_text}</p>
382
- </details>
383
- )}
384
-
385
- {/* Status buttons */}
386
- <div className="pt-2 border-t border-gray-100">
387
- <p className="text-xs text-gray-400 mb-3 font-medium">Update status</p>
388
- <div className="flex gap-3">
389
- <button onClick={() => updateStatus(selected.id, 'resolved')}
390
- className="flex-1 py-2.5 text-sm font-semibold text-emerald-600 border border-emerald-200 rounded-xl hover:bg-emerald-50 transition-all">
391
- Mark resolved
392
- </button>
393
- <button onClick={() => updateStatus(selected.id, 'pending')}
394
- className="flex-1 py-2.5 text-sm font-medium text-amber-600 border border-amber-200 rounded-xl hover:bg-amber-50 transition-all">
395
- Mark pending
396
- </button>
397
- </div>
398
- </div>
399
- </div>
400
- </div>
401
- </div>
402
- )}
403
- </div>
404
- )
405
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/index.jsx DELETED
@@ -1,568 +0,0 @@
1
- import { useState, useRef } from 'react'
2
- import Link from 'next/link'
3
-
4
- const LANGUAGES = ['English','Hindi','Telugu','Tamil','Kannada','Marathi','Bengali','Gujarati','Punjabi','Odia']
5
-
6
- const DEPT_ICONS = {
7
- Health: '🏥', Hospital: '🏨', Police: '🚔', Sanitation: '🗑️', Roads: '🛣️',
8
- Water: '💧', Electricity: '⚡', Education: '🎓', Rescue: '🚒',
9
- Transport: '🚌', Municipal: '🏛️', Environment: '🌿', Other: '📋'
10
- }
11
-
12
- const URGENCY_CONFIG = {
13
- Low: { bar: 'bg-emerald-500', bg: 'bg-emerald-50', border: 'border-emerald-200', text: 'text-emerald-700', badge: 'bg-emerald-100 text-emerald-700', dot: 'bg-emerald-400', label: 'Low Priority' },
14
- Medium: { bar: 'bg-amber-500', bg: 'bg-amber-50', border: 'border-amber-200', text: 'text-amber-700', badge: 'bg-amber-100 text-amber-700', dot: 'bg-amber-400', label: 'Medium Priority' },
15
- High: { bar: 'bg-orange-500', bg: 'bg-orange-50', border: 'border-orange-200', text: 'text-orange-700', badge: 'bg-orange-100 text-orange-700', dot: 'bg-orange-400', label: 'High Priority' },
16
- Critical: { bar: 'bg-red-500', bg: 'bg-red-50', border: 'border-red-200', text: 'text-red-700', badge: 'bg-red-100 text-red-700', dot: 'bg-red-500', label: 'Critical — Alert sent' }
17
- }
18
-
19
- function StepBadge({ n, active, done }) {
20
- return (
21
- <div className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold transition-all
22
- ${done ? 'bg-indigo-600 text-white' : active ? 'bg-indigo-600 text-white ring-4 ring-indigo-100' : 'bg-gray-100 text-gray-400'}`}>
23
- {done ? '✓' : n}
24
- </div>
25
- )
26
- }
27
-
28
- function Spinner() {
29
- return (
30
- <svg className="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
31
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
32
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
33
- </svg>
34
- )
35
- }
36
-
37
- export default function CitizenPortal() {
38
- const [activeTab, setActiveTab] = useState('submit')
39
- const [text, setText] = useState('')
40
- const [lang, setLang] = useState('English')
41
- const [loading, setLoading] = useState(false)
42
- const [result, setResult] = useState(null)
43
- const [error, setError] = useState(null)
44
- const [recording, setRecording] = useState(false)
45
- const [transcribing, setTranscribing] = useState(false)
46
- const [copied, setCopied] = useState(false)
47
- const [trackId, setTrackId] = useState('')
48
- const [trackResult, setTrackResult] = useState(null)
49
- const [trackLoading, setTrackLoading] = useState(false)
50
- const mediaRef = useRef(null)
51
- const chunksRef = useRef([])
52
- const resultRef = useRef(null)
53
- const speechRef = useRef(null)
54
-
55
- // ── LANGUAGE CODE MAP for Web Speech API ──
56
- const LANG_CODES = {
57
- English: 'en-IN', Hindi: 'hi-IN', Telugu: 'te-IN', Tamil: 'ta-IN',
58
- Kannada: 'kn-IN', Marathi: 'mr-IN', Bengali: 'bn-IN',
59
- Gujarati: 'gu-IN', Punjabi: 'pa-IN', Odia: 'or-IN'
60
- }
61
-
62
- async function startRecording() {
63
- setError(null)
64
-
65
- // ── TRY 1: Browser Web Speech API (works offline, no credits needed) ──
66
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition
67
- if (SpeechRecognition) {
68
- try {
69
- const recognition = new SpeechRecognition()
70
- recognition.lang = LANG_CODES[lang] || 'en-IN'
71
- recognition.interimResults = false
72
- recognition.continuous = true
73
- recognition.maxAlternatives = 1
74
-
75
- let finalTranscript = ''
76
-
77
- recognition.onresult = (event) => {
78
- for (let i = event.resultIndex; i < event.results.length; i++) {
79
- if (event.results[i].isFinal) {
80
- finalTranscript += event.results[i][0].transcript + ' '
81
- }
82
- }
83
- }
84
-
85
- recognition.onerror = (event) => {
86
- console.error('Speech recognition error:', event.error)
87
- if (event.error === 'not-allowed') {
88
- setError('Microphone access denied. Please allow microphone and try again.')
89
- }
90
- setRecording(false)
91
- }
92
-
93
- recognition.onend = () => {
94
- if (finalTranscript.trim()) {
95
- setText(prev => prev ? prev + ' ' + finalTranscript.trim() : finalTranscript.trim())
96
- }
97
- setRecording(false)
98
- }
99
-
100
- speechRef.current = recognition
101
- recognition.start()
102
- setRecording(true)
103
- return
104
- } catch (speechErr) {
105
- console.error('Web Speech API failed, trying MediaRecorder fallback:', speechErr)
106
- }
107
- }
108
-
109
- // ── TRY 2: MediaRecorder + OpenAI Whisper API fallback ──
110
- try {
111
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
112
- mediaRef.current = new MediaRecorder(stream)
113
- chunksRef.current = []
114
- mediaRef.current.ondataavailable = e => chunksRef.current.push(e.data)
115
- mediaRef.current.onstop = async () => {
116
- setTranscribing(true)
117
- try {
118
- const blob = new Blob(chunksRef.current, { type: 'audio/webm' })
119
- const fd = new FormData()
120
- fd.append('audio', blob, 'complaint.webm')
121
- const r = await fetch('/api/transcribe', { method: 'POST', body: fd })
122
- const data = await r.json()
123
- if (data.error) throw new Error(data.error)
124
- setText(data.text)
125
- } catch {
126
- setError('Voice transcription failed. Please type your complaint instead.')
127
- } finally {
128
- setTranscribing(false)
129
- }
130
- stream.getTracks().forEach(t => t.stop())
131
- }
132
- mediaRef.current.start()
133
- setRecording(true)
134
- } catch {
135
- setError('Microphone access denied. Please allow microphone and try again.')
136
- }
137
- }
138
-
139
- function stopRecording() {
140
- // Stop Web Speech API if active
141
- if (speechRef.current) {
142
- speechRef.current.stop()
143
- speechRef.current = null
144
- }
145
- // Stop MediaRecorder if active
146
- mediaRef.current?.stop()
147
- setRecording(false)
148
- }
149
-
150
- async function submit() {
151
- if (!text.trim()) return
152
- setLoading(true)
153
- setResult(null)
154
- setError(null)
155
- try {
156
- const r = await fetch('/api/process', {
157
- method: 'POST',
158
- headers: { 'Content-Type': 'application/json' },
159
- body: JSON.stringify({ text, language: lang })
160
- })
161
- const data = await r.json()
162
- if (!r.ok) throw new Error(data.detail || data.error)
163
- setResult(data)
164
- setText('')
165
- setTimeout(() => resultRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 100)
166
- } catch (err) {
167
- setError(err.message || 'Something went wrong. Please try again.')
168
- } finally {
169
- setLoading(false)
170
- }
171
- }
172
-
173
- async function trackComplaint() {
174
- if (!trackId.trim()) return
175
- setTrackLoading(true)
176
- setTrackResult(null)
177
- try {
178
- const r = await fetch(`/api/status?id=${trackId.trim()}`)
179
- const data = await r.json()
180
- if (!r.ok) throw new Error('Not found')
181
- setTrackResult(data)
182
- } catch {
183
- setTrackResult({ error: 'No complaint found with this ID. Please check and try again.' })
184
- } finally {
185
- setTrackLoading(false)
186
- }
187
- }
188
-
189
- function copyId() {
190
- if (result?.id) {
191
- navigator.clipboard.writeText(result.id)
192
- setCopied(true)
193
- setTimeout(() => setCopied(false), 2000)
194
- }
195
- }
196
-
197
- function getProgress(eta) {
198
- if (eta.includes('12 hours')) return 90
199
- if (eta.includes('24-48 hours')) return 70
200
- if (eta.includes('3-5 days')) return 40
201
- if (eta.includes('7-14 days')) return 20
202
- return 50
203
- }
204
-
205
- const uc = result ? URGENCY_CONFIG[result.urgency] || URGENCY_CONFIG.Low : null
206
- const charCount = text.length
207
- const maxChars = 1000
208
-
209
- return (
210
- <div className="min-h-screen" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)' }}>
211
-
212
- {/* ── NAVBAR ─────────────────────────────────────────────────────────── */}
213
- <nav className="bg-white/80 backdrop-blur border-b border-gray-200/60 sticky top-0 z-30">
214
- <div className="max-w-4xl mx-auto px-4 h-14 flex items-center justify-between">
215
- <div className="flex items-center gap-2.5">
216
- <div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center">
217
- <span className="text-white text-sm font-bold">C</span>
218
- </div>
219
- <div>
220
- <span className="font-semibold text-gray-900 text-sm">CitizenCare</span>
221
- <span className="text-gray-400 text-xs ml-2 hidden sm:inline">Citizen Grievance Portal</span>
222
- </div>
223
- </div>
224
- <div className="flex items-center gap-3">
225
- <Link href="/support"
226
- className="text-xs font-medium text-indigo-600 border border-indigo-200 px-3 py-1.5 rounded-lg hover:bg-indigo-50 transition-all">
227
- Help & Support
228
- </Link>
229
- <Link href="/dashboard"
230
- className="text-xs font-medium text-indigo-600 border border-indigo-200 px-3 py-1.5 rounded-lg hover:bg-indigo-50 transition-all">
231
- Authority Dashboard →
232
- </Link>
233
- </div>
234
- </div>
235
- </nav>
236
-
237
- <div className="max-w-4xl mx-auto px-4 py-8">
238
-
239
- {/* ── HERO ───────────────────────────────────────────────────────────── */}
240
- <div className="text-center mb-8">
241
- <div className="inline-flex items-center gap-2 bg-indigo-50 border border-indigo-200 text-indigo-700 text-xs font-medium px-3 py-1.5 rounded-full mb-4">
242
- <span>Processed by local keyword parser</span>
243
- <span className="w-1 h-1 bg-indigo-400 rounded-full"></span>
244
- <span>Results in under 3 seconds</span>
245
- </div>
246
- <h1 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-3 leading-tight">
247
- Your complaint, <span className="text-indigo-600">heard instantly</span>
248
- </h1>
249
- <p className="text-gray-500 text-base max-w-xl mx-auto leading-relaxed">
250
- Submit in any language — text or voice. Our AI converts it into a structured report and routes it to the right authority automatically.
251
- </p>
252
- </div>
253
-
254
- {/* ── STAT PILLS ─────────────────────────────────────────────────────── */}
255
- <div className="flex flex-wrap justify-center gap-3 mb-8">
256
- {[
257
- { label: '13 departments', icon: '🏛️' },
258
- { label: '10+ languages', icon: '🌐' },
259
- { label: 'Voice + Text', icon: '🎙️' },
260
- { label: 'Instant alerts', icon: '🔔' }
261
- ].map(s => (
262
- <div key={s.label} className="flex items-center gap-1.5 bg-white border border-gray-200 px-3 py-1.5 rounded-full text-sm text-gray-600 shadow-sm">
263
- <span className="text-base" style={{fontSize:'14px'}}>{s.icon}</span>
264
- <span>{s.label}</span>
265
- </div>
266
- ))}
267
- </div>
268
-
269
- {/* ── TABS ───────────────────────────────────────────────────────────── */}
270
- <div className="flex gap-1 bg-white border border-gray-200 p-1 rounded-xl mb-6 w-fit mx-auto shadow-sm">
271
- {[
272
- { id: 'submit', label: 'Submit complaint' },
273
- { id: 'track', label: 'Track complaint' }
274
- ].map(tab => (
275
- <button key={tab.id} onClick={() => { setActiveTab(tab.id); setError(null) }}
276
- className={`px-5 py-2 rounded-lg text-sm font-medium transition-all ${
277
- activeTab === tab.id
278
- ? 'bg-indigo-600 text-white shadow-sm'
279
- : 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
280
- }`}>
281
- {tab.label}
282
- </button>
283
- ))}
284
- </div>
285
-
286
- {/* ══════════════════════════════════════════════════════════════════════
287
- SUBMIT TAB
288
- ══════════════════════════════════════════════════════════════════════ */}
289
- {activeTab === 'submit' && (
290
- <div className="space-y-4 animate-fade-in">
291
-
292
- {/* Steps indicator */}
293
- <div className="flex items-center justify-center gap-0 mb-6">
294
- {['Write complaint','AI processes','Report generated'].map((label, i) => (
295
- <div key={i} className="flex items-center">
296
- <div className="flex flex-col items-center gap-1">
297
- <StepBadge n={i+1} active={i===0 && !result} done={result && i < 2 || (result && i === 2)} />
298
- <span className="text-xs text-gray-400 hidden sm:block">{label}</span>
299
- </div>
300
- {i < 2 && <div className={`w-12 sm:w-20 h-0.5 mx-2 mb-4 transition-all ${result ? 'bg-indigo-400' : 'bg-gray-200'}`}/>}
301
- </div>
302
- ))}
303
- </div>
304
-
305
- {/* Main form card */}
306
- <div className="bg-white rounded-2xl border border-gray-200 shadow-sm overflow-hidden">
307
- {/* Card header */}
308
- <div className="px-6 pt-5 pb-4 border-b border-gray-100">
309
- <h2 className="font-semibold text-gray-800 text-base">Describe your complaint</h2>
310
- <p className="text-xs text-gray-400 mt-0.5">Be as detailed as possible — include location, time, and what happened</p>
311
- </div>
312
-
313
- <div className="p-6 space-y-4">
314
- {/* Language + Voice row */}
315
- <div className="flex gap-3 flex-wrap">
316
- <div className="flex items-center gap-2">
317
- <label className="text-xs font-medium text-gray-500">Language</label>
318
- <select value={lang} onChange={e => setLang(e.target.value)}
319
- className="border border-gray-200 rounded-lg px-3 py-2 text-sm text-gray-700 bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-200 focus:bg-white transition-all">
320
- {LANGUAGES.map(l => <option key={l}>{l}</option>)}
321
- </select>
322
- </div>
323
-
324
- <button
325
- onClick={recording ? stopRecording : startRecording}
326
- disabled={transcribing}
327
- className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium border transition-all ${
328
- recording ? 'bg-red-50 border-red-300 text-red-600 animate-pulse' :
329
- transcribing ? 'bg-gray-50 border-gray-200 text-gray-400 cursor-wait' :
330
- 'bg-gray-50 border-gray-200 text-gray-600 hover:bg-indigo-50 hover:border-indigo-200 hover:text-indigo-600'
331
- }`}>
332
- {recording ? (
333
- <><span className="w-2 h-2 bg-red-500 rounded-full pulse-dot"></span> Stop recording</>
334
- ) : transcribing ? (
335
- <><Spinner /> Transcribing...</>
336
- ) : (
337
- <><span className="text-base" style={{fontSize:'14px'}}>🎙️</span> Voice input</>
338
- )}
339
- </button>
340
- </div>
341
-
342
- {/* Textarea */}
343
- <div className="relative">
344
- <textarea
345
- value={text}
346
- onChange={e => setText(e.target.value.slice(0, maxChars))}
347
- placeholder={`Write your complaint here in any language...\n\nExample: "There is a large pothole near Ramaiah School on MG Road. Two bikes fell last week. Nobody has fixed it in 3 weeks despite multiple calls."`}
348
- className="w-full border border-gray-200 rounded-xl p-4 text-sm text-gray-800 resize-none focus:outline-none focus:ring-2 focus:ring-indigo-300 focus:border-indigo-300 placeholder-gray-300 transition-all bg-gray-50 focus:bg-white"
349
- rows={6}
350
- />
351
- <span className={`absolute bottom-3 right-3 text-xs ${charCount > maxChars * 0.9 ? 'text-orange-400' : 'text-gray-300'}`}>
352
- {charCount}/{maxChars}
353
- </span>
354
- </div>
355
-
356
- {/* Error */}
357
- {error && (
358
- <div className="flex items-start gap-2 p-3 bg-red-50 border border-red-200 rounded-xl text-sm text-red-600 animate-slide-up">
359
- <span className="text-base mt-0.5" style={{fontSize:'14px'}}>⚠️</span>
360
- {error}
361
- </div>
362
- )}
363
-
364
- {/* Submit button */}
365
- <button
366
- onClick={submit}
367
- disabled={loading || !text.trim() || transcribing}
368
- className="w-full bg-indigo-600 text-white rounded-xl py-3.5 text-sm font-semibold hover:bg-indigo-700 active:scale-[.99] disabled:opacity-40 disabled:cursor-not-allowed transition-all flex items-center justify-center gap-2 shadow-sm">
369
- {loading ? (
370
- <><Spinner /> Processing your complaint...</>
371
- ) : (
372
- <><span className="text-base" style={{fontSize:'14px'}}>📤</span> Submit complaint</>
373
- )}
374
- </button>
375
-
376
- <p className="text-center text-xs text-gray-400">
377
- Your complaint is processed using local keyword detection and routed to the best department within seconds.
378
- </p>
379
- </div>
380
- </div>
381
-
382
- {/* ── RESULT CARD ─────────────────────────────────────────────────── */}
383
- {result && (
384
- <div ref={resultRef} className={`bg-white rounded-2xl border-2 ${uc.border} shadow-sm overflow-hidden animate-slide-up`}>
385
-
386
- {/* Urgency banner */}
387
- <div className={`${uc.bar} px-6 py-3 flex items-center justify-between`}>
388
- <div className="flex items-center gap-2">
389
- <span className="text-white font-semibold text-sm">{uc.label}</span>
390
- {result.urgency === 'Critical' && (
391
- <span className="bg-white/20 text-white text-xs px-2 py-0.5 rounded-full">Alert sent to authority</span>
392
- )}
393
- </div>
394
- <span className="text-white/80 text-xs">{result.department}</span>
395
- </div>
396
-
397
- <div className="p-6 space-y-5">
398
- {/* ID row */}
399
- <div className="flex items-center justify-between p-3 bg-gray-50 rounded-xl">
400
- <div>
401
- <p className="text-xs text-gray-400 mb-0.5">Your complaint ID — save this to track your complaint</p>
402
- <p className="font-mono text-sm text-gray-700 font-medium">{result.id}</p>
403
- </div>
404
- <button onClick={copyId}
405
- className="text-xs text-indigo-600 border border-indigo-200 px-3 py-1.5 rounded-lg hover:bg-indigo-50 transition-all flex items-center gap-1">
406
- {copied ? '✓ Copied' : 'Copy ID'}
407
- </button>
408
- </div>
409
-
410
- {/* Summary */}
411
- <div className={`${uc.bg} rounded-xl p-4`}>
412
- <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">AI Summary</p>
413
- <p className="text-sm text-gray-700 leading-relaxed">{result.summary}</p>
414
- </div>
415
-
416
- {/* Meta grid */}
417
- <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
418
- {[
419
- { label: 'Department', value: `${DEPT_ICONS[result.department] || '📋'} ${result.department}` },
420
- { label: 'Resolution ETA', value: result.estimated_resolution, progress: getProgress(result.estimated_resolution) },
421
- { label: 'Language', value: result.language_detected },
422
- { label: 'Location', value: result.location || 'Not specified' }
423
- ].map(item => (
424
- <div key={item.label} className="bg-gray-50 rounded-xl p-3">
425
- <p className="text-gray-400 text-xs mb-1">{item.label}</p>
426
- <p className="text-gray-800 font-medium text-sm leading-tight">{item.value}</p>
427
- {item.progress && (
428
- <div className="mt-2 w-full bg-gray-200 rounded-full h-1.5">
429
- <div
430
- className="bg-indigo-600 h-1.5 rounded-full transition-all duration-500"
431
- style={{ width: `${item.progress}%` }}
432
- ></div>
433
- </div>
434
- )}
435
- </div>
436
- ))}
437
- </div>
438
-
439
- {/* Action items */}
440
- <div>
441
- <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">Action items for authority</p>
442
- <div className="space-y-2">
443
- {result.action_items?.map((a, i) => (
444
- <div key={i} className="flex items-start gap-3 p-3 bg-indigo-50 border border-indigo-100 rounded-xl">
445
- <span className="w-5 h-5 bg-indigo-600 text-white text-xs rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 font-semibold">
446
- {i + 1}
447
- </span>
448
- <span className="text-sm text-indigo-900">{a}</span>
449
- </div>
450
- ))}
451
- </div>
452
- </div>
453
-
454
- <div className="pt-3 border-t border-gray-100 flex items-center justify-between">
455
- <p className="text-xs text-gray-400">
456
- Submitted {new Date(result.created_at).toLocaleString('en-IN')}
457
- </p>
458
- <button
459
- onClick={() => { setResult(null); setActiveTab('track'); setTrackId(result.id) }}
460
- className="text-xs text-indigo-600 hover:underline">
461
- Track this complaint →
462
- </button>
463
- </div>
464
- </div>
465
- </div>
466
- )}
467
- </div>
468
- )}
469
-
470
- {/* ══════════════════════════════════════════════════════════════════════
471
- TRACK TAB
472
- ══════════════════════════════════════════════════════════════════════ */}
473
- {activeTab === 'track' && (
474
- <div className="animate-fade-in">
475
- <div className="bg-white rounded-2xl border border-gray-200 shadow-sm overflow-hidden">
476
- <div className="px-6 pt-5 pb-4 border-b border-gray-100">
477
- <h2 className="font-semibold text-gray-800 text-base">Track your complaint</h2>
478
- <p className="text-xs text-gray-400 mt-0.5">Enter the complaint ID you received when you submitted</p>
479
- </div>
480
- <div className="p-6 space-y-4">
481
- <div className="flex gap-3">
482
- <input
483
- value={trackId}
484
- onChange={e => setTrackId(e.target.value)}
485
- placeholder="Paste your complaint ID here..."
486
- className="flex-1 border border-gray-200 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-200 bg-gray-50 focus:bg-white transition-all"
487
- />
488
- <button onClick={trackComplaint} disabled={!trackId.trim() || trackLoading}
489
- className="px-5 py-3 bg-indigo-600 text-white rounded-xl text-sm font-semibold hover:bg-indigo-700 disabled:opacity-40 transition-all flex items-center gap-2">
490
- {trackLoading ? <Spinner /> : 'Track'}
491
- </button>
492
- </div>
493
-
494
- {trackResult && (
495
- <div className="animate-slide-up">
496
- {trackResult.error ? (
497
- <div className="p-4 bg-red-50 border border-red-200 rounded-xl text-sm text-red-600">
498
- {trackResult.error}
499
- </div>
500
- ) : (
501
- <div className="space-y-4">
502
- {/* Status banner */}
503
- <div className={`p-4 rounded-xl border flex items-center justify-between ${
504
- trackResult.status === 'resolved'
505
- ? 'bg-emerald-50 border-emerald-200'
506
- : 'bg-amber-50 border-amber-200'
507
- }`}>
508
- <div className="flex items-center gap-3">
509
- <span className={`w-3 h-3 rounded-full ${trackResult.status === 'resolved' ? 'bg-emerald-500' : 'bg-amber-400 pulse-dot'}`}></span>
510
- <span className={`font-semibold text-sm ${trackResult.status === 'resolved' ? 'text-emerald-700' : 'text-amber-700'}`}>
511
- {trackResult.status === 'resolved' ? 'Complaint Resolved' : 'Under Review — Pending'}
512
- </span>
513
- </div>
514
- <span className="text-xs text-gray-400">{new Date(trackResult.created_at).toLocaleDateString('en-IN')}</span>
515
- </div>
516
-
517
- <p className="text-sm text-gray-700 leading-relaxed">{trackResult.summary}</p>
518
-
519
- <div className="grid grid-cols-2 gap-3">
520
- {[
521
- { label: 'Department', value: `${DEPT_ICONS[trackResult.department]||'📋'} ${trackResult.department}` },
522
- { label: 'Urgency', value: trackResult.urgency },
523
- { label: 'ETA', value: trackResult.estimated_resolution },
524
- { label: 'Status', value: trackResult.status === 'resolved' ? 'Resolved ✓' : 'Pending...' }
525
- ].map(item => (
526
- <div key={item.label} className="bg-gray-50 rounded-xl p-3">
527
- <p className="text-xs text-gray-400 mb-1">{item.label}</p>
528
- <p className="text-sm font-medium text-gray-800">{item.value}</p>
529
- </div>
530
- ))}
531
- </div>
532
- </div>
533
- )}
534
- </div>
535
- )}
536
- </div>
537
- </div>
538
- </div>
539
- )}
540
-
541
- {/* ── HOW IT WORKS ───────────────────────────────────────────────────── */}
542
- <div className="mt-10 grid grid-cols-1 sm:grid-cols-3 gap-4">
543
- {[
544
- { step: '01', icon: '✍️', title: 'Submit your complaint', desc: 'Type or speak in any Indian language. Our AI understands all of them.' },
545
- { step: '02', icon: '🤖', title: 'AI processes it', desc: 'Smart parser extracts key details, assigns department and urgency in under 3 seconds.' },
546
- { step: '03', icon: '📨', title: 'Authority is notified', desc: 'A structured report is routed to the right department. Critical complaints trigger instant alerts.' }
547
- ].map(item => (
548
- <div key={item.step} className="bg-white/60 border border-gray-200/80 rounded-2xl p-5 backdrop-blur-sm">
549
- <div className="flex items-start gap-3">
550
- <span className="text-xl" style={{fontSize:'20px'}}>{item.icon}</span>
551
- <div>
552
- <p className="text-xs text-indigo-400 font-bold mb-1">Step {item.step}</p>
553
- <p className="text-sm font-semibold text-gray-800 mb-1">{item.title}</p>
554
- <p className="text-xs text-gray-500 leading-relaxed">{item.desc}</p>
555
- </div>
556
- </div>
557
- </div>
558
- ))}
559
- </div>
560
-
561
- {/* Footer */}
562
- <p className="text-center text-xs text-gray-400 mt-8 pb-4">
563
- CitizenCare — Built for the National Hackathon · Powered by Smart AI parser, Supabase & Next.js
564
- </p>
565
- </div>
566
- </div>
567
- )
568
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/login.jsx DELETED
@@ -1,108 +0,0 @@
1
- import { useState } from 'react'
2
- import { supabase } from '../lib/supabase'
3
- import Link from 'next/link'
4
- import { useRouter } from 'next/router'
5
-
6
- export default function Login() {
7
- const [email, setEmail] = useState('')
8
- const [password, setPassword] = useState('')
9
- const [loading, setLoading] = useState(false)
10
- const [error, setError] = useState('')
11
- const [isSignUp, setIsSignUp] = useState(false)
12
- const router = useRouter()
13
-
14
- const handleAuth = async (e) => {
15
- e.preventDefault()
16
- setLoading(true)
17
- setError('')
18
-
19
- try {
20
- if (isSignUp) {
21
- const { error } = await supabase.auth.signUp({ email, password })
22
- if (error) throw error
23
- setError('Check your email for confirmation link')
24
- } else {
25
- const { error } = await supabase.auth.signInWithPassword({ email, password })
26
- if (error) throw error
27
- router.push('/dashboard')
28
- }
29
- } catch (err) {
30
- setError(err.message)
31
- } finally {
32
- setLoading(false)
33
- }
34
- }
35
-
36
- return (
37
- <div className="min-h-screen bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50 flex items-center justify-center p-4">
38
- <div className="max-w-md w-full bg-white rounded-2xl shadow-xl p-8">
39
- <div className="text-center mb-8">
40
- <div className="w-16 h-16 bg-indigo-600 rounded-2xl flex items-center justify-center mx-auto mb-4">
41
- <span className="text-white text-2xl font-bold">C</span>
42
- </div>
43
- <h1 className="text-2xl font-bold text-gray-900 mb-2">
44
- {isSignUp ? 'Create Account' : 'Welcome Back'}
45
- </h1>
46
- <p className="text-gray-600">
47
- {isSignUp ? 'Sign up to access the authority dashboard' : 'Sign in to your authority account'}
48
- </p>
49
- </div>
50
-
51
- <form onSubmit={handleAuth} className="space-y-6">
52
- <div>
53
- <label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
54
- <input
55
- type="email"
56
- value={email}
57
- onChange={(e) => setEmail(e.target.value)}
58
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
59
- placeholder="authority@city.gov.in"
60
- required
61
- />
62
- </div>
63
-
64
- <div>
65
- <label className="block text-sm font-medium text-gray-700 mb-2">Password</label>
66
- <input
67
- type="password"
68
- value={password}
69
- onChange={(e) => setPassword(e.target.value)}
70
- className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
71
- placeholder="Enter your password"
72
- required
73
- />
74
- </div>
75
-
76
- {error && (
77
- <div className="p-3 bg-red-50 border border-red-200 rounded-xl text-sm text-red-600">
78
- {error}
79
- </div>
80
- )}
81
-
82
- <button
83
- type="submit"
84
- disabled={loading}
85
- className="w-full bg-indigo-600 text-white py-3 rounded-xl font-semibold hover:bg-indigo-700 disabled:opacity-50 transition-all"
86
- >
87
- {loading ? 'Please wait...' : (isSignUp ? 'Create Account' : 'Sign In')}
88
- </button>
89
- </form>
90
-
91
- <div className="mt-6 text-center">
92
- <button
93
- onClick={() => setIsSignUp(!isSignUp)}
94
- className="text-indigo-600 hover:text-indigo-700 text-sm"
95
- >
96
- {isSignUp ? 'Already have an account? Sign in' : 'Need an account? Sign up'}
97
- </button>
98
- </div>
99
-
100
- <div className="mt-4 text-center">
101
- <Link href="/" className="text-gray-500 hover:text-gray-700 text-sm">
102
- ← Back to citizen portal
103
- </Link>
104
- </div>
105
- </div>
106
- </div>
107
- )
108
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/pages/support.jsx DELETED
@@ -1,101 +0,0 @@
1
- import Link from 'next/link'
2
-
3
- const FAQ_ITEMS = [
4
- {
5
- question: 'How does the AI process my complaint?',
6
- answer: 'Our AI analyzes your complaint text using keyword detection and natural language processing to automatically categorize it, determine urgency, and route it to the appropriate government department.'
7
- },
8
- {
9
- question: 'What languages are supported?',
10
- answer: 'We support 10 Indian languages: English, Hindi, Telugu, Tamil, Kannada, Marathi, Bengali, Gujarati, Punjabi, and Odia. The AI detects the language automatically.'
11
- },
12
- {
13
- question: 'How long does it take to resolve a complaint?',
14
- answer: 'Resolution time depends on urgency: Critical issues (12 hours), High priority (24-48 hours), Medium (3-5 days), Low priority (7-14 days).'
15
- },
16
- {
17
- question: 'Can I track my complaint status?',
18
- answer: 'Yes! Use the tracking feature on the main page with your complaint ID. You can also check the status in real-time.'
19
- },
20
- {
21
- question: 'What if my complaint is urgent?',
22
- answer: 'Critical complaints trigger instant alerts to authorities. Use keywords like "accident", "emergency", or "collapsed" to mark it as critical.'
23
- },
24
- {
25
- question: 'How do I know which department handles my issue?',
26
- answer: 'The AI automatically detects the relevant department based on keywords in your complaint. You can see this in the complaint summary.'
27
- },
28
- {
29
- question: 'Can I submit complaints anonymously?',
30
- answer: 'Yes, complaints are submitted anonymously. Only the complaint content and location (if provided) are stored.'
31
- },
32
- {
33
- question: 'What happens after I submit a complaint?',
34
- answer: 'Your complaint is processed instantly, categorized, and routed to the relevant department. Authorities receive structured reports with action items.'
35
- }
36
- ]
37
-
38
- export default function Support() {
39
- return (
40
- <div className="min-h-screen" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)' }}>
41
- {/* ── NAVBAR ─────────────────────────────────────────────────────────── */}
42
- <nav className="bg-white/80 backdrop-blur border-b border-gray-200/60 sticky top-0 z-30">
43
- <div className="max-w-4xl mx-auto px-4 h-14 flex items-center justify-between">
44
- <div className="flex items-center gap-2.5">
45
- <div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center">
46
- <span className="text-white text-sm font-bold">C</span>
47
- </div>
48
- <div>
49
- <span className="font-semibold text-gray-900 text-sm">CitizenCare</span>
50
- <span className="text-gray-400 text-xs ml-2 hidden sm:inline">Support Center</span>
51
- </div>
52
- </div>
53
- <div className="flex items-center gap-3">
54
- <Link href="/dashboard"
55
- className="text-xs font-medium text-indigo-600 border border-indigo-200 px-3 py-1.5 rounded-lg hover:bg-indigo-50 transition-all">
56
- Authority Dashboard →
57
- </Link>
58
- </div>
59
- </div>
60
- </nav>
61
-
62
- <div className="max-w-4xl mx-auto px-4 py-8">
63
- <div className="text-center mb-8">
64
- <h1 className="text-3xl sm:text-4xl font-bold text-white mb-3 leading-tight">
65
- Help & Support
66
- </h1>
67
- <p className="text-white/80 text-base max-w-xl mx-auto leading-relaxed">
68
- Find answers to common questions about using CitizenCare
69
- </p>
70
- </div>
71
-
72
- <div className="space-y-4">
73
- {FAQ_ITEMS.map((item, index) => (
74
- <details key={index} className="bg-white/90 backdrop-blur rounded-2xl border border-white/20 shadow-sm overflow-hidden">
75
- <summary className="px-6 py-5 font-semibold text-gray-900 cursor-pointer hover:bg-white/50 transition-all">
76
- {item.question}
77
- </summary>
78
- <div className="px-6 pb-5 text-gray-700 leading-relaxed">
79
- {item.answer}
80
- </div>
81
- </details>
82
- ))}
83
- </div>
84
-
85
- <div className="mt-8 text-center">
86
- <p className="text-white/80 mb-4">Still need help?</p>
87
- <Link href="/" className="inline-flex items-center gap-2 bg-white/20 backdrop-blur border border-white/30 text-white px-6 py-3 rounded-xl hover:bg-white/30 transition-all">
88
- <span>📞</span>
89
- Contact Support
90
- </Link>
91
- </div>
92
-
93
- <div className="mt-8 text-center">
94
- <Link href="/" className="text-white/60 hover:text-white text-sm">
95
- ← Back to Home
96
- </Link>
97
- </div>
98
- </div>
99
- </div>
100
- )
101
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/postcss.config.js DELETED
@@ -1 +0,0 @@
1
- module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } }
 
 
Downloads/hackathon/civicai/styles/globals.css DELETED
@@ -1,34 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- * { box-sizing: border-box; }
6
-
7
- body {
8
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Inter', sans-serif;
9
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
- }
11
-
12
- @keyframes spin-slow {
13
- from { transform: rotate(0deg); }
14
- to { transform: rotate(360deg); }
15
- }
16
-
17
- @keyframes pulse-dot {
18
- 0%, 100% { opacity: 1; transform: scale(1); }
19
- 50% { opacity: 0.5; transform: scale(0.8); }
20
- }
21
-
22
- @keyframes slide-up {
23
- from { opacity: 0; transform: translateY(16px); }
24
- to { opacity: 1; transform: translateY(0); }
25
- }
26
-
27
- @keyframes fade-in {
28
- from { opacity: 0; }
29
- to { opacity: 1; }
30
- }
31
-
32
- .animate-slide-up { animation: slide-up 0.35s ease forwards; }
33
- .animate-fade-in { animation: fade-in 0.25s ease forwards; }
34
- .pulse-dot { animation: pulse-dot 1.2s ease-in-out infinite; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Downloads/hackathon/civicai/tailwind.config.js DELETED
@@ -1,35 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- module.exports = {
3
- content: ['./pages/**/*.{js,jsx}', './components/**/*.{js,jsx}'],
4
- theme: {
5
- extend: {
6
- colors: {
7
- primary: {
8
- 50: '#eff6ff',
9
- 100: '#dbeafe',
10
- 200: '#bfdbfe',
11
- 300: '#93c5fd',
12
- 400: '#60a5fa',
13
- 500: '#3b82f6',
14
- 600: '#2563eb',
15
- 700: '#1d4ed8',
16
- 800: '#1e40af',
17
- 900: '#1e3a8a',
18
- },
19
- secondary: {
20
- 50: '#f8fafc',
21
- 100: '#f1f5f9',
22
- 200: '#e2e8f0',
23
- 300: '#cbd5e1',
24
- 400: '#94a3b8',
25
- 500: '#64748b',
26
- 600: '#475569',
27
- 700: '#334155',
28
- 800: '#1e293b',
29
- 900: '#0f172a',
30
- }
31
- }
32
- }
33
- },
34
- plugins: []
35
- }