diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e98eaa7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+# Dependencies
+node_modules/
+
+# Environment variables
+.env
+.env.local
+.env.*.local
+
+# Build outputs
+dist/
+build/
+*.log
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Editor directories
+.vscode/
+.idea/
+
+# Backup files
+*.backup
+*.bak
+*.old
+
+# Lock files (optional - include if using different package managers)
+# package-lock.json
+# yarn.lock
+# bun.lockb
diff --git a/README.md b/README.md
index 1a83e63..5773a67 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,135 @@
-# Idea-Feedback-Form
+# Job Creation Form with Excel Sync
+**Unified system** that combines job creation form with immediate Excel synchronization.
+
+## Architecture
+
+This system merges:
+- **JobSubmissionFlow**: User form with auto-incrementing Job_Number
+- **SyncToExcelFromPb**: Microsoft Graph API Excel sync logic
+
+### Flow
+
+1. User submits job form
+2. Server authenticates with PocketBase (agent credentials)
+3. Auto-calculates `Job_Number = max(existing) + 1`
+4. Creates record in PocketBase `Job_Info_Prod`
+5. Captures `record.id` from creation response
+6. Acquires Microsoft Graph API token
+7. Gets Excel table columns
+8. Maps PocketBase fields to Excel columns (case-insensitive, only existing columns)
+9. Adds `pb_id = record.id` to Excel row
+10. POSTs new row to Excel `Test_Table`
+
+## Configuration
+
+All credentials are in `.env` (copied from existing systems):
+
+```bash
+# Azure AD
+CLIENT_ID=3c846e71-9609-40e1-b458-0eb805e21b9f
+CLIENT_SECRET=7aD8Q~d5K~_PzQv6KqDdrEnmyXHE60eVDpbcnaK_
+TENANT_ID=3fd97ea7-b124-41f1-855f-52d8ac3b16c7
+
+# PocketBase Agent
+PB_DB=https://pocketbase.ccllc.pro
+PB_AGENT_EMAIL=pbserviceupdate@cardoza.construction
+PB_AGENT_PASSWORD=gJFjYx5y0_BSMgX
+PB_COLLECTION=Job_Info_Prod
+PB_AUTH_COLLECTION=Users
+
+# Excel
+DRIVE_ID=b!9YOqqr2xM0G2DFBwMEJYY4UzQgocFddEtpLYWuS9_AtkCKl2q8yjQJteQ7Ti4QSx
+ITEM_ID=01SPNXLDQRICHB63BFUNGKZ3GE6RL7LGBT
+EXCEL_TABLE=/drives/.../tables/Test_Table
+
+# Server
+PORT=4000
+```
+
+## Installation
+
+**Use npm install** (not bun install) to avoid Windows/OneDrive symlink issues:
+
+```bash
+npm install
+```
+
+## Usage
+
+Start the server:
+
+```bash
+bun run dev
+```
+
+Server runs at: http://localhost:4000
+
+## Key Features
+
+### Auto-Increment Job Numbers
+- Queries all existing `Job_Number` values
+- Calculates `max + 1` for new job
+- No manual input required
+
+### Excel Column Mapping
+- **Case-insensitive**: Matches `Job_Number` to `job_number`, `Company_Client` to `company_client`, etc.
+- **Only existing columns**: Ignores PocketBase fields not in Excel table
+- **Special mapping**: `record.id` → Excel column `pb_id`
+
+### Error Handling
+- If Excel sync fails, PocketBase record is still created
+- Returns warning with partial success
+- Logs Excel error details
+
+## Endpoints
+
+- `POST /api/submit` - Submit job form (creates in PB + Excel)
+- `GET /api/health` - Health check
+
+## Technical Details
+
+### Dependencies
+- **Hono**: Web framework
+- **PocketBase SDK**: Database client
+- **@microsoft/microsoft-graph-client**: Excel API
+- **dotenv**: Environment config
+
+### Authentication
+- **PocketBase**: Service account (`pbserviceupdate@cardoza.construction`)
+- **Microsoft Graph**: OAuth2 client credentials flow
+
+### Excel Operations
+- Table path: `EXCEL_TABLE` from .env
+- Operation: `POST /rows` with `{ values: [[...]] }`
+- Row format: 2D array matching column order
+
+## Differences from Other Systems
+
+### vs. SyncToExcelFromPb
+- **No batch sync**: Creates one record at a time
+- **No duplicate detection**: Always adds new row
+- **No change comparison**: Simple create operation
+- **No collision warnings**: Assumes Job_Number is unique
+
+### vs. JobSubmissionFlow
+- **Excel sync included**: Adds to Excel immediately after PB creation
+- **pb_id captured**: Stores PocketBase record.id in Excel
+- **Graph token acquisition**: Handles Microsoft authentication
+- **Column-aware**: Only maps fields that exist in Excel table
+
+## Troubleshooting
+
+### Bun Package Resolution Error
+**Symptom**: `Cannot find package 'hono'`
+**Solution**: Use `npm install` instead of `bun install`
+
+### Excel Sync Fails
+- Check `EXCEL_TABLE` path in .env
+- Verify Azure app has Files.ReadWrite.All permission
+- Check Excel table name matches exactly (case-sensitive in Graph API)
+
+### PocketBase Auth Fails
+- Verify `PB_AGENT_EMAIL` and `PB_AGENT_PASSWORD` are correct
+- Check agent account exists in `Users` collection
+- Ensure `PB_DB` URL is accessible
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..523abde
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,237 @@
+
+
+
+
+
+ Idea & Feedback Form
+
+
+
+
+
+
+
+
Cardoza Construction
+
Idea & Feedback Form
+
+
+
+
Sign in with your Microsoft account to access the Idea & Feedback Form
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..f08a06c
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,384 @@
+{
+ "name": "job-creation-with-excel-sync",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "job-creation-with-excel-sync",
+ "version": "1.0.0",
+ "dependencies": {
+ "@microsoft/microsoft-graph-client": "^3.0.7",
+ "dotenv": "^17.2.3",
+ "hono": "^4.10.8",
+ "pocketbase": "^0.26.5"
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "@types/node": "latest",
+ "autoprefixer": "^10.4.22",
+ "postcss": "^8.5.6"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@microsoft/microsoft-graph-client": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz",
+ "integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "tslib": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@azure/identity": {
+ "optional": true
+ },
+ "@azure/msal-browser": {
+ "optional": true
+ },
+ "buffer": {
+ "optional": true
+ },
+ "stream-browserify": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@types/bun": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.4.tgz",
+ "integrity": "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bun-types": "1.3.4"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.0.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz",
+ "integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.22",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz",
+ "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.27.0",
+ "caniuse-lite": "^1.0.30001754",
+ "fraction.js": "^5.3.4",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.7",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz",
+ "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bun-types": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.4.tgz",
+ "integrity": "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001760",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
+ "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.10.8",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.8.tgz",
+ "integrity": "sha512-DDT0A0r6wzhe8zCGoYOmMeuGu3dyTAE40HHjwUsWFTEy5WxK1x2WDSsBPlEXgPbRIFY6miDualuUDbasPogIww==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/pocketbase": {
+ "version": "0.26.5",
+ "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.5.tgz",
+ "integrity": "sha512-SXcq+sRvVpNxfLxPB1C+8eRatL7ZY4o3EVl/0OdE3MeR9fhPyZt0nmmxLqYmkLvXCN9qp3lXWV/0EUYb3MmMXQ==",
+ "license": "MIT"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+ "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8adb75f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "idea-feedback-form",
+ "version": "1.0.0",
+ "description": "Idea and Feedback Form with PocketBase",
+ "type": "module",
+ "scripts": {
+ "dev": "bun run server.ts",
+ "start": "bun run server.ts"
+ },
+ "dependencies": {
+ "@microsoft/microsoft-graph-client": "^3.0.7",
+ "dotenv": "^17.2.3",
+ "hono": "^4.10.8",
+ "pocketbase": "^0.26.5"
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "@types/node": "latest"
+ }
+}
diff --git a/server.ts b/server.ts
new file mode 100644
index 0000000..474f510
--- /dev/null
+++ b/server.ts
@@ -0,0 +1,77 @@
+import { Hono } from 'hono';
+import { serveStatic } from 'hono/bun';
+import { cors } from 'hono/cors';
+import PocketBase from 'pocketbase';
+import { config } from 'dotenv';
+
+config();
+
+const app = new Hono();
+
+// Enable CORS
+app.use('/*', cors());
+
+// Serve static files from frontend directory
+app.use('/*', serveStatic({ root: './frontend' }));
+
+// PocketBase client
+const pb = new PocketBase(process.env.PB_DB || 'https://pocketbase.ccllc.pro');
+
+// Use user's PocketBase token (passed from authenticated frontend)
+function setUserPocketBaseAuth(token: string) {
+ pb.authStore.save(token);
+}
+// API endpoint to submit feedback
+app.post('/api/submit', async (c) => {
+ try {
+ const data = await c.req.json();
+ const userToken = data.pbToken;
+
+ if (!userToken) {
+ return c.json({
+ success: false,
+ message: 'User authentication required. Please log in first.'
+ }, 401);
+ }
+
+ // Use user's PocketBase token
+ setUserPocketBaseAuth(userToken);
+
+ // Remove pbToken from data before creating record
+ delete data.pbToken;
+
+ // Create the record in PocketBase as the authenticated user
+ const record = await pb.collection(process.env.PB_COLLECTION!).create(data);
+ console.log(`✓ Created PocketBase record with ID: ${record.id}`);
+
+ return c.json({
+ success: true,
+ message: 'Thank you! Your feedback has been submitted successfully.',
+ recordId: record.id
+ });
+ } catch (error: any) {
+ console.error('✗ Error submitting feedback:', error);
+ return c.json({
+ success: false,
+ message: error.message || 'Failed to submit feedback'
+ }, 400);
+ }
+});
+
+// Health check endpoint
+app.get('/api/health', (c) => {
+ return c.json({
+ status: 'ok',
+ service: 'idea-feedback-form',
+ timestamp: new Date().toISOString()
+ });
+});
+
+const PORT = Number(process.env.PORT || 7000);
+
+console.log(`Idea & Feedback Form server running at http://localhost:${PORT}`);
+
+export default {
+ port: PORT,
+ fetch: app.fetch,
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..6f0efe0
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "types": ["bun-types", "node", "bun"],
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "composite": true,
+ "strict": true,
+ "downlevelIteration": true,
+ "skipLibCheck": true,
+ "jsx": "react-jsx",
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "allowJs": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true
+ }
+}