From a53897276eb0cf7db36a5772c0bca03d64ff93d2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:35:00 +0000 Subject: [PATCH 001/381] chore: update env example with Mailgun configuration Co-Authored-By: G --- .env.example | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/.env.example b/.env.example index c44afbf87..b1c3550de 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,5 @@ -# MongoDB connection string -MONGODB_URI=mongodb://localhost:27017/botsmann - -# API key for authentication -API_KEY=your-secure-api-key-here -NEXT_PUBLIC_API_KEY=your-secure-api-key-here - -# Email Configuration -SENDGRID_API_KEY=your_sendgrid_api_key -SENDGRID_WELCOME_TEMPLATE_ID=your_template_id -EMAIL_FROM=noreply@botsmann.com -ADMIN_EMAIL=admin@botsmann.com -DASHBOARD_URL=https://app.botsmann.com/dashboard +# Mailgun Configuration +MAILGUN_API_KEY=your_mailgun_api_key_here +MAILGUN_DOMAIN=botsmann.com +MAILGUN_FROM_EMAIL=noreply@botsmann.com +ADMIN_EMAIL=butaeff@gmail.com From 08c674a27104198c01b450bc547c4003d6fe28f3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:35:21 +0000 Subject: [PATCH 002/381] feat: add Mailgun email service implementation Co-Authored-By: G --- package-lock.json | 28 ++++++++++++++++++++++ package.json | 2 ++ src/lib/email/service.ts | 52 ++++++++++++++++++++++++---------------- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bc4f30a4..530617591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,9 @@ "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.16", "autoprefixer": "^10.4.17", + "form-data": "^4.0.1", "lru-cache": "^11.0.2", + "mailgun.js": "^11.1.0", "mongodb": "^6.13.0", "mongoose": "^8.10.0", "next": "^14.0.4", @@ -2355,6 +2357,12 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5408,6 +5416,20 @@ "lz-string": "bin/bin.js" } }, + "node_modules/mailgun.js": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-11.1.0.tgz", + "integrity": "sha512-pXYcQT3nU32gMjUjZpl2FdQN4Vv2iobqYiXqyyevk0vXTKQj8Or0ifLXLNAGqMHnymTjV0OphBpurkchvHsRAg==", + "license": "MIT", + "dependencies": { + "axios": "^1.7.4", + "base-64": "^1.0.0", + "url-join": "^4.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8324,6 +8346,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "license": "MIT" + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", diff --git a/package.json b/package.json index 8e2f68d58..08b5f34f9 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.16", "autoprefixer": "^10.4.17", + "form-data": "^4.0.1", "lru-cache": "^11.0.2", + "mailgun.js": "^11.1.0", "mongodb": "^6.13.0", "mongoose": "^8.10.0", "next": "^14.0.4", diff --git a/src/lib/email/service.ts b/src/lib/email/service.ts index dcbfb1f3d..162094c1d 100644 --- a/src/lib/email/service.ts +++ b/src/lib/email/service.ts @@ -1,47 +1,59 @@ -import sgMail from '@sendgrid/mail'; -import { Customer } from '../schemas/customer'; +import formData from 'form-data'; +import Mailgun from 'mailgun.js'; +import Client from 'mailgun.js/dist/lib/client'; export class EmailService { + private client: Client; + private domain: string; + private fromEmail: string; + constructor() { - sgMail.setApiKey(process.env.SENDGRID_API_KEY!); + const mailgun = new Mailgun(formData); + this.client = mailgun.client({ + username: 'api', + key: process.env.MAILGUN_API_KEY || '', + url: 'https://api.eu.mailgun.net' // EU endpoint for Swiss compliance + }); + this.domain = process.env.MAILGUN_DOMAIN || ''; + this.fromEmail = process.env.MAILGUN_FROM_EMAIL || ''; } - async sendWelcomeEmail(customer: Customer): Promise { + async sendWelcomeEmail(customer: any) { try { - await sgMail.send({ + await this.client.messages.create(this.domain, { + from: this.fromEmail, to: customer.email, - from: process.env.EMAIL_FROM!, - templateId: process.env.SENDGRID_WELCOME_TEMPLATE_ID!, - dynamicTemplateData: { + subject: 'Welcome to Botsmann!', + template: 'welcome-email', + 'h:X-Mailgun-Variables': JSON.stringify({ name: customer.name, - preferences: customer.preferences, - dashboardUrl: process.env.DASHBOARD_URL, - }, + preferences: customer.preferences || {}, + dashboardUrl: process.env.DASHBOARD_URL + }) }); } catch (error) { console.error('Failed to send welcome email:', error); - throw new Error('Failed to send welcome email'); } } - async sendAdminNotification(customer: Customer): Promise { + async sendAdminNotification(customer: any) { try { - await sgMail.send({ - to: process.env.ADMIN_EMAIL!, - from: process.env.EMAIL_FROM!, + await this.client.messages.create(this.domain, { + from: this.fromEmail, + to: process.env.ADMIN_EMAIL || '', subject: 'New Customer Registration', text: ` New customer registration: Name: ${customer.name} Email: ${customer.email} Message: ${customer.message} -Preferences: ${JSON.stringify(customer.preferences)} -Metadata: ${JSON.stringify(customer.metadata)} - `.trim(), +Preferences: +- Newsletter: ${customer.preferences?.newsletter ? 'Yes' : 'No'} +- Product Updates: ${customer.preferences?.productUpdates ? 'Yes' : 'No'} +` }); } catch (error) { console.error('Failed to send admin notification:', error); - // Don't throw error for admin notifications } } } From 43b013a60225ea30cddb33f5c5162a14a7434715 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:07:16 +0000 Subject: [PATCH 003/381] chore: update env example with Mailgun template ID Co-Authored-By: G --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index b1c3550de..a39fc3a53 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ # Mailgun Configuration -MAILGUN_API_KEY=your_mailgun_api_key_here +MAILGUN_API_KEY=4c22ccf4174df5daf69cc9b31be960c3-1654a412-cc8e8ac4 MAILGUN_DOMAIN=botsmann.com MAILGUN_FROM_EMAIL=noreply@botsmann.com +MAILGUN_TEMPLATE_ID=welcome-email ADMIN_EMAIL=butaeff@gmail.com From 3d4604c96c94d4c49b23e1c32c912f3c8b16aab0 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:18:29 +0000 Subject: [PATCH 004/381] chore: update env example with placeholder values Co-Authored-By: G --- .env.example | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index a39fc3a53..d64af425e 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # Mailgun Configuration -MAILGUN_API_KEY=4c22ccf4174df5daf69cc9b31be960c3-1654a412-cc8e8ac4 -MAILGUN_DOMAIN=botsmann.com -MAILGUN_FROM_EMAIL=noreply@botsmann.com -MAILGUN_TEMPLATE_ID=welcome-email -ADMIN_EMAIL=butaeff@gmail.com +MAILGUN_API_KEY=your_mailgun_api_key_here +MAILGUN_DOMAIN=your_domain_here +MAILGUN_FROM_EMAIL=noreply@your_domain +MAILGUN_TEMPLATE_ID=your_template_id +ADMIN_EMAIL=admin@your_domain From d5edb0b23404b245338cd489ead835c9c8c710a6 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:18:46 +0000 Subject: [PATCH 005/381] feat: add Netlify function for consultation form submissions Co-Authored-By: G --- netlify/functions/api/consultations.ts | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 netlify/functions/api/consultations.ts diff --git a/netlify/functions/api/consultations.ts b/netlify/functions/api/consultations.ts new file mode 100644 index 000000000..f1ee5d968 --- /dev/null +++ b/netlify/functions/api/consultations.ts @@ -0,0 +1,52 @@ +import { Handler } from '@netlify/functions'; +import { connectDB } from '../../../src/lib/mongodb'; +import { Consultation } from '../../../src/lib/models/consultation'; +import { CustomerSchema } from '../../../src/lib/schemas/customer'; +import { EmailService } from '../../../src/lib/email/service'; +import { validateApiKey } from '../../../src/lib/middleware/auth'; +import { monitorRequest } from '../../../src/lib/middleware/monitoring'; + +const emailService = new EmailService(); + +export const handler: Handler = async (event, context) => { + if (event.httpMethod !== 'POST') { + return { + statusCode: 405, + body: JSON.stringify({ error: 'Method not allowed' }), + }; + } + + try { + const body = JSON.parse(event.body || '{}'); + const validatedData = CustomerSchema.parse(body); + + if (process.env.NODE_ENV !== 'test') { + await connectDB(); + } + + const consultation = await Consultation.create(validatedData); + + // Send emails asynchronously + try { + await Promise.all([ + emailService.sendWelcomeEmail(validatedData), + emailService.sendAdminNotification(validatedData), + ]); + } catch (emailError) { + console.error('Failed to send emails:', emailError); + } + + return { + statusCode: 200, + body: JSON.stringify({ success: true, id: consultation._id }), + }; + } catch (error: any) { + console.error('Consultation submission error:', error); + return { + statusCode: error.code === 'VALIDATION_ERROR' ? 400 : 500, + body: JSON.stringify({ + error: error.message || 'Failed to submit consultation' + }), + }; + } +}; From 83ca51f9af11f58b986289257f29b4ad3bb1f115 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:22:57 +0000 Subject: [PATCH 006/381] fix: update netlify configuration for API routing Co-Authored-By: G --- netlify.toml | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/netlify.toml b/netlify.toml index 8c06f20cd..ca8556978 100644 --- a/netlify.toml +++ b/netlify.toml @@ -8,23 +8,6 @@ NEXT_USE_NETLIFY = "true" NEXT_STATIC_EXPORT = "true" -[build.processing] - skip_processing = false - -[build.processing.html] - pretty_urls = true - -[build.processing.css] - bundle = true - minify = true - -[build.processing.js] - bundle = true - minify = true - -[build.processing.images] - compress = true - [functions] node_bundler = "esbuild" included_files = ["app/api/**"] @@ -37,16 +20,11 @@ force = true [[redirects]] - from = "/" + from = "/*" to = "/index.html" status = 200 force = true -[[redirects]] - from = "/*" - to = "/404.html" - status = 404 - [[headers]] for = "/api/*" [headers.values] @@ -65,8 +43,3 @@ X-XSS-Protection = "1; mode=block" X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin" - -[[headers]] - for = "/_next/*" - [headers.values] - Cache-Control = "public, max-age=31536000, immutable" From c2edebeef105f2c939ee07de14a8bae1d9f5dc91 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:21:51 +0000 Subject: [PATCH 007/381] chore: add AWS SES SDK dependency Co-Authored-By: G --- package-lock.json | 1534 ++++++++++++++++++++++++++++++++++++++++----- package.json | 1 + 2 files changed, 1384 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index 530617591..3df4db751 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "botsmann", "version": "1.0.0", "dependencies": { + "@aws-sdk/client-ses": "^3.744.0", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@next/mdx": "^15.1.6", @@ -76,6 +77,611 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-ses": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.744.0.tgz", + "integrity": "sha512-nMshH8HrZddO+PBPIxocmRoM7rNc71iQtuwISwtTXAX7Fe4v3REv9DBWK46bgtNyBLSUbe3lvaPM8CCAPYOtgQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/credential-provider-node": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.744.0.tgz", + "integrity": "sha512-mzJxPQ9mcnNY50pi7+pxB34/Dt7PUn0OgkashHdJPTnavoriLWvPcaQCG1NEVAtyzxNdowhpi4KjC+aN1EwAeA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.744.0.tgz", + "integrity": "sha512-R0XLfDDq7MAXYyDf7tPb+m0R7gmzTRRDtPNQ5jvuq8dbkefph5gFMkxZ2zSx7dfTsfYHhBPuTBsQ0c5Xjal3Vg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.744.0.tgz", + "integrity": "sha512-hyjC7xqzAeERorYYjhQG1ivcr1XlxgfBpa+r4pG29toFG60mACyVzaR7+og3kgzjRFAB7D1imMxPQyEvQ1QokA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.744.0.tgz", + "integrity": "sha512-k+P1Tl5ewBvVByR6hB726qFIzANgQVf2cY87hZ/e09pQYlH4bfBcyY16VJhkqYnKmv6HMdWxKHX7D8nwlc8Obg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.744.0.tgz", + "integrity": "sha512-hjEWgkF86tkvg8PIsDiB3KkTj7z8ZFGR0v0OLQYD47o17q1qfoMzZmg9wae3wXp9KzU+lZETo+8oMqX9a+7aVQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/credential-provider-env": "3.744.0", + "@aws-sdk/credential-provider-http": "3.744.0", + "@aws-sdk/credential-provider-process": "3.744.0", + "@aws-sdk/credential-provider-sso": "3.744.0", + "@aws-sdk/credential-provider-web-identity": "3.744.0", + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.744.0.tgz", + "integrity": "sha512-4oUfRd6pe/VGmKoav17pPoOO0WP0L6YXmHqtJHSDmFUOAa+Vh0ZRljTj/yBdleRgdO6rOfdWqoGLFSFiAZDrsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.744.0", + "@aws-sdk/credential-provider-http": "3.744.0", + "@aws-sdk/credential-provider-ini": "3.744.0", + "@aws-sdk/credential-provider-process": "3.744.0", + "@aws-sdk/credential-provider-sso": "3.744.0", + "@aws-sdk/credential-provider-web-identity": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.744.0.tgz", + "integrity": "sha512-m0d/pDBIaiEAAxWXt/c79RHsKkUkyPOvF2SAMRddVhhOt1GFZI4ml+3f4drmAZfXldIyJmvJTJJqWluVPwTIqQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.744.0.tgz", + "integrity": "sha512-xdMufTZOvpbDoDPI2XLu0/Rg3qJ/txpS8IJR63NsCGotHJZ/ucLNKwTcGS40hllZB8qSHTlvmlOzElDahTtx/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.744.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/token-providers": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.744.0.tgz", + "integrity": "sha512-cNk93GZxORzqEojWfXdrPBF6a7Nu3LpPCWG5mV+lH2tbuGsmw6XhKkwpt7o+OiIP4tKCpHlvqOD8f1nmhe1KDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.744.0.tgz", + "integrity": "sha512-ROUbDQHfVWiBHXd4m9E9mKj1Azby8XCs8RC8OCf9GVH339GSE6aMrPJSzMlsV1LmzPdPIypgp5qqh5NfSrKztg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.744.0.tgz", + "integrity": "sha512-Mnrlh4lRY1gZQnKvN2Lh/5WXcGkzC41NM93mtn2uaqOh+DZLCXCttNCfbUesUvYJLOo3lYaOpiDsjTkPVB1yjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.744.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.744.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.2", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-retry": "^4.0.4", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.4", + "@smithy/util-defaults-mode-node": "^4.0.4", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", + "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.744.0.tgz", + "integrity": "sha512-v/1+lWkDCd60Ei6oyhJqli6mTsPEVepLoSMB50vHUVlJP0fzXu/3FMje90/RzeUoh/VugZQJCEv/NNpuC6wztg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", + "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.743.0.tgz", + "integrity": "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", + "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", + "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.744.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.744.0.tgz", + "integrity": "sha512-BJURjwIXhNa4heXkLC0+GcL+8wVXaU7JoyW6ckdvp93LL+sVHeR1d5FxXZHQW/pMI4E3gNlKyBqjKaT75tObNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.744.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1285,213 +1891,792 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", + "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", + "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", + "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", + "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", + "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", + "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sendgrid/client": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.4.tgz", + "integrity": "sha512-VxZoQ82MpxmjSXLR3ZAE2OWxvQIW2k2G24UeRPr/SYX8HqWLV/8UBN15T2WmjjnEb5XSmFImTJOKDzzSeKr9YQ==", + "license": "MIT", + "dependencies": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.7.4" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.4.tgz", + "integrity": "sha512-MUpIZykD9ARie8LElYCqbcBhGGMaA/E6I7fEcG7Hc2An26QJyLtwOaKQ3taGp8xO8BICPJrSKuYV4bDeAJKFGQ==", + "license": "MIT", + "dependencies": { + "@sendgrid/client": "^8.1.4", + "@sendgrid/helpers": "^8.0.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", + "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", + "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.2.tgz", + "integrity": "sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", + "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", + "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", + "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", + "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", + "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.3.tgz", + "integrity": "sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.4.tgz", + "integrity": "sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", + "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", + "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", + "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz", + "integrity": "sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", + "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", + "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", + "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", + "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", + "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", + "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", + "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.3.tgz", + "integrity": "sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", + "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@smithy/url-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", + "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.4.tgz", + "integrity": "sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw==", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.4.tgz", + "integrity": "sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", + "node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", + "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=18.0.0" } }, - "node_modules/@sendgrid/client": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.4.tgz", - "integrity": "sha512-VxZoQ82MpxmjSXLR3ZAE2OWxvQIW2k2G24UeRPr/SYX8HqWLV/8UBN15T2WmjjnEb5XSmFImTJOKDzzSeKr9YQ==", - "license": "MIT", + "node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", + "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "license": "Apache-2.0", "dependencies": { - "@sendgrid/helpers": "^8.0.0", - "axios": "^1.7.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.*" + "node": ">=18.0.0" } }, - "node_modules/@sendgrid/helpers": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", - "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", - "license": "MIT", + "node_modules/@smithy/util-retry": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", + "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "license": "Apache-2.0", "dependencies": { - "deepmerge": "^4.2.2" + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@sendgrid/mail": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.4.tgz", - "integrity": "sha512-MUpIZykD9ARie8LElYCqbcBhGGMaA/E6I7fEcG7Hc2An26QJyLtwOaKQ3taGp8xO8BICPJrSKuYV4bDeAJKFGQ==", - "license": "MIT", + "node_modules/@smithy/util-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz", + "integrity": "sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==", + "license": "Apache-2.0", "dependencies": { - "@sendgrid/client": "^8.1.4", - "@sendgrid/helpers": "^8.0.0" + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.*" + "node": ">=18.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-waiter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", + "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@smithy/abort-controller": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@swc/helpers": { @@ -2375,6 +3560,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3441,6 +4632,28 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -7797,6 +9010,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/style-to-object": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", @@ -8369,6 +9588,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", diff --git a/package.json b/package.json index 08b5f34f9..c35fc9f2c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "test:watch": "jest --watch" }, "dependencies": { + "@aws-sdk/client-ses": "^3.744.0", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@next/mdx": "^15.1.6", From 8458a55d4e31f703bafe7f86fae482605319448d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:02:15 +0000 Subject: [PATCH 008/381] feat: update email service to use AWS SES Co-Authored-By: G --- src/lib/email/service.ts | 91 +++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/src/lib/email/service.ts b/src/lib/email/service.ts index 162094c1d..e1ca82daa 100644 --- a/src/lib/email/service.ts +++ b/src/lib/email/service.ts @@ -1,59 +1,72 @@ -import formData from 'form-data'; -import Mailgun from 'mailgun.js'; -import Client from 'mailgun.js/dist/lib/client'; +import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses'; +import type { Customer } from '@/src/lib/schemas/customer'; export class EmailService { - private client: Client; - private domain: string; + private ses: SESClient; private fromEmail: string; + private adminEmail: string; constructor() { - const mailgun = new Mailgun(formData); - this.client = mailgun.client({ - username: 'api', - key: process.env.MAILGUN_API_KEY || '', - url: 'https://api.eu.mailgun.net' // EU endpoint for Swiss compliance + this.ses = new SESClient({ + region: 'eu-central-1', // Frankfurt region for Swiss compliance + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '' + } }); - this.domain = process.env.MAILGUN_DOMAIN || ''; - this.fromEmail = process.env.MAILGUN_FROM_EMAIL || ''; + this.fromEmail = process.env.FROM_EMAIL || 'noreply@botsmann.com'; + this.adminEmail = process.env.ADMIN_EMAIL || 'butaeff@gmail.com'; } - async sendWelcomeEmail(customer: any) { + async sendWelcomeEmail(customer: Customer): Promise { + const params = { + Source: this.fromEmail, + Destination: { + ToAddresses: [customer.email] + }, + Message: { + Subject: { + Data: 'Welcome to Botsmann!' + }, + Body: { + Text: { + Data: `Hello ${customer.name},\n\nThank you for your interest in Botsmann! We've received your message and will get back to you soon.\n\nBest regards,\nThe Botsmann Team` + } + } + } + }; + try { - await this.client.messages.create(this.domain, { - from: this.fromEmail, - to: customer.email, - subject: 'Welcome to Botsmann!', - template: 'welcome-email', - 'h:X-Mailgun-Variables': JSON.stringify({ - name: customer.name, - preferences: customer.preferences || {}, - dashboardUrl: process.env.DASHBOARD_URL - }) - }); + await this.ses.send(new SendEmailCommand(params)); } catch (error) { console.error('Failed to send welcome email:', error); + throw error; } } - async sendAdminNotification(customer: any) { + async sendAdminNotification(customer: Customer): Promise { + const params = { + Source: this.fromEmail, + Destination: { + ToAddresses: [this.adminEmail] + }, + Message: { + Subject: { + Data: 'New Customer Registration' + }, + Body: { + Text: { + Data: `New customer registration:\n\nName: ${customer.name}\nEmail: ${customer.email}\nMessage: ${customer.message}\n\nPreferences:\n- Newsletter: ${customer.preferences.newsletter ? 'Yes' : 'No'}\n- Product Updates: ${customer.preferences.productUpdates ? 'Yes' : 'No'}` + } + } + } + }; + try { - await this.client.messages.create(this.domain, { - from: this.fromEmail, - to: process.env.ADMIN_EMAIL || '', - subject: 'New Customer Registration', - text: ` -New customer registration: -Name: ${customer.name} -Email: ${customer.email} -Message: ${customer.message} -Preferences: -- Newsletter: ${customer.preferences?.newsletter ? 'Yes' : 'No'} -- Product Updates: ${customer.preferences?.productUpdates ? 'Yes' : 'No'} -` - }); + await this.ses.send(new SendEmailCommand(params)); } catch (error) { console.error('Failed to send admin notification:', error); + throw error; } } } From 942e11d2a4abb9ca71a7354cb5ce69742ff82592 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:23:33 +0000 Subject: [PATCH 009/381] fix: update AWS SES environment variable names Co-Authored-By: G --- src/lib/email/service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/email/service.ts b/src/lib/email/service.ts index e1ca82daa..bf1eda9f5 100644 --- a/src/lib/email/service.ts +++ b/src/lib/email/service.ts @@ -8,10 +8,10 @@ export class EmailService { constructor() { this.ses = new SESClient({ - region: 'eu-central-1', // Frankfurt region for Swiss compliance + region: process.env.NEXT_AWS_REGION || 'eu-central-1', // Frankfurt region for Swiss compliance credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '' + accessKeyId: process.env.NEXT_AWS_ACCESS_KEY_ID || '', + secretAccessKey: process.env.NEXT_AWS_SECRET_ACCESS_KEY || '' } }); this.fromEmail = process.env.FROM_EMAIL || 'noreply@botsmann.com'; From 1b1d499dc6f96a40c340c6d5a9ec357beaca6470 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:40:40 +0000 Subject: [PATCH 010/381] chore: update env example with generic placeholders Co-Authored-By: G --- .env.example | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index d64af425e..c14a76591 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,15 @@ -# Mailgun Configuration -MAILGUN_API_KEY=your_mailgun_api_key_here -MAILGUN_DOMAIN=your_domain_here -MAILGUN_FROM_EMAIL=noreply@your_domain -MAILGUN_TEMPLATE_ID=your_template_id -ADMIN_EMAIL=admin@your_domain +# MongoDB connection string +MONGODB_URI=mongodb://localhost:27017/botsmann + +# API key for authentication +API_KEY=your-secure-api-key-here +NEXT_PUBLIC_API_KEY=your-secure-api-key-here + +# AWS SES Configuration +NEXT_AWS_ACCESS_KEY_ID=your-aws-access-key-id +NEXT_AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key +NEXT_AWS_REGION=your-aws-region + +# Email Configuration +FROM_EMAIL=your-verified-sender@example.com +ADMIN_EMAIL=your-admin@example.com From 5ffbb527c1408397739e496b0144900f6123c36e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:37:30 +0000 Subject: [PATCH 011/381] fix: update netlify configuration with processing settings Co-Authored-By: G --- netlify.toml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index ca8556978..19a15257f 100644 --- a/netlify.toml +++ b/netlify.toml @@ -8,6 +8,23 @@ NEXT_USE_NETLIFY = "true" NEXT_STATIC_EXPORT = "true" +[build.processing] + skip_processing = false + +[build.processing.html] + pretty_urls = true + +[build.processing.css] + bundle = true + minify = true + +[build.processing.js] + bundle = true + minify = true + +[build.processing.images] + compress = true + [functions] node_bundler = "esbuild" included_files = ["app/api/**"] @@ -20,11 +37,16 @@ force = true [[redirects]] - from = "/*" + from = "/" to = "/index.html" status = 200 force = true +[[redirects]] + from = "/*" + to = "/404.html" + status = 404 + [[headers]] for = "/api/*" [headers.values] From 315c21b269173368b008fcdb550d327fff0ce9fc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:49:49 +0000 Subject: [PATCH 012/381] chore: remove Vercel configuration Co-Authored-By: G --- vercel.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 vercel.json diff --git a/vercel.json b/vercel.json deleted file mode 100644 index 72570550f..000000000 --- a/vercel.json +++ /dev/null @@ -1,12 +0,0 @@ - -{ - "version": 2, - "builds": [ - { "src": "start-server.js", "use": "@vercel/node" }, - { "src": "public/**/*", "use": "@vercel/static" } - ], - "routes": [ - { "src": "/api/(.*)", "dest": "/start-server.js" }, - { "src": "/(.*)", "dest": "/public/$1" } - ] -} From b7c994776e750cee96cc56fe624859079aef80c3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:50:16 +0000 Subject: [PATCH 013/381] chore: update test configuration to use Netlify URL Co-Authored-By: G --- tests/api.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api.test.js b/tests/api.test.js index 0de0d0a7b..9805ff752 100644 --- a/tests/api.test.js +++ b/tests/api.test.js @@ -1,5 +1,5 @@ const request = require('supertest'); -const baseURL = process.env.VERCEL_URL || 'http://localhost:3000'; +const baseURL = process.env.NETLIFY_URL || 'http://localhost:3000'; describe('API Tests', () => { test('POST /api/consultations with valid data', async () => { @@ -26,4 +26,4 @@ describe('API Tests', () => { expect(response.status).toBe(400); expect(response.body.error).toBeTruthy(); }); -}); \ No newline at end of file +}); From b5cca5590edddc24e96b22f0cca69401aefd0d6c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:53:42 +0000 Subject: [PATCH 014/381] fix: update build script for Netlify functions Co-Authored-By: G --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c35fc9f2c..454b50645 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "next build && mkdir -p netlify/functions/api && cp -r app/api netlify/functions/", "start": "next start", "test": "jest", "test:watch": "jest --watch" From 54cdb138d11bf372420028ad42ef1f8f735710db Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:12:38 +0000 Subject: [PATCH 015/381] fix: simplify netlify configuration for headers and redirects Co-Authored-By: G --- netlify.toml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/netlify.toml b/netlify.toml index 19a15257f..d665e1db8 100644 --- a/netlify.toml +++ b/netlify.toml @@ -34,21 +34,14 @@ from = "/api/*" to = "/.netlify/functions/api/:splat" status = 200 - force = true [[redirects]] - from = "/" + from = "/*" to = "/index.html" status = 200 - force = true - -[[redirects]] - from = "/*" - to = "/404.html" - status = 404 [[headers]] - for = "/api/*" + for = "/*" [headers.values] X-Frame-Options = "DENY" X-XSS-Protection = "1; mode=block" @@ -57,11 +50,3 @@ Access-Control-Allow-Origin = "*" Access-Control-Allow-Headers = "Origin, X-Requested-With, Content-Type, Accept, x-api-key" Access-Control-Allow-Methods = "GET, POST, OPTIONS" - -[[headers]] - for = "/*" - [headers.values] - X-Frame-Options = "DENY" - X-XSS-Protection = "1; mode=block" - X-Content-Type-Options = "nosniff" - Referrer-Policy = "strict-origin-when-cross-origin" From 27b2ed97f9af718875e6bdad3ed33a3ff14f62e7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:14:24 +0000 Subject: [PATCH 016/381] fix: remove processing settings from netlify configuration Co-Authored-By: G --- netlify.toml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/netlify.toml b/netlify.toml index d665e1db8..a61e4a30b 100644 --- a/netlify.toml +++ b/netlify.toml @@ -8,23 +8,6 @@ NEXT_USE_NETLIFY = "true" NEXT_STATIC_EXPORT = "true" -[build.processing] - skip_processing = false - -[build.processing.html] - pretty_urls = true - -[build.processing.css] - bundle = true - minify = true - -[build.processing.js] - bundle = true - minify = true - -[build.processing.images] - compress = true - [functions] node_bundler = "esbuild" included_files = ["app/api/**"] From 447ce956d02b0bdc90d5b8ef0c0ff0c3ddbe176e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:16:22 +0000 Subject: [PATCH 017/381] fix: simplify netlify configuration to basic Next.js static export Co-Authored-By: G --- netlify.toml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/netlify.toml b/netlify.toml index a61e4a30b..ee2c61876 100644 --- a/netlify.toml +++ b/netlify.toml @@ -12,24 +12,3 @@ node_bundler = "esbuild" included_files = ["app/api/**"] external_node_modules = ["mongodb"] - -[[redirects]] - from = "/api/*" - to = "/.netlify/functions/api/:splat" - status = 200 - -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 - -[[headers]] - for = "/*" - [headers.values] - X-Frame-Options = "DENY" - X-XSS-Protection = "1; mode=block" - X-Content-Type-Options = "nosniff" - Referrer-Policy = "strict-origin-when-cross-origin" - Access-Control-Allow-Origin = "*" - Access-Control-Allow-Headers = "Origin, X-Requested-With, Content-Type, Accept, x-api-key" - Access-Control-Allow-Methods = "GET, POST, OPTIONS" From 73b06288df488da6e9c9a47309488413ab789d59 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:18:29 +0000 Subject: [PATCH 018/381] fix: add basic redirects for Next.js static export Co-Authored-By: G --- netlify.toml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/netlify.toml b/netlify.toml index ee2c61876..fa430d122 100644 --- a/netlify.toml +++ b/netlify.toml @@ -12,3 +12,20 @@ node_bundler = "esbuild" included_files = ["app/api/**"] external_node_modules = ["mongodb"] + +[[redirects]] + from = "/api/*" + to = "/.netlify/functions/api/:splat" + status = 200 + +[[redirects]] + from = "/*" + to = "/404.html" + status = 404 + force = false + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + force = false From 40b74616f467ab5bc0fe956e9d6aff26cd56d5fe Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:38:09 +0000 Subject: [PATCH 019/381] fix: simplify Next.js configuration for static export Co-Authored-By: G --- next.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/next.config.js b/next.config.js index 3ef5c4f81..824ba0af2 100644 --- a/next.config.js +++ b/next.config.js @@ -1,12 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, - swcMinify: true, output: 'export', images: { unoptimized: true, }, - trailingSlash: true, }; module.exports = nextConfig; From ce9abd65abeed5501cf646197828aa7c9731435a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:39:56 +0000 Subject: [PATCH 020/381] fix: update build script to handle functions directory Co-Authored-By: G --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 454b50645..235cd0a0d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build && mkdir -p netlify/functions/api && cp -r app/api netlify/functions/", + "build": "next build && cp -r netlify/functions .", "start": "next start", "test": "jest", "test:watch": "jest --watch" From 8df6b35c8fdfcaecbfe8a82a2cf5a1ef943f582b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:41:25 +0000 Subject: [PATCH 021/381] fix: use minimal netlify configuration Co-Authored-By: G --- netlify.toml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/netlify.toml b/netlify.toml index fa430d122..808f98698 100644 --- a/netlify.toml +++ b/netlify.toml @@ -5,27 +5,8 @@ [build.environment] NODE_VERSION = "18" - NEXT_USE_NETLIFY = "true" - NEXT_STATIC_EXPORT = "true" [functions] node_bundler = "esbuild" included_files = ["app/api/**"] external_node_modules = ["mongodb"] - -[[redirects]] - from = "/api/*" - to = "/.netlify/functions/api/:splat" - status = 200 - -[[redirects]] - from = "/*" - to = "/404.html" - status = 404 - force = false - -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 - force = false From db414bb7feb8d1786c053609ae0eb5e04adfac98 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:42:39 +0000 Subject: [PATCH 022/381] fix: add Netlify _redirects file for SPA routing Co-Authored-By: G --- public/_redirects | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/_redirects diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 000000000..7797f7c6a --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 From 91e0dc313cc9c12cf50cfa0a00c61fcd8fd5b75f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:44:06 +0000 Subject: [PATCH 023/381] fix: add API redirects and CORS headers Co-Authored-By: G --- netlify.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/netlify.toml b/netlify.toml index 808f98698..1337d1247 100644 --- a/netlify.toml +++ b/netlify.toml @@ -10,3 +10,15 @@ node_bundler = "esbuild" included_files = ["app/api/**"] external_node_modules = ["mongodb"] + +[[redirects]] + from = "/api/*" + to = "/.netlify/functions/api/:splat" + status = 200 + +[[headers]] + for = "/api/*" + [headers.values] + Access-Control-Allow-Origin = "*" + Access-Control-Allow-Headers = "Origin, X-Requested-With, Content-Type, Accept, x-api-key" + Access-Control-Allow-Methods = "GET, POST, OPTIONS" From f63addb2a82c9d2ea6d8bcceb2d8348efa014bc3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:45:22 +0000 Subject: [PATCH 024/381] fix: update build script to copy redirects file Co-Authored-By: G --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 235cd0a0d..25c05742a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build && cp -r netlify/functions .", + "build": "next build && cp -r netlify/functions . && cp public/_redirects out/", "start": "next start", "test": "jest", "test:watch": "jest --watch" From b38e2ba09af8f73bfbee4a87d1062e765033fe19 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:53:28 +0000 Subject: [PATCH 025/381] fix: update build script to remove next export Co-Authored-By: G --- package-lock.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 46 insertions(+) diff --git a/package-lock.json b/package-lock.json index 3df4db751..d37bf020e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "zod": "^3.24.1" }, "devDependencies": { + "@netlify/functions": "^3.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", @@ -1810,6 +1811,43 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@netlify/functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-3.0.0.tgz", + "integrity": "sha512-XXf9mNw4+fkxUzukDpJtzc32bl1+YlXZwEhc5ZgMcTbJPLpgRLDs5WWSPJ4eY/Mv1ZFvtxmMwmfgoQYVt68Qog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/serverless-functions-api": "1.30.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@netlify/node-cookies": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@netlify/node-cookies/-/node-cookies-0.1.0.tgz", + "integrity": "sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.16.0 || >=16.0.0" + } + }, + "node_modules/@netlify/serverless-functions-api": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.30.1.tgz", + "integrity": "sha512-JkbaWFeydQdeDHz1mAy4rw+E3bl9YtbCgkntfTxq+IlNX/aIMv2/b1kZnQZcil4/sPoZGL831Dq6E374qRpU1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/node-cookies": "^0.1.0", + "urlpattern-polyfill": "8.0.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@next/env": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", @@ -9582,6 +9620,13 @@ "requires-port": "^1.0.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz", + "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 25c05742a..a0b17ea2f 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "zod": "^3.24.1" }, "devDependencies": { + "@netlify/functions": "^3.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", From 96174ff8f245f4dc79b8de33b3f012d9fc1a081a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:55:45 +0000 Subject: [PATCH 026/381] feat: add Netlify functions directory Co-Authored-By: G --- functions/api/consultations.ts | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 functions/api/consultations.ts diff --git a/functions/api/consultations.ts b/functions/api/consultations.ts new file mode 100644 index 000000000..f1ee5d968 --- /dev/null +++ b/functions/api/consultations.ts @@ -0,0 +1,52 @@ +import { Handler } from '@netlify/functions'; +import { connectDB } from '../../../src/lib/mongodb'; +import { Consultation } from '../../../src/lib/models/consultation'; +import { CustomerSchema } from '../../../src/lib/schemas/customer'; +import { EmailService } from '../../../src/lib/email/service'; +import { validateApiKey } from '../../../src/lib/middleware/auth'; +import { monitorRequest } from '../../../src/lib/middleware/monitoring'; + +const emailService = new EmailService(); + +export const handler: Handler = async (event, context) => { + if (event.httpMethod !== 'POST') { + return { + statusCode: 405, + body: JSON.stringify({ error: 'Method not allowed' }), + }; + } + + try { + const body = JSON.parse(event.body || '{}'); + const validatedData = CustomerSchema.parse(body); + + if (process.env.NODE_ENV !== 'test') { + await connectDB(); + } + + const consultation = await Consultation.create(validatedData); + + // Send emails asynchronously + try { + await Promise.all([ + emailService.sendWelcomeEmail(validatedData), + emailService.sendAdminNotification(validatedData), + ]); + } catch (emailError) { + console.error('Failed to send emails:', emailError); + } + + return { + statusCode: 200, + body: JSON.stringify({ success: true, id: consultation._id }), + }; + } catch (error: any) { + console.error('Consultation submission error:', error); + return { + statusCode: error.code === 'VALIDATION_ERROR' ? 400 : 500, + body: JSON.stringify({ + error: error.message || 'Failed to submit consultation' + }), + }; + } +}; From 39f860b759938f0b61a70e507c62c7b88cd1385e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:57:40 +0000 Subject: [PATCH 027/381] fix: update Netlify configuration with proper headers and redirects Co-Authored-By: G --- netlify.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/netlify.toml b/netlify.toml index 1337d1247..3b896fbec 100644 --- a/netlify.toml +++ b/netlify.toml @@ -5,17 +5,32 @@ [build.environment] NODE_VERSION = "18" + NEXT_TELEMETRY_DISABLED = "1" [functions] node_bundler = "esbuild" included_files = ["app/api/**"] external_node_modules = ["mongodb"] +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + [[redirects]] from = "/api/*" to = "/.netlify/functions/api/:splat" status = 200 +[[headers]] + for = "/*" + [headers.values] + X-Frame-Options = "DENY" + X-XSS-Protection = "1; mode=block" + X-Content-Type-Options = "nosniff" + Referrer-Policy = "strict-origin-when-cross-origin" + Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" + [[headers]] for = "/api/*" [headers.values] From 576a080569fbbe043556d6209161cdc5052c9380 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:58:50 +0000 Subject: [PATCH 028/381] fix: update _redirects file with API and SPA routes Co-Authored-By: G --- public/_redirects | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/_redirects b/public/_redirects index 7797f7c6a..7bb7c0b7d 100644 --- a/public/_redirects +++ b/public/_redirects @@ -1 +1,2 @@ -/* /index.html 200 +/api/* /.netlify/functions/api/:splat 200 +/* /index.html 200 From ad6a78737e61a8187d793fd121eaf0b46a03d2a5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:59:34 +0000 Subject: [PATCH 029/381] fix: update CSP headers for Netlify deployment Co-Authored-By: G --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 3b896fbec..14822cf34 100644 --- a/netlify.toml +++ b/netlify.toml @@ -29,7 +29,7 @@ X-XSS-Protection = "1; mode=block" X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin" - Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" + Content-Security-Policy = "default-src 'self' *.netlify.app; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' *.netlify.app *.netlify.com;" [[headers]] for = "/api/*" From 18257c8cd66e03717317a2b2a9df498c854ad048 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:00:36 +0000 Subject: [PATCH 030/381] fix: simplify Netlify configuration to minimal settings Co-Authored-By: G --- netlify.toml | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/netlify.toml b/netlify.toml index 14822cf34..a6bd635d9 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,39 +1,8 @@ [build] command = "npm run build" publish = "out" - functions = "netlify/functions" - -[build.environment] - NODE_VERSION = "18" - NEXT_TELEMETRY_DISABLED = "1" - -[functions] - node_bundler = "esbuild" - included_files = ["app/api/**"] - external_node_modules = ["mongodb"] [[redirects]] from = "/*" to = "/index.html" status = 200 - -[[redirects]] - from = "/api/*" - to = "/.netlify/functions/api/:splat" - status = 200 - -[[headers]] - for = "/*" - [headers.values] - X-Frame-Options = "DENY" - X-XSS-Protection = "1; mode=block" - X-Content-Type-Options = "nosniff" - Referrer-Policy = "strict-origin-when-cross-origin" - Content-Security-Policy = "default-src 'self' *.netlify.app; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' *.netlify.app *.netlify.com;" - -[[headers]] - for = "/api/*" - [headers.values] - Access-Control-Allow-Origin = "*" - Access-Control-Allow-Headers = "Origin, X-Requested-With, Content-Type, Accept, x-api-key" - Access-Control-Allow-Methods = "GET, POST, OPTIONS" From 85906d7b2a2a0f008459ed9ca7d88e119c021116 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:01:25 +0000 Subject: [PATCH 031/381] fix: update Netlify configuration with functions and redirects Co-Authored-By: G --- netlify.toml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/netlify.toml b/netlify.toml index a6bd635d9..c0ffa4cd5 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,6 +1,19 @@ [build] command = "npm run build" publish = "out" + functions = "netlify/functions" + +[build.environment] + NODE_VERSION = "18" + +[functions] + node_bundler = "esbuild" + external_node_modules = ["mongodb"] + +[[redirects]] + from = "/api/*" + to = "/.netlify/functions/api/:splat" + status = 200 [[redirects]] from = "/*" From 4952773c8d1742baf95c3dedf40a89b74916cb20 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:02:26 +0000 Subject: [PATCH 032/381] fix: remove duplicate functions directory Co-Authored-By: G --- functions/api/consultations.ts | 52 ---------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 functions/api/consultations.ts diff --git a/functions/api/consultations.ts b/functions/api/consultations.ts deleted file mode 100644 index f1ee5d968..000000000 --- a/functions/api/consultations.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Handler } from '@netlify/functions'; -import { connectDB } from '../../../src/lib/mongodb'; -import { Consultation } from '../../../src/lib/models/consultation'; -import { CustomerSchema } from '../../../src/lib/schemas/customer'; -import { EmailService } from '../../../src/lib/email/service'; -import { validateApiKey } from '../../../src/lib/middleware/auth'; -import { monitorRequest } from '../../../src/lib/middleware/monitoring'; - -const emailService = new EmailService(); - -export const handler: Handler = async (event, context) => { - if (event.httpMethod !== 'POST') { - return { - statusCode: 405, - body: JSON.stringify({ error: 'Method not allowed' }), - }; - } - - try { - const body = JSON.parse(event.body || '{}'); - const validatedData = CustomerSchema.parse(body); - - if (process.env.NODE_ENV !== 'test') { - await connectDB(); - } - - const consultation = await Consultation.create(validatedData); - - // Send emails asynchronously - try { - await Promise.all([ - emailService.sendWelcomeEmail(validatedData), - emailService.sendAdminNotification(validatedData), - ]); - } catch (emailError) { - console.error('Failed to send emails:', emailError); - } - - return { - statusCode: 200, - body: JSON.stringify({ success: true, id: consultation._id }), - }; - } catch (error: any) { - console.error('Consultation submission error:', error); - return { - statusCode: error.code === 'VALIDATION_ERROR' ? 400 : 500, - body: JSON.stringify({ - error: error.message || 'Failed to submit consultation' - }), - }; - } -}; From 931d6cbea0b03c15001e0edeffb8fa6d319471fa Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:14:27 +0000 Subject: [PATCH 033/381] feat: add mobile navigation with hamburger menu Co-Authored-By: G --- components/Header.tsx | 103 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/components/Header.tsx b/components/Header.tsx index b61d1b01e..80532e5cb 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,15 +1,51 @@ 'use client'; +import { useState, useEffect } from 'react'; import Link from 'next/link'; export default function Header() { + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const toggleMenu = () => setIsMenuOpen(!isMenuOpen); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + const target = event.target as Element; + if (isMenuOpen && !target.closest('#mobile-menu')) { + setIsMenuOpen(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [isMenuOpen]); + return (
Botsmann -
); From e56d656b04b8680f9d327dcc0fa7aa964ef54b49 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:58:28 +0000 Subject: [PATCH 034/381] feat: enhance page-to-page navigation with NextSection component Co-Authored-By: G --- app/about/page.tsx | 7 ++ app/solutions/venture-credit/page.tsx | 107 +++++++++++++++++++++ src/components/bots/BotPage.tsx | 108 ++++++++++++++++++++++ src/components/navigation/NextSection.tsx | 24 +++++ src/types/bot.ts | 19 ++++ 5 files changed, 265 insertions(+) create mode 100644 app/solutions/venture-credit/page.tsx create mode 100644 src/components/bots/BotPage.tsx create mode 100644 src/components/navigation/NextSection.tsx create mode 100644 src/types/bot.ts diff --git a/app/about/page.tsx b/app/about/page.tsx index 3252eac13..781f92ae6 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ConsultationForm from '@/components/ConsultationForm'; +import { NextSection } from '@/src/components/navigation/NextSection'; export default function About() { return ( @@ -16,6 +17,12 @@ export default function About() { + + ); } diff --git a/app/solutions/venture-credit/page.tsx b/app/solutions/venture-credit/page.tsx new file mode 100644 index 000000000..68cf6e9f0 --- /dev/null +++ b/app/solutions/venture-credit/page.tsx @@ -0,0 +1,107 @@ +'use client'; + +import React from 'react'; +import { NextSection } from '../../../src/components/navigation/NextSection'; + +export default function VentureCredit() { + return ( +
+

Venture Credit Workflow Automation

+ +
+

+ Streamline your venture credit operations with our AI-powered workflow automation solution. + Monitor portfolio companies, analyze financial data, and make informed decisions faster. +

+
+ +
+

Key Features

+
+
+

Automated Reporting

+

+ Automatically ingest and process regular reports from portfolio companies, saving time and reducing errors. +

+
+
+

Debt Monitoring

+

+ Track and analyze debt metrics in real-time with automated alerts for key thresholds. +

+
+
+

Financial Analysis

+

+ Comprehensive analysis of financials, KPIs, and qualitative factors including investor information. +

+
+
+
+ +
+

How It Works

+
+
+
+
+ 1 +
+
+
+

Data Integration

+

+ Connect your portfolio companies' reporting systems for automated data ingestion. +

+
+
+
+
+
+ 2 +
+
+
+

AI Analysis

+

+ Our AI processes financial data, identifies trends, and generates insights automatically. +

+
+
+
+
+
+ 3 +
+
+
+

Monitoring Dashboard

+

+ Access real-time insights and alerts through an intuitive dashboard interface. +

+
+
+
+
+ +
+

Ready to Transform Your Workflow?

+

+ Let us help you automate your venture credit operations and make better decisions faster. +

+ + Contact Us + +
+ + +
+ ); +} diff --git a/src/components/bots/BotPage.tsx b/src/components/bots/BotPage.tsx new file mode 100644 index 000000000..dc964f298 --- /dev/null +++ b/src/components/bots/BotPage.tsx @@ -0,0 +1,108 @@ +'use client'; + +import React from 'react'; +import { NextSection } from '../navigation/NextSection'; +import type { BotPageProps, Feature, Step } from '@/src/types/bot'; + +function Overview({ content }: { content: string }) { + return ( +
+

{content}

+
+ ); +} + +function Features({ items }: { items: Feature[] }) { + return ( +
+

Features

+
+ {items.map((feature, index) => ( +
+ {feature.icon && ( +
{feature.icon}
+ )} +

{feature.title}

+

{feature.description}

+
+ ))} +
+
+ ); +} + +function HowItWorks({ steps }: { steps: Step[] }) { + return ( +
+

How It Works

+
+ {steps.map((step, index) => ( +
+
+
+ {index + 1} +
+
+
+

{step.title}

+

{step.description}

+ {step.image && ( + {step.title} + )} +
+
+ ))} +
+
+ ); +} + +function Demo({ children }: { children: React.ReactNode }) { + return ( +
+

Demo

+
+ {children} +
+
+ ); +} + +function CTASection() { + return ( +
+

Ready to Get Started?

+

+ Experience the power of AI automation for your specific needs. +

+ + Contact Us + +
+ ); +} + +export function BotPage({ title, overview, features, howItWorks, demo }: BotPageProps) { + return ( +
+

{title}

+ + + + {demo && {demo}} + + +
+ ); +} diff --git a/src/components/navigation/NextSection.tsx b/src/components/navigation/NextSection.tsx new file mode 100644 index 000000000..a74f75b27 --- /dev/null +++ b/src/components/navigation/NextSection.tsx @@ -0,0 +1,24 @@ +'use client'; + +import React from 'react'; +import Link from 'next/link'; + +interface NextSectionProps { + nextPage: string; + title: string; + description: string; +} + +export function NextSection({ nextPage, title, description }: NextSectionProps) { + return ( +
+

Continue Your Journey

+ +
+

{title}

+

{description}

+
+ +
+ ); +} diff --git a/src/types/bot.ts b/src/types/bot.ts new file mode 100644 index 000000000..289587bf5 --- /dev/null +++ b/src/types/bot.ts @@ -0,0 +1,19 @@ +export interface Feature { + title: string; + description: string; + icon?: string; +} + +export interface Step { + title: string; + description: string; + image?: string; +} + +export interface BotPageProps { + title: string; + overview: string; + features: Feature[]; + howItWorks: Step[]; + demo?: React.ReactNode; +} From 5968a4086eebe2a8ab027b99d017816b66842a70 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:00:45 +0000 Subject: [PATCH 035/381] feat: update About section with inspiring automation message Co-Authored-By: G --- app/about/page.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/about/page.tsx b/app/about/page.tsx index 781f92ae6..6f42de1a6 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -6,10 +6,16 @@ export default function About() { return (

About Botsmann

+

+ At Botsmann, we are committed to methodically automating redundant, intransparent, and + labor-intensive processes. Our mission is to free people from unpleasant tasks, giving + them back their most precious resource: time and freedom to focus on what truly matters. +

- We specialize in developing cutting-edge AI solutions that help businesses automate tasks, - enhance productivity, and unlock new possibilities. Our suite of specialized bots is designed - to tackle specific challenges across various industries. + Through innovative AI solutions and automation technologies, we transform complex, + time-consuming workflows into efficient, transparent processes. Our commitment extends + beyond mere automation – we strive to create a future where technology serves human + potential, enabling everyone to pursue meaningful endeavors.

Get in Touch

From d537541f41c75c3cfa9ed88dad668fba8c438c1f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:56:11 +0000 Subject: [PATCH 036/381] feat: update project structure with professional names and descriptions Co-Authored-By: G --- app/projects/page.tsx | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/app/projects/page.tsx b/app/projects/page.tsx index 88be5b14a..da6335ffe 100644 --- a/app/projects/page.tsx +++ b/app/projects/page.tsx @@ -6,16 +6,28 @@ import Image from 'next/image'; const projects = [ { - title: 'LiberTech', - description: 'Technologies dedicated to maximizing human liberty and minimizing government power. Featuring innovative solutions like the Venmo-style government spending tracker.', - href: '/projects/libertech', - image: '/libertech.png' + title: 'Governance', + description: 'Technologies dedicated to maximizing transparency and accountability in government spending, featuring innovative solutions like the Venmo-style spending tracker.', + href: '/projects/governance', + image: '/governance.png' }, { - title: 'Roboshop', + title: 'Credit', + description: 'Enterprise-grade automation for venture credit operations. Automatically ingest and analyze portfolio company reports, monitor debt metrics, and make data-driven decisions.', + href: '/projects/credit', + image: '/credit.png' + }, + { + title: 'Shopping', description: 'AI-powered shopping assistant that finds exactly what you need with just one word. Integrating with multiple e-commerce platforms for the best results.', - href: '/projects/roboshop', - image: '/roboshop.png' + href: '/projects/shopping', + image: '/shopping.png' + }, + { + title: 'Project Finance', + description: 'Full transparency project finance and management tool. Start projects, manage funding through donations/credit/investments, track tasks and costs, with complete public visibility.', + href: '/projects/finance', + image: '/finance.png' } ]; @@ -26,7 +38,8 @@ export default function Projects() {

Projects

- Explore our ambitious projects aimed at transforming society through technology. + Explore our transformative projects focused on governance, finance, and automation. + Each project represents our commitment to transparency, efficiency, and technological innovation.

From ab3ab2c28fadf9990dfd57f0ef3a8a8c0ac80bcb Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:57:13 +0000 Subject: [PATCH 037/381] feat: update navigation to make Bots/Projects links clickable with dropdowns Co-Authored-By: G --- components/Header.tsx | 64 +++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/components/Header.tsx b/components/Header.tsx index 80532e5cb..d1d2c5132 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -47,9 +47,9 @@ export default function Header() {
); From 1b53c59701ca755542a7bcca83ab7ac5894f70f8 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:39:42 +0000 Subject: [PATCH 172/381] fix: simplify component imports to resolve infinite loop Co-Authored-By: G --- app/layout.tsx | 6 ++---- components/Header.tsx | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 22fca6ac3..664f0845d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,8 @@ import { Inter } from 'next/font/google'; -import dynamic from 'next/dynamic'; +import Header from '@/components/Header'; +import Footer from '@/components/Footer'; import './globals.css'; -const Header = dynamic(() => import('@/components/Header'), { ssr: true }); -const Footer = dynamic(() => import('@/components/Footer'), { ssr: true }); - const inter = Inter({ subsets: ['latin'] }); export const metadata = { diff --git a/components/Header.tsx b/components/Header.tsx index 156cea3f1..9a0d708ca 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,9 +1,5 @@ import Link from 'next/link'; -import dynamic from 'next/dynamic'; - -const MobileMenu = dynamic(() => import('./MobileMenu'), { - ssr: false -}); +import MobileMenu from './MobileMenu'; export default function Header() { From b2734a14b82cd2516d31d55bb9e015a6ee52611a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:40:13 +0000 Subject: [PATCH 173/381] fix: split menu button into separate client component Co-Authored-By: G --- components/MenuButton.tsx | 45 +++++++++++++++++++++++++++++++++++++ components/MobileMenu.tsx | 47 +++++++-------------------------------- 2 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 components/MenuButton.tsx diff --git a/components/MenuButton.tsx b/components/MenuButton.tsx new file mode 100644 index 000000000..e7be06f75 --- /dev/null +++ b/components/MenuButton.tsx @@ -0,0 +1,45 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; + +export function MenuButton({ onToggle }: { onToggle: (isOpen: boolean) => void }) { + const [isOpen, setIsOpen] = useState(false); + + const handleClick = () => { + const newState = !isOpen; + setIsOpen(newState); + onToggle(newState); + }; + + useEffect(() => { + const handleClickOutside = (event: Event) => { + const target = event.target as Element; + if (isOpen && !target.closest('#mobile-menu')) { + setIsOpen(false); + onToggle(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [isOpen, onToggle]); + + return ( + + ); +} diff --git a/components/MobileMenu.tsx b/components/MobileMenu.tsx index 994c3b1bf..e075a451a 100644 --- a/components/MobileMenu.tsx +++ b/components/MobileMenu.tsx @@ -1,51 +1,20 @@ -'use client'; - -import React, { useState, useEffect, type MouseEventHandler } from 'react'; import Link from 'next/link'; -import { Fragment } from 'react'; +import { MenuButton } from './MenuButton'; export default function MobileMenu() { - const [isMenuOpen, setIsMenuOpen] = useState(false); + const [isOpen, setIsOpen] = React.useState(false); - const toggleMenu: MouseEventHandler = (e) => { - e.stopPropagation(); - setIsMenuOpen(!isMenuOpen); + const handleToggle = (state: boolean) => { + setIsOpen(state); }; - useEffect(() => { - const handleClickOutside = (event: Event) => { - const target = event.target as Element; - if (isMenuOpen && !target.closest('#mobile-menu')) { - setIsMenuOpen(false); - } - }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); - }, [isMenuOpen]); - return ( - - - + <> + +
- + ); } From 357ac5ae7e6c1aca358907fa63eadf53a850d2c5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:41:55 +0000 Subject: [PATCH 175/381] fix: make MobileMenu a server component Co-Authored-By: G --- components/MobileMenu.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/components/MobileMenu.tsx b/components/MobileMenu.tsx index d3f43c556..8bc5517f9 100644 --- a/components/MobileMenu.tsx +++ b/components/MobileMenu.tsx @@ -1,14 +1,15 @@ -'use client'; - -import React, { useState } from 'react'; import Link from 'next/link'; -import { MenuButton } from './MenuButton'; +import dynamic from 'next/dynamic'; + +const MenuButton = dynamic(() => import('./MenuButton'), { + ssr: false +}); export default function MobileMenu() { - const [isOpen, setIsOpen] = useState(false); + let isOpen = false; const handleToggle = (state: boolean) => { - setIsOpen(state); + isOpen = state; }; return ( From b1a7c8aa10fdde3f68217ffb7b451f1e791f0bc9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:42:12 +0000 Subject: [PATCH 176/381] fix: improve type handling in menu components Co-Authored-By: G --- components/MenuButton.tsx | 6 +++++- components/MobileMenu.tsx | 13 ++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/components/MenuButton.tsx b/components/MenuButton.tsx index e7be06f75..d3f3b880d 100644 --- a/components/MenuButton.tsx +++ b/components/MenuButton.tsx @@ -2,7 +2,11 @@ import React, { useState, useEffect } from 'react'; -export function MenuButton({ onToggle }: { onToggle: (isOpen: boolean) => void }) { +interface MenuButtonProps { + onToggle: (isOpen: boolean) => void; +} + +export function MenuButton({ onToggle }: MenuButtonProps) { const [isOpen, setIsOpen] = useState(false); const handleClick = () => { diff --git a/components/MobileMenu.tsx b/components/MobileMenu.tsx index 8bc5517f9..d3f43c556 100644 --- a/components/MobileMenu.tsx +++ b/components/MobileMenu.tsx @@ -1,15 +1,14 @@ -import Link from 'next/link'; -import dynamic from 'next/dynamic'; +'use client'; -const MenuButton = dynamic(() => import('./MenuButton'), { - ssr: false -}); +import React, { useState } from 'react'; +import Link from 'next/link'; +import { MenuButton } from './MenuButton'; export default function MobileMenu() { - let isOpen = false; + const [isOpen, setIsOpen] = useState(false); const handleToggle = (state: boolean) => { - isOpen = state; + setIsOpen(state); }; return ( From 6590437b2c085236eed6a5fc53964d69c0359625 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:43:47 +0000 Subject: [PATCH 177/381] fix: handle menu visibility in client component Co-Authored-By: G --- components/MenuButton.tsx | 9 +++++++++ components/MobileMenu.tsx | 12 +++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/components/MenuButton.tsx b/components/MenuButton.tsx index d3f3b880d..549cd2b7d 100644 --- a/components/MenuButton.tsx +++ b/components/MenuButton.tsx @@ -13,6 +13,15 @@ export function MenuButton({ onToggle }: MenuButtonProps) { const newState = !isOpen; setIsOpen(newState); onToggle(newState); + + // Toggle menu visibility using DOM + const menu = document.querySelector('#mobile-menu-overlay'); + if (menu) { + menu.classList.toggle('translate-y-0'); + menu.classList.toggle('opacity-100'); + menu.classList.toggle('-translate-y-full'); + menu.classList.toggle('opacity-0'); + } }; useEffect(() => { diff --git a/components/MobileMenu.tsx b/components/MobileMenu.tsx index d3f43c556..0d2fbb8ed 100644 --- a/components/MobileMenu.tsx +++ b/components/MobileMenu.tsx @@ -1,14 +1,9 @@ -'use client'; - -import React, { useState } from 'react'; import Link from 'next/link'; import { MenuButton } from './MenuButton'; export default function MobileMenu() { - const [isOpen, setIsOpen] = useState(false); - const handleToggle = (state: boolean) => { - setIsOpen(state); + // State is handled in MenuButton component }; return ( @@ -16,9 +11,8 @@ export default function MobileMenu() {
@@ -95,7 +95,8 @@ export default async function InfrastructurePage() {
- Best for: Startups, rapid prototyping, teams without DevOps + Best for: Startups, rapid prototyping, teams + without DevOps
@@ -123,7 +124,8 @@ export default async function InfrastructurePage() {
- Best for: Low-traffic apps, side projects, event-driven workloads + Best for: Low-traffic apps, side projects, + event-driven workloads
@@ -176,8 +178,8 @@ export default async function InfrastructurePage() { AI Model Comparison

- OpenAI vs Claude vs open source. Compare GPT-4, Claude 3, Llama 3, Mistral - by quality, cost, speed, and privacy. + OpenAI vs Claude vs open source. Compare GPT-4, Claude 3, Llama 3, Mistral by + quality, cost, speed, and privacy.

Read comparison @@ -201,8 +203,8 @@ export default async function InfrastructurePage() { Cost Estimation Guide

- Understand token pricing, estimate monthly costs for different usage levels, - and discover hidden infrastructure costs. + Understand token pricing, estimate monthly costs for different usage levels, and + discover hidden infrastructure costs.

Read guide @@ -226,8 +228,8 @@ export default async function InfrastructurePage() { Security Best Practices

- Secure your AI infrastructure. API key management, data encryption, - rate limiting, and compliance considerations. + Secure your AI infrastructure. API key management, data encryption, rate + limiting, and compliance considerations.

Read guide @@ -242,9 +244,7 @@ export default async function InfrastructurePage() { {/* Quick Reference Tables */}

Quick Reference

-

- At-a-glance comparisons for common decisions. -

+

At-a-glance comparisons for common decisions.

{/* Hosting Quick Compare */}
@@ -255,33 +255,67 @@ export default async function InfrastructurePage() { - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -298,40 +332,82 @@ export default async function InfrastructurePage() {
OptionSetupCostControlBest For + Option + + Setup + + Cost + + Control + + Best For +
Vercel / Netlify Easy$20-100/mo Limited + Vercel / Netlify + + Easy + + $20-100/mo + + Limited + Quick deploys, small teams
AWS / GCP Medium$50-500/mo High + AWS / GCP + + Medium + + $50-500/mo + + High + Scale, enterprise features
Self-hosted VPS Complex$10-200/mo Full + Self-hosted VPS + + Complex + + $10-200/mo + + Full + Privacy, cost control
- - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -344,8 +420,8 @@ export default async function InfrastructurePage() {

Need Help Deciding?

- Not sure which infrastructure is right for your project? We can help you - evaluate options and make the right choice. + Not sure which infrastructure is right for your project? We can help you evaluate + options and make the right choice.

- + ); } @@ -413,7 +494,12 @@ function ServerIcon({ className }: { className: string }) { function CloudIcon({ className }: { className: string }) { return ( - + ); } @@ -421,7 +507,12 @@ function CloudIcon({ className }: { className: string }) { function BoltIcon({ className }: { className: string }) { return ( - + ); } diff --git a/app/knowledge/page.tsx b/app/knowledge/page.tsx index 724dddb81..37283a3a6 100644 --- a/app/knowledge/page.tsx +++ b/app/knowledge/page.tsx @@ -24,77 +24,91 @@ const faqData: FAQItem[] = [ { category: 'Getting Started', question: 'What is Botsmann and how can it help my business?', - answer: 'Botsmann is an AI bot platform that provides specialized intelligent assistants for various domains including legal, medical, research, and language learning. We help businesses automate tasks, provide 24/7 customer support, and enhance productivity through custom AI solutions tailored to your specific industry needs.', + answer: + 'Botsmann is an AI bot platform that provides specialized intelligent assistants for various domains including legal, medical, research, and language learning. We help businesses automate tasks, provide 24/7 customer support, and enhance productivity through custom AI solutions tailored to your specific industry needs.', }, { category: 'Getting Started', question: 'Do I need technical expertise to use Botsmann bots?', - answer: 'No technical expertise is required to use our pre-built bots. Simply choose the bot that fits your needs and start interacting through natural conversation. For custom bot development or integrations, our consulting team can handle the technical aspects while you focus on your business goals.', + answer: + 'No technical expertise is required to use our pre-built bots. Simply choose the bot that fits your needs and start interacting through natural conversation. For custom bot development or integrations, our consulting team can handle the technical aspects while you focus on your business goals.', }, { category: 'Getting Started', question: 'How do I get started with a Botsmann bot?', - answer: 'Getting started is simple: 1) Browse our collection of specialized bots, 2) Select one that matches your needs, 3) Start interacting through natural language conversation. For enterprise solutions or custom bots, contact our consulting team for a personalized demo and implementation plan.', + answer: + 'Getting started is simple: 1) Browse our collection of specialized bots, 2) Select one that matches your needs, 3) Start interacting through natural language conversation. For enterprise solutions or custom bots, contact our consulting team for a personalized demo and implementation plan.', }, // Building AI Bots { category: 'Building AI Bots', question: 'What technologies do you use to build AI bots?', - answer: 'We leverage cutting-edge AI technologies including large language models (LLMs) like GPT-4 and Claude, combined with custom fine-tuning, retrieval-augmented generation (RAG), and domain-specific knowledge bases. Our tech stack includes Next.js, TypeScript, and various AI/ML frameworks for robust, scalable solutions.', + answer: + 'We leverage cutting-edge AI technologies including large language models (LLMs) like GPT-4 and Claude, combined with custom fine-tuning, retrieval-augmented generation (RAG), and domain-specific knowledge bases. Our tech stack includes Next.js, TypeScript, and various AI/ML frameworks for robust, scalable solutions.', }, { category: 'Building AI Bots', question: 'Can you build a custom bot for my specific industry?', - answer: 'Absolutely! Our consulting team specializes in building custom AI bots for any industry. We analyze your workflows, understand your unique requirements, and develop tailored solutions. From healthcare compliance to financial advisory, we have experience across diverse sectors.', + answer: + 'Absolutely! Our consulting team specializes in building custom AI bots for any industry. We analyze your workflows, understand your unique requirements, and develop tailored solutions. From healthcare compliance to financial advisory, we have experience across diverse sectors.', }, { category: 'Building AI Bots', question: 'How long does it take to develop a custom AI bot?', - answer: 'Development timelines vary based on complexity. Simple chatbots can be deployed in 2-4 weeks, while sophisticated enterprise solutions with custom integrations typically take 2-3 months. We provide detailed project timelines during our initial consultation.', + answer: + 'Development timelines vary based on complexity. Simple chatbots can be deployed in 2-4 weeks, while sophisticated enterprise solutions with custom integrations typically take 2-3 months. We provide detailed project timelines during our initial consultation.', }, { category: 'Building AI Bots', question: 'What is RAG and why is it important for AI bots?', - answer: 'RAG (Retrieval-Augmented Generation) combines the power of large language models with your specific knowledge base. This ensures your bot provides accurate, up-to-date information from your documents, databases, and proprietary content rather than generic responses.', + answer: + 'RAG (Retrieval-Augmented Generation) combines the power of large language models with your specific knowledge base. This ensures your bot provides accurate, up-to-date information from your documents, databases, and proprietary content rather than generic responses.', }, // Integration & Deployment { category: 'Integration & Deployment', question: 'Can Botsmann bots integrate with my existing systems?', - answer: 'Yes, our bots are designed for seamless integration. We support connections with CRMs (Salesforce, HubSpot), communication platforms (Slack, Teams, WhatsApp), helpdesk systems (Zendesk, Freshdesk), and custom APIs. Our team handles the technical integration process.', + answer: + 'Yes, our bots are designed for seamless integration. We support connections with CRMs (Salesforce, HubSpot), communication platforms (Slack, Teams, WhatsApp), helpdesk systems (Zendesk, Freshdesk), and custom APIs. Our team handles the technical integration process.', }, { category: 'Integration & Deployment', question: 'What deployment options are available?', - answer: 'We offer flexible deployment options: cloud-hosted (managed by us), on-premise (for sensitive data requirements), and hybrid solutions. All deployments include monitoring, maintenance, and regular updates to ensure optimal performance.', + answer: + 'We offer flexible deployment options: cloud-hosted (managed by us), on-premise (for sensitive data requirements), and hybrid solutions. All deployments include monitoring, maintenance, and regular updates to ensure optimal performance.', }, { category: 'Integration & Deployment', question: 'Is my data secure with Botsmann?', - answer: 'Security is our top priority. We implement enterprise-grade encryption, SOC 2 compliance standards, GDPR-compliant data handling, and offer data residency options. For sensitive industries, we provide on-premise deployment with complete data isolation.', + answer: + 'Security is our top priority. We implement enterprise-grade encryption, SOC 2 compliance standards, GDPR-compliant data handling, and offer data residency options. For sensitive industries, we provide on-premise deployment with complete data isolation.', }, // Pricing & Support { category: 'Pricing & Support', question: 'How much does it cost to build a custom AI bot?', - answer: 'Pricing depends on complexity, integrations, and support requirements. We offer three tiers: Starter (pre-built bots), Professional (customized solutions), and Enterprise (full custom development with dedicated support). Contact us for a detailed quote based on your specific needs.', + answer: + 'Pricing depends on complexity, integrations, and support requirements. We offer three tiers: Starter (pre-built bots), Professional (customized solutions), and Enterprise (full custom development with dedicated support). Contact us for a detailed quote based on your specific needs.', }, { category: 'Pricing & Support', question: 'What kind of support do you provide?', - answer: 'We provide comprehensive support including: documentation and guides (free), email support (all tiers), priority support with SLAs (Professional+), and dedicated success managers (Enterprise). Our consulting packages also include training sessions for your team.', + answer: + 'We provide comprehensive support including: documentation and guides (free), email support (all tiers), priority support with SLAs (Professional+), and dedicated success managers (Enterprise). Our consulting packages also include training sessions for your team.', }, { category: 'Pricing & Support', question: 'Do you offer training for our team?', - answer: 'Yes! Our consulting packages include training sessions covering bot management, conversation design best practices, and basic troubleshooting. We also provide documentation and video tutorials to help your team get the most out of your AI solution.', + answer: + 'Yes! Our consulting packages include training sessions covering bot management, conversation design best practices, and basic troubleshooting. We also provide documentation and video tutorials to help your team get the most out of your AI solution.', }, ]; const guides: Guide[] = [ { title: 'Building Your First AI Chatbot', - description: 'A comprehensive guide to creating a basic AI chatbot from scratch using modern tools and best practices.', + description: + 'A comprehensive guide to creating a basic AI chatbot from scratch using modern tools and best practices.', category: 'Beginner', readTime: '15 min', icon: '🤖', @@ -102,7 +116,8 @@ const guides: Guide[] = [ }, { title: 'Implementing RAG for Custom Knowledge', - description: 'Learn how to enhance your AI bot with retrieval-augmented generation for domain-specific accuracy.', + description: + 'Learn how to enhance your AI bot with retrieval-augmented generation for domain-specific accuracy.', category: 'Intermediate', readTime: '25 min', icon: '📚', @@ -110,7 +125,8 @@ const guides: Guide[] = [ }, { title: 'Designing Effective Conversation Flows', - description: 'Best practices for designing intuitive, helpful conversation flows that delight users.', + description: + 'Best practices for designing intuitive, helpful conversation flows that delight users.', category: 'Beginner', readTime: '12 min', icon: '💬', @@ -118,7 +134,8 @@ const guides: Guide[] = [ }, { title: 'Integrating AI Bots with Slack & Teams', - description: 'Step-by-step guide to deploying your AI assistant in popular workplace communication tools.', + description: + 'Step-by-step guide to deploying your AI assistant in popular workplace communication tools.', category: 'Intermediate', readTime: '20 min', icon: '🔗', @@ -126,7 +143,8 @@ const guides: Guide[] = [ }, { title: 'AI Bot Security Best Practices', - description: 'Ensure your AI bot implementation follows security best practices and compliance requirements.', + description: + 'Ensure your AI bot implementation follows security best practices and compliance requirements.', category: 'Advanced', readTime: '30 min', icon: '🔒', @@ -134,7 +152,8 @@ const guides: Guide[] = [ }, { title: 'Measuring AI Bot Performance', - description: 'Key metrics and analytics to track the success and ROI of your AI bot implementation.', + description: + 'Key metrics and analytics to track the success and ROI of your AI bot implementation.', category: 'Intermediate', readTime: '18 min', icon: '📊', @@ -142,15 +161,20 @@ const guides: Guide[] = [ }, ]; -const categories = ['All', 'Getting Started', 'Building AI Bots', 'Integration & Deployment', 'Pricing & Support']; +const categories = [ + 'All', + 'Getting Started', + 'Building AI Bots', + 'Integration & Deployment', + 'Pricing & Support', +]; export default function KnowledgeCenterPage() { const [activeCategory, setActiveCategory] = useState('All'); const [openFAQ, setOpenFAQ] = useState(null); - const filteredFAQs = activeCategory === 'All' - ? faqData - : faqData.filter(faq => faq.category === activeCategory); + const filteredFAQs = + activeCategory === 'All' ? faqData : faqData.filter((faq) => faq.category === activeCategory); return (
@@ -173,23 +197,35 @@ export default function KnowledgeCenterPage() { Knowledge - {" "}Center + {' '} + Center

- Everything you need to understand, build, and deploy AI bots. Free guides, tutorials, and answers - to help you succeed with or without our consulting services. + Everything you need to understand, build, and deploy AI bots. Free guides, tutorials, + and answers to help you succeed with or without our consulting services.

@@ -203,11 +239,13 @@ export default function KnowledgeCenterPage() { Step-by-Step - {" "}Guides + {' '} + Guides

- Practical tutorials to help you build and deploy AI bots yourself. From beginner to advanced. + Practical tutorials to help you build and deploy AI bots yourself. From beginner to + advanced.

@@ -220,26 +258,33 @@ export default function KnowledgeCenterPage() { >
{guide.icon} - + {guide.category}

{guide.title}

-

- {guide.description} -

+

{guide.description}

{guide.readTime} read Read guide - +
@@ -250,7 +295,9 @@ export default function KnowledgeCenterPage() {

More guides coming soon. Have a topic request?{' '} - Let us know + + Let us know +

@@ -263,11 +310,13 @@ export default function KnowledgeCenterPage() { Frequently Asked - {" "}Questions + {' '} + Questions

- Quick answers to common questions about AI bots, our platform, and consulting services. + Quick answers to common questions about AI bots, our platform, and consulting + services.

@@ -300,7 +349,9 @@ export default function KnowledgeCenterPage() { className="w-full flex items-center justify-between p-5 text-left hover:bg-gray-50 transition-colors" >
- {faq.category} + + {faq.category} + {faq.question}
- + {openFAQ === index && ( @@ -324,12 +380,10 @@ export default function KnowledgeCenterPage() { {/* CTA Section */}
-

- Still have questions? -

+

Still have questions?

- Our team is here to help. Whether you want to build it yourself or need expert assistance, - we're happy to guide you in the right direction. + Our team is here to help. Whether you want to build it yourself or need expert + assistance, we're happy to guide you in the right direction.

Talk to an Expert - + import('@vercel/analytics/react').then((m) => m.Analytics).catch(() => () => null), + { ssr: false }, +); export const metadata = { - title: 'Botsmann - Private AI for Your Data', - description: 'AI assistants that work with your documents. Pre-built experts for legal, medical, research, or bring your own data. Your data stays private.', + title: `${site.name} - ${site.tagline}`, + description: site.description, }; -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( @@ -20,6 +24,15 @@ export default function RootLayout({
{children}
+ + diff --git a/app/profile/page.tsx b/app/profile/page.tsx new file mode 100644 index 000000000..e72526717 --- /dev/null +++ b/app/profile/page.tsx @@ -0,0 +1,326 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import type { Route } from 'next'; +import { useRequireAuth } from '@/lib/auth'; +import { UserAvatar } from '@/components/shared/UserAvatar'; + +interface CustomBot { + id: string; + slug: string; + title: string; + description: string | null; + emoji: string; + accent_color: string; + is_public: boolean; + is_published: boolean; + knowledge_count: number; + created_at: string; + updated_at: string; +} + +interface ProfileStats { + documentsCount: number; + botsCount: number; + publishedBotsCount: number; +} + +export default function ProfilePage() { + const { user, loading: authLoading, displayName, avatarUrl } = useRequireAuth(); + const [bots, setBots] = useState([]); + const [stats, setStats] = useState({ + documentsCount: 0, + botsCount: 0, + publishedBotsCount: 0, + }); + const [loading, setLoading] = useState(true); + + // Load profile data + useEffect(() => { + if (!user) return; + + const loadData = async () => { + try { + const [docsResponse, botsResponse] = await Promise.all([ + fetch('/api/documents'), + fetch('/api/custom-bots'), + ]); + + const docsData = await docsResponse.json(); + const botsData = await botsResponse.json(); + + if (docsData.success) { + setStats((prev) => ({ + ...prev, + documentsCount: docsData.documents?.length || 0, + })); + } + + if (botsData.success) { + const botsList: CustomBot[] = botsData.bots || []; + setBots(botsList); + setStats((prev) => ({ + ...prev, + botsCount: botsList.length, + publishedBotsCount: botsList.filter((b) => b.is_published).length, + })); + } + } catch (err) { + // Silently handle + } finally { + setLoading(false); + } + }; + + loadData(); + }, [user]); + + if (authLoading || !user) { + return ( +
+
+
+ ); + } + + const publishedBots = bots.filter((b) => b.is_published); + const draftBots = bots.filter((b) => !b.is_published); + + const formatDate = (dateStr: string | undefined) => { + if (!dateStr) return 'Unknown'; + return new Date(dateStr).toLocaleDateString('en-US', { + month: 'long', + year: 'numeric', + }); + }; + + return ( +
+
+ {/* Profile Header */} +
+
+ +
+

+ {displayName || 'Anonymous User'} +

+

{user.email}

+

+ Member since {formatDate(user.created_at)} +

+ +
+
+

{stats.documentsCount}

+

Documents

+
+
+

{stats.botsCount}

+

Bots

+
+
+

{stats.publishedBotsCount}

+

Published

+
+
+ +
+ + + + + Edit Profile + +
+
+
+
+ + {/* Published Bots */} +
+
+

Published Bots

+ + Create new + +
+ + {loading ? ( +
+
+
+ ) : publishedBots.length === 0 ? ( +
+ 🤖 +

No published bots yet

+

+ Create and publish bots to share them with others +

+ + Create Your First Bot + +
+ ) : ( +
+ {publishedBots.map((bot) => ( + + ))} +
+ )} +
+ + {/* Draft Bots */} + {draftBots.length > 0 && ( +
+

Drafts

+
+ {draftBots.map((bot) => ( + + ))} +
+
+ )} + + {/* Quick Links */} +
+

Quick Links

+
+ + + + } + label="Dashboard" + /> + + + + } + label="Documents" + /> + + + + + } + label="AI Settings" + /> + + + + } + label="Settings" + /> +
+
+
+
+ ); +} + +function BotCard({ bot, isDraft }: { bot: CustomBot; isDraft?: boolean }) { + const accentColors: Record = { + blue: 'border-blue-200 hover:border-blue-300', + green: 'border-green-200 hover:border-green-300', + purple: 'border-purple-200 hover:border-purple-300', + orange: 'border-orange-200 hover:border-orange-300', + red: 'border-red-200 hover:border-red-300', + yellow: 'border-yellow-200 hover:border-yellow-300', + }; + + const borderColor = accentColors[bot.accent_color] || accentColors.blue; + + return ( + +
+ {bot.emoji} +
+
+

{bot.title}

+ {isDraft && ( + + Draft + + )} +
+ {bot.description && ( +

{bot.description}

+ )} +

{bot.knowledge_count} knowledge chunks

+
+
+ + ); +} + +function QuickLink({ href, icon, label }: { href: Route; icon: React.ReactNode; label: string }) { + return ( + +
{icon}
+

{label}

+ + ); +} diff --git a/app/projects/credit/page.tsx b/app/projects/credit/page.tsx index de30fb216..19b26bd0a 100644 --- a/app/projects/credit/page.tsx +++ b/app/projects/credit/page.tsx @@ -7,10 +7,10 @@ export default function Credit() { return (

Credit Workflow Automation

- +

- Streamline your credit operations with our AI-powered workflow automation solution. + Streamline your credit operations with our AI-powered workflow automation solution. Monitor portfolio companies, analyze financial data, and make informed decisions faster.

@@ -21,7 +21,8 @@ export default function Credit() {

Automated Reporting

- Automatically ingest and process regular reports from portfolio companies, saving time and reducing errors. + Automatically ingest and process regular reports from portfolio companies, saving time + and reducing errors.

@@ -33,7 +34,8 @@ export default function Credit() {

Financial Analysis

- Comprehensive analysis of financials, KPIs, and qualitative factors including investor information. + Comprehensive analysis of financials, KPIs, and qualitative factors including investor + information.

@@ -64,7 +66,8 @@ export default function Credit() {

AI Analysis

- Our AI processes financial data, identifies trends, and generates insights automatically. + Our AI processes financial data, identifies trends, and generates insights + automatically.

diff --git a/app/projects/finance/page.tsx b/app/projects/finance/page.tsx index 3346fb353..171cf043b 100644 --- a/app/projects/finance/page.tsx +++ b/app/projects/finance/page.tsx @@ -8,7 +8,9 @@ export default function ProjectFinance() {
-

Project Finance

+

+ Project Finance +

A revolutionary platform for transparent project finance and management. Start projects, manage funding, and track progress with complete public visibility. Every transaction, @@ -19,32 +21,50 @@ export default function ProjectFinance() {

Easy Project Creation

-

Start any project with a few clicks. Define goals, milestones, and funding needs with our intuitive interface.

+

+ Start any project with a few clicks. Define goals, milestones, and funding needs with + our intuitive interface. +

- +

Multiple Funding Sources

-

Accept donations, credit, or investments. Track all contributions transparently and provide real-time updates to stakeholders.

+

+ Accept donations, credit, or investments. Track all contributions transparently and + provide real-time updates to stakeholders. +

- +

Public Financial Dashboard

-

Real-time visibility into project finances, tasks, and progress. Monitor every transaction and milestone in real-time.

+

+ Real-time visibility into project finances, tasks, and progress. Monitor every + transaction and milestone in real-time. +

Task Management

-

Break down projects into tasks, assign costs, and track completion. Every task's budget and progress is visible to all stakeholders.

+

+ Break down projects into tasks, assign costs, and track completion. Every task's + budget and progress is visible to all stakeholders. +

Public Audit Trail

-

Complete transparency with every transaction logged and visible. Built-in tools for financial stewardship and accountability.

+

+ Complete transparency with every transaction logged and visible. Built-in tools for + financial stewardship and accountability. +

Insights and Analytics

-

Data-driven insights into project performance, spending patterns, and milestone achievement rates.

+

+ Data-driven insights into project performance, spending patterns, and milestone + achievement rates. +

@@ -54,27 +74,27 @@ export default function ProjectFinance() {

1. Project Setup

- Create your project with a clear description, goals, and funding requirements. - Break down the project into tasks, each with its own budget and timeline. - All this information is immediately public and searchable. + Create your project with a clear description, goals, and funding requirements. Break + down the project into tasks, each with its own budget and timeline. All this + information is immediately public and searchable.

2. Funding Collection

- Accept multiple types of funding: donations, investments, or credit. Each contribution - is tracked and displayed in real-time. Donors and investors can see exactly how their - money is being used. + Accept multiple types of funding: donations, investments, or credit. Each + contribution is tracked and displayed in real-time. Donors and investors can see + exactly how their money is being used.

3. Transparent Execution

- As the project progresses, every transaction and task update is automatically recorded - and displayed. Stakeholders can track progress, view financial statements, and monitor - milestone completion in real-time. + As the project progresses, every transaction and task update is automatically + recorded and displayed. Stakeholders can track progress, view financial statements, + and monitor milestone completion in real-time.

diff --git a/app/projects/governance/README.md b/app/projects/governance/README.md index 407123b4e..a4507a46a 100644 --- a/app/projects/governance/README.md +++ b/app/projects/governance/README.md @@ -13,6 +13,7 @@ This is a Next.js implementation of the Solon decentralized direct democracy pla ## Implementation Details ### Products + - `page.tsx`: Main landing page component - `layout.tsx`: Layout component with metadata - Helper components: @@ -20,6 +21,7 @@ This is a Next.js implementation of the Solon decentralized direct democracy pla - `RoadmapItem`: Shows implementation timeline phases ### Folder Structure + - `/open-pay`: Open payment and transaction tracking system - `/open-law`: Open law creation and tracking framework - `/open-service`: Public service marketplace @@ -38,19 +40,23 @@ This is a Next.js implementation of the Solon decentralized direct democracy pla - `/build`: Developer resources and contribution guidelines ### Technical Stack + - Next.js 14.2 - React - TypeScript - TailwindCSS ## Future Development + This is a frontend prototype. Future development will include: + - Backend API integration - Authentication system - Interactive transaction and voting systems - Mobile responsiveness enhancements ## TypeScript Interfaces + ```typescript interface VisionCardProps { title: string; @@ -65,4 +71,4 @@ interface RoadmapItemProps { timeline: string; current?: boolean; } -``` \ No newline at end of file +``` diff --git a/app/projects/governance/agencies/[id]/page.tsx b/app/projects/governance/agencies/[id]/page.tsx index ed278c2be..e1ddde9f5 100644 --- a/app/projects/governance/agencies/[id]/page.tsx +++ b/app/projects/governance/agencies/[id]/page.tsx @@ -9,9 +9,9 @@ import { sampleAgencies } from '../../data/sampleData'; export default function AgencyDetailPage({ params }: { params: { id: string } }) { const _router = useRouter(); const agencyId = params.id; - - const agency = sampleAgencies.find(a => a.id === agencyId); - + + const agency = sampleAgencies.find((a) => a.id === agencyId); + if (!agency) { return (
@@ -21,7 +21,7 @@ export default function AgencyDetailPage({ params }: { params: { id: string } }) The agency you are looking for does not exist or has been moved.

- @@ -32,26 +32,37 @@ export default function AgencyDetailPage({ params }: { params: { id: string } })
); } - + return (
- +
); -} \ No newline at end of file +} diff --git a/app/projects/governance/agencies/page.tsx b/app/projects/governance/agencies/page.tsx index 283e6c7c6..c61e91e0c 100644 --- a/app/projects/governance/agencies/page.tsx +++ b/app/projects/governance/agencies/page.tsx @@ -15,7 +15,7 @@ const AgenciesPage = () => { Explore all government agencies, their performance metrics, and transparency scores.

- +

Agency Transparency Index

@@ -29,19 +29,24 @@ const AgenciesPage = () => {

{agency.name}

- = 90 ? 'bg-green-100 text-green-800' : - agency.transparencyScore >= 70 ? 'bg-blue-100 text-blue-800' : - agency.transparencyScore >= 50 ? 'bg-yellow-100 text-yellow-800' : - 'bg-red-100 text-red-800' - }`}> + = 90 + ? 'bg-green-100 text-green-800' + : agency.transparencyScore >= 70 + ? 'bg-blue-100 text-blue-800' + : agency.transparencyScore >= 50 + ? 'bg-yellow-100 text-yellow-800' + : 'bg-red-100 text-red-800' + }`} + > T-Score: {agency.transparencyScore}
@@ -62,7 +67,9 @@ const AgenciesPage = () => {
@@ -79,7 +86,7 @@ const AgenciesPage = () => {
- +

Comparative Analysis

@@ -92,22 +99,40 @@ const AgenciesPage = () => {
ModelQualityCostSpeedSelf-host? + Model + + Quality + + Cost + + Speed + + Self-host? +
GPT-4o$5/1M tokens Fast + GPT-4o + + + + $5/1M tokens + + Fast + No
Claude 3.5 Sonnet$3/1M tokens Fast + Claude 3.5 Sonnet + + + + $3/1M tokens + + Fast + No
Llama 3 70BFree (self-host) Medium + Llama 3 70B + + + + Free (self-host) + + Medium + Yes
Mistral Large$2/1M tokens Fast + Mistral Large + + + + $2/1M tokens + + Fast + Yes
- - - - - - @@ -116,58 +141,73 @@ const AgenciesPage = () => { {sampleAgencies .sort((a, b) => b.transparencyScore - a.transparencyScore) .map((agency) => ( - - - - - - - - - ))} + + + + + + + + + ))}
+ Agency + Transparency + Budget Efficiency + Citizens Served + Response Time + Satisfaction
- - {agency.name} - - -
= 90 ? 'bg-green-100 text-green-800' : - agency.transparencyScore >= 70 ? 'bg-blue-100 text-blue-800' : - agency.transparencyScore >= 50 ? 'bg-yellow-100 text-yellow-800' : - 'bg-red-100 text-red-800' - }`}> - {agency.transparencyScore}/100 -
-
-
- {Math.round((agency.budget.spent / agency.budget.allocated) * 100)}% -
-
-
- {formatNumber(agency.citizenImpact.citizensServed)} -
-
-
- {agency.citizenImpact.avgResponseTime} -
-
-
= 90 ? 'text-green-600' : - agency.citizenImpact.satisfactionScore >= 70 ? 'text-blue-600' : - agency.citizenImpact.satisfactionScore >= 50 ? 'text-yellow-600' : - 'text-red-600' - }`}> - - - - {agency.citizenImpact.satisfactionScore}% -
-
+ + {agency.name} + + +
= 90 + ? 'bg-green-100 text-green-800' + : agency.transparencyScore >= 70 + ? 'bg-blue-100 text-blue-800' + : agency.transparencyScore >= 50 + ? 'bg-yellow-100 text-yellow-800' + : 'bg-red-100 text-red-800' + }`} + > + {agency.transparencyScore}/100 +
+
+
+ {Math.round((agency.budget.spent / agency.budget.allocated) * 100)}% +
+
+
+ {formatNumber(agency.citizenImpact.citizensServed)} +
+
+
+ {agency.citizenImpact.avgResponseTime} +
+
+
= 90 + ? 'text-green-600' + : agency.citizenImpact.satisfactionScore >= 70 + ? 'text-blue-600' + : agency.citizenImpact.satisfactionScore >= 50 + ? 'text-yellow-600' + : 'text-red-600' + }`} + > + + + + {agency.citizenImpact.satisfactionScore}% +
+
@@ -178,4 +218,4 @@ const AgenciesPage = () => { ); }; -export default AgenciesPage; \ No newline at end of file +export default AgenciesPage; diff --git a/app/projects/governance/citizen/page.tsx b/app/projects/governance/citizen/page.tsx index f7969f6c8..6a90d7841 100644 --- a/app/projects/governance/citizen/page.tsx +++ b/app/projects/governance/citizen/page.tsx @@ -10,21 +10,32 @@ export default function CitizenProfilePage() {
- +
); -} \ No newline at end of file +} diff --git a/app/projects/governance/components/AgencyProfile.tsx b/app/projects/governance/components/AgencyProfile.tsx index 8762126cb..ef654c386 100644 --- a/app/projects/governance/components/AgencyProfile.tsx +++ b/app/projects/governance/components/AgencyProfile.tsx @@ -95,11 +95,15 @@ const AgencyProfile: React.FC = ({ agency }) => { Budget: {formatCurrency(agency.budget.total)}/yr
-
= 80 ? 'bg-green-100 text-green-800' : - agency.transparencyScore >= 60 ? 'bg-yellow-100 text-yellow-800' : - 'bg-red-100 text-red-800' - }`}> +
= 80 + ? 'bg-green-100 text-green-800' + : agency.transparencyScore >= 60 + ? 'bg-yellow-100 text-yellow-800' + : 'bg-red-100 text-red-800' + }`} + > Transparency Score: {agency.transparencyScore}/100
@@ -107,7 +111,7 @@ const AgencyProfile: React.FC = ({ agency }) => {
- -
- + {/* Tab Navigation */}
@@ -147,7 +151,7 @@ const AgencyProfile: React.FC = ({ agency }) => {
- + {/* Agency Content */}
{/* Overview Tab */} @@ -169,13 +173,17 @@ const AgencyProfile: React.FC = ({ agency }) => { Spent: {formatCurrency(agency.budget.spent)}
-
-

Fiscal Year: {agency.budget.fiscalYear}

+

+ Fiscal Year: {agency.budget.fiscalYear} +

@@ -183,19 +191,27 @@ const AgencyProfile: React.FC = ({ agency }) => {
- {agency.citizenImpact.servicesProvided.toLocaleString()} + + {agency.citizenImpact.servicesProvided.toLocaleString()} + Services Provided
- {agency.citizenImpact.citizensServed.toLocaleString()} + + {agency.citizenImpact.citizensServed.toLocaleString()} + Citizens Served
- {agency.citizenImpact.satisfactionScore}% + + {agency.citizenImpact.satisfactionScore}% + Satisfaction Score
- {agency.citizenImpact.avgResponseTime} + + {agency.citizenImpact.avgResponseTime} + Average Response Time
@@ -204,23 +220,23 @@ const AgencyProfile: React.FC = ({ agency }) => {
- + {/* Agency Metrics */}
{agency.metrics.map((metric, index) => (
-
- {metric.name} -
-
- {metric.value} -
-
+
{metric.name}
+
{metric.value}
+
{metric.change}
@@ -229,7 +245,7 @@ const AgencyProfile: React.FC = ({ agency }) => {
)} - + {/* Transactions Tab */} {activeTab === 'transactions' && (
@@ -237,10 +253,10 @@ const AgencyProfile: React.FC = ({ agency }) => {
    {agency.transactions.map((transaction) => (
  • - @@ -251,11 +267,15 @@ const AgencyProfile: React.FC = ({ agency }) => { {transaction.description}

    -

    +

    {transaction.status}

    @@ -282,7 +302,7 @@ const AgencyProfile: React.FC = ({ agency }) => {
)} - + {/* Regulations Tab */} {activeTab === 'regulations' && (
@@ -292,30 +312,39 @@ const AgencyProfile: React.FC = ({ agency }) => {
  • -

    {regulation.title}

    +

    + {regulation.title} +

    -

    +

    {regulation.status.charAt(0).toUpperCase() + regulation.status.slice(1)}

    -

    {regulation.description}

    - +

    + {regulation.description} +

    +

    - Enacted: {regulation.dateEnacted} | Last Updated: {regulation.lastUpdated} + Enacted: {regulation.dateEnacted} | Last Updated:{' '} + {regulation.lastUpdated}

    - Enabling Law: {' '} - @@ -324,7 +353,7 @@ const AgencyProfile: React.FC = ({ agency }) => {

    - + {/* KPIs */}

    Performance Metrics:

    @@ -332,15 +361,22 @@ const AgencyProfile: React.FC = ({ agency }) => { {regulation.kpis.map((kpi, idx) => (
    -
    +

    {kpi.metric}

    -

    {kpi.current} / {kpi.target}

    +

    + {kpi.current} / {kpi.target} +

    @@ -354,11 +390,14 @@ const AgencyProfile: React.FC = ({ agency }) => {
    )} - + {/* Team Tab */} {activeTab === 'team' && (
    -
      +
        {agency.team.map((member) => (
      • = ({ agency }) => {
        {member.imageUrl ? ( /* eslint-disable-next-line @next/next/no-img-element -- Dynamic external image URL from data */ - {member.name} + {member.name} ) : (
        {member.name.charAt(0)} @@ -382,11 +425,15 @@ const AgencyProfile: React.FC = ({ agency }) => {
        Department
        {member.department}
        - = 80 ? 'bg-green-100 text-green-800' : - member.transparency >= 60 ? 'bg-yellow-100 text-yellow-800' : - 'bg-red-100 text-red-800' - }`}> + = 80 + ? 'bg-green-100 text-green-800' + : member.transparency >= 60 + ? 'bg-yellow-100 text-yellow-800' + : 'bg-red-100 text-red-800' + }`} + > T-Score: {member.transparency}/100
        @@ -397,8 +444,8 @@ const AgencyProfile: React.FC = ({ agency }) => {
        @@ -427,4 +474,4 @@ const AgencyProfile: React.FC = ({ agency }) => { ); }; -export default AgencyProfile; \ No newline at end of file +export default AgencyProfile; diff --git a/app/projects/governance/components/ApplicationsSection.tsx b/app/projects/governance/components/ApplicationsSection.tsx index f5f9e2ce6..4cf19858e 100644 --- a/app/projects/governance/components/ApplicationsSection.tsx +++ b/app/projects/governance/components/ApplicationsSection.tsx @@ -13,46 +13,93 @@ const ApplicationsSection: React.FC = () => {

        Application Areas

        - Solon's versatile platform adapts to governance needs across various levels of government and specialized jurisdictions. + Solon's versatile platform adapts to governance needs across various levels of + government and specialized jurisdictions.

        - +
        {/* Municipal Government */}
        - +

        Municipal Governance

        - Local governments can implement direct democracy for enhancing community engagement and transparency. + Local governments can implement direct democracy for enhancing community engagement + and transparency.

        • - - + + Local budget allocation through citizen voting
        • - - + + Infrastructure project prioritization
        • - - + + Community service evaluation and improvement
        • - - + + Local ordinance creation and review
        • @@ -60,48 +107,99 @@ const ApplicationsSection: React.FC = () => {
          - + 23 municipalities currently implementing Solon
        - + {/* State/Provincial Level */}
        - +

        State/Provincial Level

        - State governments can leverage the platform for broader policy implementation and regional coordination. + State governments can leverage the platform for broader policy implementation and + regional coordination.

        • - - + + State budget transparency and allocation
        • - - + + Legislative review and creation by citizens
        • - - + + Public service efficiency tracking
        • - - + + Resource allocation optimization
        • @@ -109,48 +207,99 @@ const ApplicationsSection: React.FC = () => {
          - + 7 states/provinces in pilot program
        - + {/* Federal Transparency */}
        - +

        Federal Transparency

        - National governments can provide unprecedented transparency through integrated oversight systems. + National governments can provide unprecedented transparency through integrated + oversight systems.

        • - - + + National budget transparency and oversight
        • - - + + Law creation with direct citizen input
        • - - + + Program effectiveness measurement
        • - - + + Corruption prevention through public oversight
        • @@ -158,48 +307,99 @@ const ApplicationsSection: React.FC = () => {
          - + 3 countries exploring implementation
        - + {/* Special Districts */}
        - +

        Special Districts

        - Special jurisdictions can optimize service delivery through targeted governance modules. + Special jurisdictions can optimize service delivery through targeted governance + modules.

        • - - + + School district resource allocation
        • - - + + Utility board management and oversight
        • - - + + Transportation planning and execution
        • - - + + Special-purpose governance entities
        • @@ -207,20 +407,27 @@ const ApplicationsSection: React.FC = () => {
          - + 42 special districts using customized solutions
        - +

        Custom Implementation

        - Every governmental entity has unique needs. Our team works with you to implement a custom Solon solution designed for your specific governance context, legal requirements, and community needs. + Every governmental entity has unique needs. Our team works with you to implement a + custom Solon solution designed for your specific governance context, legal + requirements, and community needs.

        @@ -235,4 +442,4 @@ const ApplicationsSection: React.FC = () => { ); }; -export default ApplicationsSection; \ No newline at end of file +export default ApplicationsSection; diff --git a/app/projects/governance/components/CTASection.tsx b/app/projects/governance/components/CTASection.tsx index 1c33c8ba5..ea174be4d 100644 --- a/app/projects/governance/components/CTASection.tsx +++ b/app/projects/governance/components/CTASection.tsx @@ -15,9 +15,10 @@ const CTASection: React.FC = () => { Ready to Transform Governance?

        - Join the future of decentralized direct democracy. Contact us to learn more about implementing Solon in your jurisdiction. + Join the future of decentralized direct democracy. Contact us to learn more about + implementing Solon in your jurisdiction.

        - +

        Request a Demo

        @@ -28,7 +29,7 @@ const CTASection: React.FC = () => { Schedule Demo
        - +

        Consultation

        @@ -39,21 +40,22 @@ const CTASection: React.FC = () => {

        - +

        Join Our Newsletter

        - Stay updated on Solon's latest developments, case studies, and governance innovations. + Stay updated on Solon's latest developments, case studies, and governance + innovations.

        -
        - +
        - +

        Contact Us

        info@solondemocracy.org

        +1 (555) 123-4567

        - +
        - +

        Community

        Join our Discord community

        Follow us on Twitter @SolonDemocracy

        - +
        - +

        Resources

        @@ -103,4 +120,4 @@ const CTASection: React.FC = () => { ); }; -export default CTASection; \ No newline at end of file +export default CTASection; diff --git a/app/projects/governance/components/CoreComponentsSection.tsx b/app/projects/governance/components/CoreComponentsSection.tsx index d5d3194b4..7faee1fbe 100644 --- a/app/projects/governance/components/CoreComponentsSection.tsx +++ b/app/projects/governance/components/CoreComponentsSection.tsx @@ -9,104 +9,152 @@ import Link from 'next/link'; */ const CoreComponentsSection: React.FC = () => { const [activeTab, setActiveTab] = useState('transparency'); - + const components = [ { id: 'transparency', name: 'Transparent Transaction System', icon: ( - - + + ), - description: 'Track and verify every government transaction with complete transparency. Our blockchain-based system ensures all financial activities are immutable, traceable, and publicly accessible.', + description: + 'Track and verify every government transaction with complete transparency. Our blockchain-based system ensures all financial activities are immutable, traceable, and publicly accessible.', features: [ 'Real-time transaction visibility', 'Tamper-proof audit trail', 'Automated verification', 'Role-based access control', - 'Public dashboards and reports' + 'Public dashboards and reports', ], stats: [ { label: 'Corruption Reduction', value: '68%' }, { label: 'Cost Savings', value: '$4.2M' }, - { label: 'Trust Increase', value: '+47%' } + { label: 'Trust Increase', value: '+47%' }, ], - link: '/projects/governance/transparency' as const + link: '/projects/governance/transparency' as const, }, { id: 'law', name: 'Law Transparency Framework', icon: ( - - + + ), - description: 'Measure and track the effectiveness of legislation with our innovative framework. Every law is tied to specific, measurable outcomes that can be monitored in real-time.', + description: + 'Measure and track the effectiveness of legislation with our innovative framework. Every law is tied to specific, measurable outcomes that can be monitored in real-time.', features: [ 'Outcome-based legislation', 'Real-time effectiveness tracking', 'Automated compliance monitoring', 'Impact assessment tools', - 'Performance dashboards' + 'Performance dashboards', ], stats: [ { label: 'Policy Effectiveness', value: '82%' }, { label: 'Compliance Rate', value: '94%' }, - { label: 'Cost Reduction', value: '$2.8M' } + { label: 'Cost Reduction', value: '$2.8M' }, ], - link: '/projects/governance/open-law' as const + link: '/projects/governance/open-law' as const, }, { id: 'marketplace', name: 'Open Service Marketplace', icon: ( - - + + ), - description: 'A decentralized marketplace for government services. Connect citizens with service providers through smart contracts and automated verification.', + description: + 'A decentralized marketplace for government services. Connect citizens with service providers through smart contracts and automated verification.', features: [ 'Smart contract automation', 'Service provider ratings', 'Automated payments', 'Quality assurance', - 'Performance tracking' + 'Performance tracking', ], stats: [ { label: 'Service Delivery', value: '95%' }, { label: 'Cost Efficiency', value: '45%' }, - { label: 'User Satisfaction', value: '92%' } + { label: 'User Satisfaction', value: '92%' }, ], - link: '/projects/governance/open-service' as const + link: '/projects/governance/open-service' as const, }, { id: 'voting', name: 'Open Vote System', icon: ( - - + + ), - description: 'Secure, verifiable, and accessible voting system for all government decisions. Built on blockchain technology for maximum transparency and trust.', + description: + 'Secure, verifiable, and accessible voting system for all government decisions. Built on blockchain technology for maximum transparency and trust.', features: [ 'Secure voting protocol', 'Real-time results', 'Vote verification', 'Accessibility features', - 'Audit trail' + 'Audit trail', ], stats: [ { label: 'Voter Turnout', value: '78%' }, { label: 'System Uptime', value: '99.9%' }, - { label: 'Trust Score', value: '95%' } + { label: 'Trust Score', value: '95%' }, ], - link: '/projects/governance/open-vote' as const - } + link: '/projects/governance/open-vote' as const, + }, ]; - - const activeComponent = components.find(component => component.id === activeTab); - + + const activeComponent = components.find((component) => component.id === activeTab); + return (
        @@ -115,11 +163,11 @@ const CoreComponentsSection: React.FC = () => { Core Technical Components

        - Solon's platform combines four revolutionary technologies to enable transparent, + Solon's platform combines four revolutionary technologies to enable transparent, efficient, and citizen-centric governance.

        - + {/* Tabs */}
        - + {/* Component Details */} {activeComponent && (
        @@ -146,63 +194,75 @@ const CoreComponentsSection: React.FC = () => {
        {activeComponent.icon} -

        - {activeComponent.name} -

        +

        {activeComponent.name}

        - -

        - {activeComponent.description} -

        - -

        - Key Features -

        - + +

        {activeComponent.description}

        + +

        Key Features

        +
          {activeComponent.features.map((feature, index) => (
        • - - + + {feature}
        • ))}
        - - Learn More - - + +
        - + {/* Right Column: Stats */}
        -

        - Impact Metrics -

        - +

        Impact Metrics

        +
        {activeComponent.stats.map((stat, index) => (
        {stat.label} - + {stat.value}
        ))}
        - +
        -

        - Example Applications -

        - +

        Example Applications

        +
          {activeComponent.id === 'transparency' && ( <> @@ -211,7 +271,7 @@ const CoreComponentsSection: React.FC = () => {
        • • Public procurement verification
        • )} - + {activeComponent.id === 'law' && ( <>
        • • Environmental protection effectiveness
        • @@ -219,7 +279,7 @@ const CoreComponentsSection: React.FC = () => {
        • • Economic development policy tracking
        • )} - + {activeComponent.id === 'marketplace' && ( <>
        • • Municipal waste management services
        • @@ -227,7 +287,7 @@ const CoreComponentsSection: React.FC = () => {
        • • Public transportation route operations
        • )} - + {activeComponent.id === 'voting' && ( <>
        • • Participatory budgeting decisions
        • @@ -245,4 +305,4 @@ const CoreComponentsSection: React.FC = () => { ); }; -export default CoreComponentsSection; \ No newline at end of file +export default CoreComponentsSection; diff --git a/app/projects/governance/components/DashboardTransactionDemo.tsx b/app/projects/governance/components/DashboardTransactionDemo.tsx index 9667438b2..335b0972c 100644 --- a/app/projects/governance/components/DashboardTransactionDemo.tsx +++ b/app/projects/governance/components/DashboardTransactionDemo.tsx @@ -43,7 +43,8 @@ const DashboardTransactionDemo: React.FC = ({ fil agency: 'Department of Transportation', recipient: 'Urban Roads Inc.', purpose: 'Highway Repair - Section 14A', - details: 'Emergency repair of damaged highway section including asphalt replacement, line repainting, and drainage improvements.', + details: + 'Emergency repair of damaged highway section including asphalt replacement, line repainting, and drainage improvements.', category: 'infrastructure', district: 'downtown', likes: 87, @@ -53,16 +54,16 @@ const DashboardTransactionDemo: React.FC = ({ fil id: 'c1', author: 'Jane Citizen', text: 'This repair was desperately needed. The potholes were damaging vehicles.', - timestamp: '2 days ago' + timestamp: '2 days ago', }, { id: 'c2', author: 'Mark Taxpayer', text: 'I drive on this section daily and can confirm the work has been completed.', - timestamp: '1 day ago' - } + timestamp: '1 day ago', + }, ], - shares: 18 + shares: 18, }, { id: 'TX-2023-06-10-014', @@ -71,7 +72,8 @@ const DashboardTransactionDemo: React.FC = ({ fil agency: 'Parks & Recreation', recipient: 'Green Spaces Landscaping', purpose: 'Community Park Maintenance', - details: 'Quarterly maintenance of Central Community Park including lawn care, tree pruning, playground equipment inspection, and irrigation system maintenance.', + details: + 'Quarterly maintenance of Central Community Park including lawn care, tree pruning, playground equipment inspection, and irrigation system maintenance.', category: 'recreation', district: 'north', likes: 134, @@ -81,10 +83,10 @@ const DashboardTransactionDemo: React.FC = ({ fil id: 'c3', author: 'Parent Council', text: 'The playground looks much better now. Thank you!', - timestamp: '3 days ago' - } + timestamp: '3 days ago', + }, ], - shares: 42 + shares: 42, }, { id: 'TX-2023-06-05-078', @@ -93,7 +95,8 @@ const DashboardTransactionDemo: React.FC = ({ fil agency: 'Education Department', recipient: 'LearnTech Solutions', purpose: 'School District Technology Upgrade', - details: 'Purchase of 500 laptops, 50 interactive whiteboards, and supporting infrastructure for the Western School District technology modernization initiative.', + details: + 'Purchase of 500 laptops, 50 interactive whiteboards, and supporting infrastructure for the Western School District technology modernization initiative.', category: 'education', district: 'west', likes: 201, @@ -103,32 +106,34 @@ const DashboardTransactionDemo: React.FC = ({ fil id: 'c4', author: 'Tech Teacher', text: 'Our school received the new equipment last week. Game changer for our STEM program!', - timestamp: '5 days ago' + timestamp: '5 days ago', }, { id: 'c5', author: 'Fiscal Watch', text: 'Why are we spending so much on technology that will be outdated in 3 years?', - timestamp: '4 days ago' + timestamp: '4 days ago', }, { id: 'c6', author: 'Education Board', text: 'These devices come with a 5-year support contract and are critical for student success.', - timestamp: '3 days ago' - } + timestamp: '3 days ago', + }, ], - shares: 67 - } + shares: 67, + }, ]); // Filter transactions based on the filter prop - const filteredTransactions = filter === 'all' - ? transactions - : transactions.filter(tx => - tx.category?.toLowerCase() === filter.toLowerCase() || - tx.district?.toLowerCase() === filter.toLowerCase() - ); + const filteredTransactions = + filter === 'all' + ? transactions + : transactions.filter( + (tx) => + tx.category?.toLowerCase() === filter.toLowerCase() || + tx.district?.toLowerCase() === filter.toLowerCase(), + ); // State for comment input const [commentInputs, setCommentInputs] = useState<{ [key: string]: string }>({}); @@ -136,17 +141,17 @@ const DashboardTransactionDemo: React.FC = ({ fil // Handle user actions (like, dislike, share) const handleAction = (id: string, action: 'like' | 'dislike' | 'share') => { - setTransactions(prev => - prev.map(tx => - tx.id === id + setTransactions((prev) => + prev.map((tx) => + tx.id === id ? { ...tx, likes: action === 'like' ? tx.likes + 1 : tx.likes, dislikes: action === 'dislike' ? tx.dislikes + 1 : tx.dislikes, - shares: action === 'share' ? tx.shares + 1 : tx.shares + shares: action === 'share' ? tx.shares + 1 : tx.shares, } - : tx - ) + : tx, + ), ); }; @@ -158,48 +163,50 @@ const DashboardTransactionDemo: React.FC = ({ fil id: `c${Math.random().toString(36).substr(2, 9)}`, author: 'You', text: commentInputs[id], - timestamp: 'Just now' + timestamp: 'Just now', }; - setTransactions(prev => - prev.map(tx => - tx.id === id - ? { ...tx, comments: [...tx.comments, newComment] } - : tx - ) + setTransactions((prev) => + prev.map((tx) => (tx.id === id ? { ...tx, comments: [...tx.comments, newComment] } : tx)), ); // Reset input - setCommentInputs(prev => ({ ...prev, [id]: '' })); + setCommentInputs((prev) => ({ ...prev, [id]: '' })); }; // Toggle comments visibility const toggleComments = (id: string) => { - setShowComments(prev => ({ + setShowComments((prev) => ({ ...prev, - [id]: !prev[id] + [id]: !prev[id], })); }; // Handle donation const handleDonate = (id: string, target: 'agency' | 'recipient') => { - const transaction = transactions.find(tx => tx.id === id); + const transaction = transactions.find((tx) => tx.id === id); if (!transaction) return; const entity = target === 'agency' ? transaction.agency : transaction.recipient; - alert(`Thank you for supporting ${entity}! This would open a donation form in a real implementation.`); + alert( + `Thank you for supporting ${entity}! This would open a donation form in a real implementation.`, + ); }; return (

          Open Payments Explorer

          - Explore and engage with government transactions. Your participation helps ensure transparency and accountability. + Explore and engage with government transactions. Your participation helps ensure + transparency and accountability.

          - + {filteredTransactions.length > 0 ? ( - filteredTransactions.map(transaction => ( -
          + filteredTransactions.map((transaction) => ( +
          {/* Transaction header */}
          @@ -215,7 +222,7 @@ const DashboardTransactionDemo: React.FC = ({ fil
          - + {/* Transaction details */}
          @@ -224,79 +231,121 @@ const DashboardTransactionDemo: React.FC = ({ fil From: {transaction.agency}
          -
          - +
          To: {transaction.recipient}
          -
          - -

          - {transaction.details} -

          + +

          {transaction.details}

          - + {/* Action buttons */}
          - - - - - - -
          - + {/* Comments section */} {showComments[transaction.id] && (

          Comments

          - + {/* Comment list */}
          - {transaction.comments.map(comment => ( + {transaction.comments.map((comment) => (
          {comment.author} @@ -322,13 +371,15 @@ const DashboardTransactionDemo: React.FC = ({ fil
          ))}
          - + {/* Comment input */}
          setCommentInputs(prev => ({ ...prev, [transaction.id]: e.target.value }))} + onChange={(e) => + setCommentInputs((prev) => ({ ...prev, [transaction.id]: e.target.value })) + } className="flex-1 min-w-0 block w-full px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" placeholder="Add your comment..." onKeyPress={(e) => e.key === 'Enter' && handleCommentSubmit(transaction.id)} @@ -348,13 +399,25 @@ const DashboardTransactionDemo: React.FC = ({ fil ) : (
          - - + +

          No transactions found

          - No transactions matching your current filter were found. Try adjusting your filters or check back later. + No transactions matching your current filter were found. Try adjusting your filters or + check back later.

          )} @@ -362,4 +425,4 @@ const DashboardTransactionDemo: React.FC = ({ fil ); }; -export default DashboardTransactionDemo; \ No newline at end of file +export default DashboardTransactionDemo; diff --git a/app/projects/governance/components/DetailedComponentsNav.tsx b/app/projects/governance/components/DetailedComponentsNav.tsx index a0300fffa..782a933e1 100644 --- a/app/projects/governance/components/DetailedComponentsNav.tsx +++ b/app/projects/governance/components/DetailedComponentsNav.tsx @@ -11,7 +11,7 @@ import { usePathname } from 'next/navigation'; */ const DetailedComponentsNav: React.FC = () => { const pathname = usePathname(); - + // Define the core components and their routes const components = [ { @@ -21,7 +21,12 @@ const DetailedComponentsNav: React.FC = () => { description: 'Blockchain-based transaction tracking system', icon: ( - + ), }, @@ -32,7 +37,12 @@ const DetailedComponentsNav: React.FC = () => { description: 'Measurable outcomes for all legislation', icon: ( - + ), }, @@ -43,7 +53,12 @@ const DetailedComponentsNav: React.FC = () => { description: 'Competitive service delivery platform', icon: ( - + ), }, @@ -54,7 +69,12 @@ const DetailedComponentsNav: React.FC = () => { description: 'Secure digital voting platform', icon: ( - + ), }, @@ -62,54 +82,61 @@ const DetailedComponentsNav: React.FC = () => { // Return to main governance page link const _backToMainLink = '/projects/governance'; - + return (
          - - + Back to Governance Overview
          - +
          {components.map((component) => { const isActive = pathname === component.path; - + return ( -
          + `} + > {component.icon}

          {component.name}

          -

          - {component.description} -

          +

          {component.description}

          ); @@ -120,4 +147,4 @@ const DetailedComponentsNav: React.FC = () => { ); }; -export default DetailedComponentsNav; \ No newline at end of file +export default DetailedComponentsNav; diff --git a/app/projects/governance/components/FAQSection.tsx b/app/projects/governance/components/FAQSection.tsx index ee61cdd08..7e4658ca6 100644 --- a/app/projects/governance/components/FAQSection.tsx +++ b/app/projects/governance/components/FAQSection.tsx @@ -10,40 +10,44 @@ interface FAQItem { } const FAQSection: React.FC = () => { - const [activeCategory, setActiveCategory] = useState<'all' | 'general' | 'technical' | 'implementation' | 'economic'>('all'); + const [activeCategory, setActiveCategory] = useState< + 'all' | 'general' | 'technical' | 'implementation' | 'economic' + >('all'); const [expandedId, setExpandedId] = useState(null); - + const toggleFAQ = (id: string) => { setExpandedId(expandedId === id ? null : id); }; - + const faqs: FAQItem[] = [ { id: 'general-1', question: 'What is Solon and how does it change governance?', answer: (

          - Solon is a blockchain-based governance platform that revolutionizes how governments operate by introducing - unprecedented transparency, accountability, and citizen participation. It replaces opaque, inefficient traditional - governance systems with a technical framework that ensures every transaction is visible, every law has - measurable outcomes, government services are competitively provided, and citizens can directly participate - in decision-making. + Solon is a blockchain-based governance platform that revolutionizes how governments + operate by introducing unprecedented transparency, accountability, and citizen + participation. It replaces opaque, inefficient traditional governance systems with a + technical framework that ensures every transaction is visible, every law has measurable + outcomes, government services are competitively provided, and citizens can directly + participate in decision-making.

          ), - category: 'general' + category: 'general', }, { id: 'general-2', question: 'Can Solon work with existing government institutions?', answer: (

          - Yes, Solon is designed to be adaptable and can be integrated with existing government structures. It can be - implemented gradually, with different components adopted at different paces based on readiness and need. - The platform can function alongside traditional systems during transition periods, and can be customized - to meet the specific requirements of different levels of government, from municipal to national. + Yes, Solon is designed to be adaptable and can be integrated with existing government + structures. It can be implemented gradually, with different components adopted at + different paces based on readiness and need. The platform can function alongside + traditional systems during transition periods, and can be customized to meet the specific + requirements of different levels of government, from municipal to national.

          ), - category: 'general' + category: 'general', }, { id: 'technical-1', @@ -51,30 +55,36 @@ const FAQSection: React.FC = () => { answer: (

          - Solon's blockchain implementation ensures transaction security through multiple mechanisms: + Solon's blockchain implementation ensures transaction security through multiple + mechanisms:

            -
          • Cryptographic validation: All transactions are cryptographically signed and validated
          • -
          • Distributed consensus: Transactions are verified across multiple nodes in the network
          • +
          • + Cryptographic validation: All transactions are cryptographically signed and validated +
          • +
          • + Distributed consensus: Transactions are verified across multiple nodes in the network +
          • Immutable records: Once recorded, transactions cannot be altered or deleted
          • Transparent audit trail: All changes are publicly visible and traceable
          • -
          • Role-based access control: Only authorized entities can initiate certain transactions
          • +
          • + Role-based access control: Only authorized entities can initiate certain transactions +

          - This approach prevents fraud, unauthorized spending, and provides real-time verification of all government financial activities. + This approach prevents fraud, unauthorized spending, and provides real-time verification + of all government financial activities.

          ), - category: 'technical' + category: 'technical', }, { id: 'technical-2', question: 'What technologies power the Solon platform?', answer: (
          -

          - Solon's platform is built on a modern technology stack including: -

          +

          Solon's platform is built on a modern technology stack including:

          • Ethereum-compatible blockchain for smart contracts and transactions
          • IPFS (InterPlanetary File System) for distributed data storage
          • @@ -88,25 +98,34 @@ const FAQSection: React.FC = () => {

          ), - category: 'technical' + category: 'technical', }, { id: 'implementation-1', question: 'How long does it take to implement Solon in a municipality?', answer: (

          - Implementation timelines vary based on municipality size and complexity, but typically follow this pattern: -

          - Small municipalities (under 50,000 residents): 3-6 months for initial deployment, with full functionality within 12 months. -

          - Medium municipalities (50,000-250,000): 6-9 months for initial deployment, with full functionality within 18 months. -

          - Large municipalities (over 250,000): 9-12 months for initial deployment, with phased implementation over 24-36 months. -

          - Each implementation includes system setup, data migration, staff training, and a transition period where Solon runs parallel to existing systems. + Implementation timelines vary based on municipality size and complexity, but typically + follow this pattern: +
          +
          + Small municipalities (under 50,000 residents): 3-6 + months for initial deployment, with full functionality within 12 months. +
          +
          + Medium municipalities (50,000-250,000): 6-9 months + for initial deployment, with full functionality within 18 months. +
          +
          + Large municipalities (over 250,000): 9-12 months for + initial deployment, with phased implementation over 24-36 months. +
          +
          + Each implementation includes system setup, data migration, staff training, and a + transition period where Solon runs parallel to existing systems.

          ), - category: 'implementation' + category: 'implementation', }, { id: 'implementation-2', @@ -114,21 +133,38 @@ const FAQSection: React.FC = () => { answer: (

          - Solon implementation includes comprehensive training programs tailored to different roles: + Solon implementation includes comprehensive training programs tailored to different + roles:

            -
          • Administrative staff: Basic platform navigation, transaction input, and reporting (1-2 days)
          • -
          • Financial officers: Advanced transaction management, budget oversight, and audit trails (2-3 days)
          • -
          • IT personnel: System maintenance, security protocols, and integration with existing systems (3-5 days)
          • -
          • Department heads: KPI management, performance tracking, and marketplace utilization (2 days)
          • -
          • Elected officials: Platform overview, policy implementation, and citizen engagement (1 day)
          • +
          • + Administrative staff: Basic platform navigation, + transaction input, and reporting (1-2 days) +
          • +
          • + Financial officers: Advanced transaction + management, budget oversight, and audit trails (2-3 days) +
          • +
          • + IT personnel: System maintenance, security + protocols, and integration with existing systems (3-5 days) +
          • +
          • + Department heads: KPI management, performance + tracking, and marketplace utilization (2 days) +
          • +
          • + Elected officials: Platform overview, policy + implementation, and citizen engagement (1 day) +

          - We also provide ongoing support, regular refresher courses, and a comprehensive knowledge base. + We also provide ongoing support, regular refresher courses, and a comprehensive + knowledge base.

          ), - category: 'implementation' + category: 'implementation', }, { id: 'economic-1', @@ -139,18 +175,33 @@ const FAQSection: React.FC = () => { Governments implementing Solon typically experience significant cost savings:

            -
          • Procurement costs: 20-30% reduction through increased competition and transparency
          • -
          • Administrative overhead: 15-25% reduction through automation and streamlined processes
          • -
          • Fraud prevention: 60-80% reduction in fraudulent transactions and misappropriation
          • -
          • Service delivery: 25-40% improved cost efficiency through marketplace competition
          • -
          • Tax compliance: 10-15% increase due to greater trust and transparency
          • +
          • + Procurement costs: 20-30% reduction through + increased competition and transparency +
          • +
          • + Administrative overhead: 15-25% reduction through + automation and streamlined processes +
          • +
          • + Fraud prevention: 60-80% reduction in fraudulent + transactions and misappropriation +
          • +
          • + Service delivery: 25-40% improved cost efficiency + through marketplace competition +
          • +
          • + Tax compliance: 10-15% increase due to greater + trust and transparency +

          Most governments achieve ROI within 24-36 months of full implementation.

          ), - category: 'economic' + category: 'economic', }, { id: 'economic-2', @@ -158,32 +209,42 @@ const FAQSection: React.FC = () => { answer: (

          Solon offers flexible licensing models to accommodate different government needs: -

          - SaaS subscription: Annual subscription based on population size, starting at $0.50 per citizen annually for basic features, with premium features available. -

          - Perpetual license: One-time purchase with ongoing maintenance fees (approximately 20% of license cost annually). Pricing is tailored to government size and required functionality. -

          - Open source community: Core components are available under open source licenses for governments with technical expertise to self-implement and maintain. -

          - All options include initial implementation support, with additional consulting and customization services available at standard rates. +
          +
          + SaaS subscription: Annual subscription based on + population size, starting at $0.50 per citizen annually for basic features, with premium + features available. +
          +
          + Perpetual license: One-time purchase with ongoing + maintenance fees (approximately 20% of license cost annually). Pricing is tailored to + government size and required functionality. +
          +
          + Open source community: Core components are available + under open source licenses for governments with technical expertise to self-implement and + maintain. +
          +
          + All options include initial implementation support, with additional consulting and + customization services available at standard rates.

          ), - category: 'economic' - } + category: 'economic', + }, ]; - - const filteredFaqs = activeCategory === 'all' - ? faqs - : faqs.filter(faq => faq.category === activeCategory); - + + const filteredFaqs = + activeCategory === 'all' ? faqs : faqs.filter((faq) => faq.category === activeCategory); + const categoryCount = { all: faqs.length, - general: faqs.filter(faq => faq.category === 'general').length, - technical: faqs.filter(faq => faq.category === 'technical').length, - implementation: faqs.filter(faq => faq.category === 'implementation').length, - economic: faqs.filter(faq => faq.category === 'economic').length, + general: faqs.filter((faq) => faq.category === 'general').length, + technical: faqs.filter((faq) => faq.category === 'technical').length, + implementation: faqs.filter((faq) => faq.category === 'implementation').length, + economic: faqs.filter((faq) => faq.category === 'economic').length, }; - + return (
          @@ -195,7 +256,7 @@ const FAQSection: React.FC = () => { Find answers to common questions about the Solon governance platform

          - + {/* Category Tabs */}
          - + {/* FAQ Items */}
          {filteredFaqs.map((faq) => ( -
          @@ -247,27 +308,37 @@ const FAQSection: React.FC = () => { className="w-full px-6 py-5 text-left focus:outline-none flex justify-between items-center" >

          {faq.question}

          - - - + + + {expandedId === faq.id && (
          -
          - {faq.answer} -
          +
          {faq.answer}
          )}
          ))}
          - + {/* Still Have Questions */}

          Still have questions?

          -

          Our team is ready to assist you with any inquiries about the Solon platform.

          +

          + Our team is ready to assist you with any inquiries about the Solon platform. +

          @@ -277,4 +348,4 @@ const FAQSection: React.FC = () => { ); }; -export default FAQSection; \ No newline at end of file +export default FAQSection; diff --git a/app/projects/governance/components/HeroSection.tsx b/app/projects/governance/components/HeroSection.tsx index 91a73e26c..c77e8b79f 100644 --- a/app/projects/governance/components/HeroSection.tsx +++ b/app/projects/governance/components/HeroSection.tsx @@ -16,8 +16,8 @@ const HeroSection: React.FC = () => { Solon — Decentralized Direct Democracy

          - Redefining governance through maximum transparency, direct citizen participation, - and market-based efficiency. A revolution in democratic systems for the 21st century. + Redefining governance through maximum transparency, direct citizen participation, and + market-based efficiency. A revolution in democratic systems for the 21st century.

          - +
          - - - + + +
          @@ -46,8 +61,18 @@ const HeroSection: React.FC = () => {
          - - + +
          @@ -57,8 +82,18 @@ const HeroSection: React.FC = () => {
          - - + +
          @@ -68,7 +103,7 @@ const HeroSection: React.FC = () => {
          - + {/* Visual content - replacing dark background with lighter design */}
          @@ -76,12 +111,14 @@ const HeroSection: React.FC = () => {

          Empowering citizens with transparent, decentralized decision-making

          - +
          {/* Key Principle 1: Transparency */}

          - 1 + + 1 + Transparency

            @@ -91,28 +128,34 @@ const HeroSection: React.FC = () => {
          • • All decision-making processes
          - + {/* Key Principle 2: Measurement */}

          - 2 + + 2 + Outcome Measurement

          - Clear metrics about the purpose, origin, and effectiveness of every law and regulation. + Clear metrics about the purpose, origin, and effectiveness of every law and + regulation.

          - + {/* Key Principle 3: Marketplace */}

          - 3 + + 3 + Government Function Marketplace

          - Government functions are first considered for elimination, automation, or - outsourcing to the private sector. Only when these options fail will the - government perform the function, with an open marketplace for competitive bidding. + Government functions are first considered for elimination, automation, or + outsourcing to the private sector. Only when these options fail will the + government perform the function, with an open marketplace for competitive + bidding.

          @@ -125,4 +168,4 @@ const HeroSection: React.FC = () => { ); }; -export default HeroSection; \ No newline at end of file +export default HeroSection; diff --git a/app/projects/governance/components/RoadmapSection.tsx b/app/projects/governance/components/RoadmapSection.tsx index 24b587d17..47e84a79b 100644 --- a/app/projects/governance/components/RoadmapSection.tsx +++ b/app/projects/governance/components/RoadmapSection.tsx @@ -11,28 +11,32 @@ const RoadmapSection: React.FC = () => { { phase: 'Phase 1', title: 'Core Platform Development', - description: 'Development of the basic transaction transparency system, law documentation framework, and secure authentication.', + description: + 'Development of the basic transaction transparency system, law documentation framework, and secure authentication.', timeline: 'Q1-Q2 2024', - current: true + current: true, }, { phase: 'Phase 2', title: 'Marketplace Development', - description: 'Building the bidding system for government functions, KPI tracking infrastructure, and contract management system.', - timeline: 'Q3-Q4 2024' + description: + 'Building the bidding system for government functions, KPI tracking infrastructure, and contract management system.', + timeline: 'Q3-Q4 2024', }, { phase: 'Phase 3', title: 'Advanced Features', - description: 'Integration of AI-powered analytics for corruption detection, predictive modeling, and existing government systems.', - timeline: 'Q1-Q2 2025' + description: + 'Integration of AI-powered analytics for corruption detection, predictive modeling, and existing government systems.', + timeline: 'Q1-Q2 2025', }, { phase: 'Phase 4', title: 'Scale and Expand', - description: 'Adding multi-language support, customization for different levels of government, and third-party API ecosystem.', - timeline: 'Q3 2025 onwards' - } + description: + 'Adding multi-language support, customization for different levels of government, and third-party API ecosystem.', + timeline: 'Q3 2025 onwards', + }, ]; return ( @@ -44,30 +48,28 @@ const RoadmapSection: React.FC = () => { Our strategic plan for bringing Solon to communities worldwide in four key phases.

          - +
          {/* Vertical Line */}
          - +
          {roadmapItems.map((item, index) => (
          {/* Circle indicator */} -
          - + {/* Content */}
          {item.phase} - - {item.timeline} - + {item.timeline} {item.current && ( Current @@ -81,7 +83,7 @@ const RoadmapSection: React.FC = () => { ))}
          - +
          @@ -95,51 +97,80 @@ const RoadmapSection: React.FC = () => {

          Adoption Metrics

          - Our implementation strategy focuses on measurable adoption metrics in partner communities: + Our implementation strategy focuses on measurable adoption metrics in partner + communities:

          - +

          Citizen Participation

          -

          Target: 40% of eligible citizens actively voting within first year

          +

          + Target: 40% of eligible citizens actively voting within first year +

          - +

          Cost Efficiency

          -

          Target: 15-20% reduction in administrative costs in first 2 years

          +

          + Target: 15-20% reduction in administrative costs in first 2 years +

          - +

          Trust in Government

          -

          Target: 60% approval rating within communities (up from average 35%)

          +

          + Target: 60% approval rating within communities (up from average 35%) +

          - +

          Law Effectiveness

          -

          Target: 75% of laws meeting defined KPIs by year 3

          +

          + Target: 75% of laws meeting defined KPIs by year 3 +

          @@ -151,4 +182,4 @@ const RoadmapSection: React.FC = () => { ); }; -export default RoadmapSection; \ No newline at end of file +export default RoadmapSection; diff --git a/app/projects/governance/components/TransactionDemo.tsx b/app/projects/governance/components/TransactionDemo.tsx index f3bb6f250..64eff7290 100644 --- a/app/projects/governance/components/TransactionDemo.tsx +++ b/app/projects/governance/components/TransactionDemo.tsx @@ -37,7 +37,8 @@ const TransactionDemo: React.FC = () => { agency: 'Department of Transportation', recipient: 'Urban Roads Inc.', purpose: 'Highway Repair - Section 14A', - details: 'Emergency repair of damaged highway section including asphalt replacement, line repainting, and drainage improvements.', + details: + 'Emergency repair of damaged highway section including asphalt replacement, line repainting, and drainage improvements.', likes: 87, dislikes: 21, comments: [ @@ -45,16 +46,16 @@ const TransactionDemo: React.FC = () => { id: 'c1', author: 'Jane Citizen', text: 'This repair was desperately needed. The potholes were damaging vehicles.', - timestamp: '2 days ago' + timestamp: '2 days ago', }, { id: 'c2', author: 'Mark Taxpayer', text: 'I drive on this section daily and can confirm the work has been completed.', - timestamp: '1 day ago' - } + timestamp: '1 day ago', + }, ], - shares: 18 + shares: 18, }, { id: 'TX-2023-06-10-014', @@ -63,7 +64,8 @@ const TransactionDemo: React.FC = () => { agency: 'Parks & Recreation', recipient: 'Green Spaces Landscaping', purpose: 'Community Park Maintenance', - details: 'Quarterly maintenance of Central Community Park including lawn care, tree pruning, playground equipment inspection, and irrigation system maintenance.', + details: + 'Quarterly maintenance of Central Community Park including lawn care, tree pruning, playground equipment inspection, and irrigation system maintenance.', likes: 134, dislikes: 8, comments: [ @@ -71,10 +73,10 @@ const TransactionDemo: React.FC = () => { id: 'c3', author: 'Parent Council', text: 'The playground looks much better now. Thank you!', - timestamp: '3 days ago' - } + timestamp: '3 days ago', + }, ], - shares: 42 + shares: 42, }, { id: 'TX-2023-06-05-078', @@ -83,7 +85,8 @@ const TransactionDemo: React.FC = () => { agency: 'Education Department', recipient: 'LearnTech Solutions', purpose: 'School District Technology Upgrade', - details: 'Purchase of 500 laptops, 50 interactive whiteboards, and supporting infrastructure for the Western School District technology modernization initiative.', + details: + 'Purchase of 500 laptops, 50 interactive whiteboards, and supporting infrastructure for the Western School District technology modernization initiative.', likes: 201, dislikes: 115, comments: [ @@ -91,23 +94,23 @@ const TransactionDemo: React.FC = () => { id: 'c4', author: 'Tech Teacher', text: 'Our school received the new equipment last week. Game changer for our STEM program!', - timestamp: '5 days ago' + timestamp: '5 days ago', }, { id: 'c5', author: 'Fiscal Watch', text: 'Why are we spending so much on technology that will be outdated in 3 years?', - timestamp: '4 days ago' + timestamp: '4 days ago', }, { id: 'c6', author: 'Education Board', text: 'These devices come with a 5-year support contract and are critical for student success.', - timestamp: '3 days ago' - } + timestamp: '3 days ago', + }, ], - shares: 67 - } + shares: 67, + }, ]); // State for comment input @@ -116,17 +119,17 @@ const TransactionDemo: React.FC = () => { // Handle user actions (like, dislike, share) const handleAction = (id: string, action: 'like' | 'dislike' | 'share') => { - setTransactions(prev => - prev.map(tx => - tx.id === id + setTransactions((prev) => + prev.map((tx) => + tx.id === id ? { ...tx, likes: action === 'like' ? tx.likes + 1 : tx.likes, dislikes: action === 'dislike' ? tx.dislikes + 1 : tx.dislikes, - shares: action === 'share' ? tx.shares + 1 : tx.shares + shares: action === 'share' ? tx.shares + 1 : tx.shares, } - : tx - ) + : tx, + ), ); }; @@ -138,47 +141,49 @@ const TransactionDemo: React.FC = () => { id: `c${Math.random().toString(36).substr(2, 9)}`, author: 'You', text: commentInputs[id], - timestamp: 'Just now' + timestamp: 'Just now', }; - setTransactions(prev => - prev.map(tx => - tx.id === id - ? { ...tx, comments: [...tx.comments, newComment] } - : tx - ) + setTransactions((prev) => + prev.map((tx) => (tx.id === id ? { ...tx, comments: [...tx.comments, newComment] } : tx)), ); // Reset input - setCommentInputs(prev => ({ ...prev, [id]: '' })); + setCommentInputs((prev) => ({ ...prev, [id]: '' })); }; // Toggle comments visibility const toggleComments = (id: string) => { - setShowComments(prev => ({ + setShowComments((prev) => ({ ...prev, - [id]: !prev[id] + [id]: !prev[id], })); }; // Handle donation const handleDonate = (id: string, target: 'agency' | 'recipient') => { - const transaction = transactions.find(tx => tx.id === id); + const transaction = transactions.find((tx) => tx.id === id); if (!transaction) return; const entity = target === 'agency' ? transaction.agency : transaction.recipient; - alert(`Thank you for supporting ${entity}! This would open a donation form in a real implementation.`); + alert( + `Thank you for supporting ${entity}! This would open a donation form in a real implementation.`, + ); }; return (

          Open Payments Explorer

          - Explore and engage with government transactions. Your participation helps ensure transparency and accountability. + Explore and engage with government transactions. Your participation helps ensure + transparency and accountability.

          - - {transactions.map(transaction => ( -
          + + {transactions.map((transaction) => ( +
          {/* Transaction header */}
          @@ -194,7 +199,7 @@ const TransactionDemo: React.FC = () => {
          - + {/* Transaction details */}
          @@ -203,79 +208,121 @@ const TransactionDemo: React.FC = () => { From: {transaction.agency}
          -
          - +
          To: {transaction.recipient}
          -
          - -

          - {transaction.details} -

          + +

          {transaction.details}

          - + {/* Action buttons */}
          - - - - - - -
          - + {/* Comments section */} {showComments[transaction.id] && (

          Comments

          - + {/* Comment list */}
          - {transaction.comments.map(comment => ( + {transaction.comments.map((comment) => (
          {comment.author} @@ -301,13 +348,15 @@ const TransactionDemo: React.FC = () => {
          ))}
          - + {/* Comment input */}
          setCommentInputs(prev => ({ ...prev, [transaction.id]: e.target.value }))} + onChange={(e) => + setCommentInputs((prev) => ({ ...prev, [transaction.id]: e.target.value })) + } className="flex-1 min-w-0 block w-full px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" placeholder="Add your comment..." onKeyPress={(e) => e.key === 'Enter' && handleCommentSubmit(transaction.id)} @@ -328,4 +377,4 @@ const TransactionDemo: React.FC = () => { ); }; -export default TransactionDemo; \ No newline at end of file +export default TransactionDemo; diff --git a/app/projects/governance/components/TransactionWithTraceability.tsx b/app/projects/governance/components/TransactionWithTraceability.tsx index 0521259c0..7d2402688 100644 --- a/app/projects/governance/components/TransactionWithTraceability.tsx +++ b/app/projects/governance/components/TransactionWithTraceability.tsx @@ -50,8 +50,12 @@ interface TransactionWithTraceabilityProps { /** * Displays a transaction with full traceability to laws and agency information */ -export const TransactionWithTraceability: React.FC = ({ transaction }) => { - const [activeTab, setActiveTab] = useState<'details' | 'laws' | 'documents' | 'timeline'>('details'); +export const TransactionWithTraceability: React.FC = ({ + transaction, +}) => { + const [activeTab, setActiveTab] = useState<'details' | 'laws' | 'documents' | 'timeline'>( + 'details', + ); const [expanded, setExpanded] = useState(false); return ( @@ -68,12 +72,15 @@ export const TransactionWithTraceability: React.FC
          - {transaction.status} @@ -84,7 +91,7 @@ export const TransactionWithTraceability: React.FC
          - + {/* Transaction Summary */}
          @@ -92,8 +99,8 @@ export const TransactionWithTraceability: React.FCDepartment @@ -107,38 +114,78 @@ export const TransactionWithTraceability: React.FC Transparency Score
          -
          = 90 ? 'bg-green-500' : - transaction.transparencyScore >= 70 ? 'bg-blue-500' : - transaction.transparencyScore >= 50 ? 'bg-yellow-500' : - 'bg-red-500' - }`}> +
          = 90 + ? 'bg-green-500' + : transaction.transparencyScore >= 70 + ? 'bg-blue-500' + : transaction.transparencyScore >= 50 + ? 'bg-yellow-500' + : 'bg-red-500' + }`} + >
          - {transaction.transparencyScore}/100 + + {transaction.transparencyScore}/100 +
          Social Engagement
          @@ -146,7 +193,7 @@ export const TransactionWithTraceability: React.FC
          - + {/* Tab Navigation */}
          - + {/* Tab Content */}
          {/* Details Tab */} @@ -204,7 +251,9 @@ export const TransactionWithTraceability: React.FC
          Cost Per Unit
          -
          {transaction.metrics.costPerUnit}
          +
          + {transaction.metrics.costPerUnit} +
          Timeline Status
          @@ -212,20 +261,26 @@ export const TransactionWithTraceability: React.FC
          Quality Score
          -
          {transaction.metrics.qualityScore}/100
          +
          + {transaction.metrics.qualityScore}/100 +
          Contract Compliance
          -
          {transaction.metrics.contractCompliance}%
          +
          + {transaction.metrics.contractCompliance}% +
          - +

          Public Engagement

          - +