Sync: IrisTEA - Plataforma de Chás Premium
This commit is contained in:
8
.env.production
Normal file
8
.env.production
Normal file
@@ -0,0 +1,8 @@
|
||||
DATABASE_URL="file:./prod.db"
|
||||
AUTH_SECRET="iristea-prod-secret-2026-kislanski"
|
||||
AUTH_URL="https://iristea.com.br"
|
||||
NEXT_PUBLIC_APP_URL="https://iristea.com.br"
|
||||
STRIPE_SECRET_KEY="sk_test_xxx"
|
||||
STRIPE_PUBLISHABLE_KEY="pk_test_xxx"
|
||||
STRIPE_WEBHOOK_SECRET="whsec_xxx"
|
||||
DAILY_API_KEY="xxx"
|
||||
50
.gitignore
vendored
50
.gitignore
vendored
@@ -1,43 +1,11 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
node_modules/
|
||||
.next/
|
||||
dist/
|
||||
.env
|
||||
.env.local
|
||||
.env*.local
|
||||
*.log
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
coverage/
|
||||
.turbo/
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
/src/generated/prisma
|
||||
|
||||
6
next-env.d.ts
vendored
Normal file
6
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
612
package-lock.json
generated
612
package-lock.json
generated
@@ -15,12 +15,15 @@
|
||||
"bcrypt": "^6.0.0",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
"framer-motion": "^12.33.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^4.1.0",
|
||||
"lucide-react": "^0.563.0",
|
||||
"next": "16.1.6",
|
||||
"next-auth": "^5.0.0-beta.30",
|
||||
"prisma": "^6.19.2",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"recharts": "^3.7.0",
|
||||
"stripe": "^20.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1393,6 +1396,42 @@
|
||||
"@prisma/debug": "6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
|
||||
"integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@standard-schema/utils": "^0.3.0",
|
||||
"immer": "^11.0.0",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"reselect": "^5.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
||||
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit/node_modules/immer": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz",
|
||||
"integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||
@@ -1481,6 +1520,12 @@
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@standard-schema/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-8.7.0.tgz",
|
||||
@@ -1798,6 +1843,69 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
|
||||
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
|
||||
"integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -1829,11 +1937,24 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pako": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
|
||||
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/raf": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz",
|
||||
"integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
@@ -1849,6 +1970,19 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.54.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz",
|
||||
@@ -2680,6 +2814,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.9.19",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
|
||||
@@ -2885,6 +3028,26 @@
|
||||
"url": "https://www.paypal.me/kirilvatev"
|
||||
}
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/raf": "^3.4.0",
|
||||
"core-js": "^3.8.3",
|
||||
"raf": "^3.4.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rgbcolor": "^1.0.1",
|
||||
"stackblur-canvas": "^2.0.0",
|
||||
"svg-pathdata": "^6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -2932,6 +3095,15 @@
|
||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -2981,6 +3153,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.48.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
|
||||
"integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -2996,13 +3180,143 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
|
||||
"integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||
@@ -3082,6 +3396,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -3178,6 +3498,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optional": true,
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
@@ -3429,6 +3759,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/es-toolkit": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz",
|
||||
"integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"docs",
|
||||
"benchmarks"
|
||||
]
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
@@ -3876,6 +4216,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
@@ -3964,6 +4310,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-png": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
|
||||
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/pako": "^2.0.3",
|
||||
"iobuffer": "^5.3.2",
|
||||
"pako": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
|
||||
@@ -3974,6 +4331,12 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -4403,6 +4766,19 @@
|
||||
"hermes-estree": "0.25.1"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -4413,6 +4789,16 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
|
||||
"integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
@@ -4455,6 +4841,21 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/iobuffer": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
|
||||
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -4987,6 +5388,23 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.1.0.tgz",
|
||||
"integrity": "sha512-xd1d/XRkwqnsq6FP3zH1Q+Ejqn2ULIJeDZ+FTKpaabVpZREjsJKRJwuokTNgdqOU+fl55KgbvgZ1pRTSWCP2kQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"fast-png": "^6.2.0",
|
||||
"fflate": "^0.8.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvg": "^3.0.11",
|
||||
"core-js": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"html2canvas": "^1.0.0-rc.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jsx-ast-utils": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||
@@ -5892,6 +6310,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -5944,6 +6368,13 @@
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -6126,6 +6557,16 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rc9": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
|
||||
@@ -6161,9 +6602,31 @@
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.2.25 || ^19",
|
||||
"react": "^18.0 || ^19",
|
||||
"redux": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
@@ -6177,6 +6640,51 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz",
|
||||
"integrity": "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"www"
|
||||
],
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "1.x.x || 2.x.x",
|
||||
"clsx": "^2.1.1",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"es-toolkit": "^1.39.3",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"immer": "^10.1.1",
|
||||
"react-redux": "8.x.x || 9.x.x",
|
||||
"reselect": "5.1.1",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"use-sync-external-store": "^1.2.2",
|
||||
"victory-vendor": "^37.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
|
||||
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"redux": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||
@@ -6200,6 +6708,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||
@@ -6221,6 +6736,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.11",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||
@@ -6273,6 +6794,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rgbcolor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.15"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -6590,6 +7121,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stackblur-canvas": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/stop-iteration-iterator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||
@@ -6806,6 +7347,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/svg-pathdata": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
|
||||
@@ -6827,6 +7378,21 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
@@ -7173,6 +7739,46 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "37.3.6",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
|
||||
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
"bcrypt": "^6.0.0",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
"framer-motion": "^12.33.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^4.1.0",
|
||||
"lucide-react": "^0.563.0",
|
||||
"next": "16.1.6",
|
||||
"next-auth": "^5.0.0-beta.30",
|
||||
"prisma": "^6.19.2",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"recharts": "^3.7.0",
|
||||
"stripe": "^20.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { auth } from '@/lib/auth';
|
||||
import { stripe, PLANS, PlanId } from '@/lib/stripe';
|
||||
import { stripe, PLANS, PlanType } from '@/lib/stripe';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
@@ -9,22 +9,24 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Não autenticado' },
|
||||
{ error: 'Não autorizado' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { planId } = body as { planId: PlanId };
|
||||
const { plan } = body as { plan: PlanType };
|
||||
|
||||
if (!planId || !PLANS[planId]) {
|
||||
if (!plan || !PLANS[plan]) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Plano inválido' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const plan = PLANS[planId];
|
||||
const selectedPlan = PLANS[plan];
|
||||
|
||||
// Buscar ou criar customer no Stripe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
include: { subscription: true },
|
||||
@@ -37,7 +39,6 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Criar ou recuperar customer no Stripe
|
||||
let customerId = user.subscription?.stripeCustomerId;
|
||||
|
||||
if (!customerId) {
|
||||
@@ -51,31 +52,26 @@ export async function POST(request: NextRequest) {
|
||||
customerId = customer.id;
|
||||
|
||||
// Atualizar subscription com customerId
|
||||
await prisma.subscription.upsert({
|
||||
await prisma.subscription.update({
|
||||
where: { userId: user.id },
|
||||
update: { stripeCustomerId: customerId },
|
||||
create: {
|
||||
userId: user.id,
|
||||
plan: planId,
|
||||
status: 'pending',
|
||||
stripeCustomerId: customerId,
|
||||
},
|
||||
data: { stripeCustomerId: customerId },
|
||||
});
|
||||
}
|
||||
|
||||
// Criar sessão de checkout
|
||||
const checkoutSession = await stripe.checkout.sessions.create({
|
||||
customer: customerId,
|
||||
mode: 'subscription',
|
||||
payment_method_types: ['card'],
|
||||
line_items: [
|
||||
{
|
||||
price_data: {
|
||||
currency: 'brl',
|
||||
product_data: {
|
||||
name: `Íris TEA - Plano ${plan.name}`,
|
||||
description: plan.description,
|
||||
name: `IrisTEA - Plano ${selectedPlan.name}`,
|
||||
description: selectedPlan.features.join(' • '),
|
||||
},
|
||||
unit_amount: plan.price,
|
||||
unit_amount: selectedPlan.price,
|
||||
recurring: {
|
||||
interval: 'month',
|
||||
},
|
||||
@@ -83,12 +79,11 @@ export async function POST(request: NextRequest) {
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
mode: 'subscription',
|
||||
success_url: `${process.env.AUTH_URL}/checkout/sucesso?session_id={CHECKOUT_SESSION_ID}`,
|
||||
cancel_url: `${process.env.AUTH_URL}/checkout/cancelado`,
|
||||
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?success=true`,
|
||||
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/planos?canceled=true`,
|
||||
metadata: {
|
||||
userId: user.id,
|
||||
planId,
|
||||
plan: plan,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -96,7 +91,7 @@ export async function POST(request: NextRequest) {
|
||||
} catch (error) {
|
||||
console.error('Erro no checkout:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erro ao criar sessão de checkout' },
|
||||
{ error: 'Erro ao criar sessão de pagamento' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
129
src/app/api/progress/route.ts
Normal file
129
src/app/api/progress/route.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { auth } from '@/lib/auth';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
// Buscar progresso das crianças
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth();
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Não autenticado' }, { status: 401 });
|
||||
}
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const childId = searchParams.get('childId');
|
||||
const days = parseInt(searchParams.get('days') || '30');
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
include: { children: true },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Usuário não encontrado' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Verificar permissão
|
||||
let targetChildId = childId;
|
||||
if (!targetChildId && user.children.length > 0) {
|
||||
targetChildId = user.children[0].id;
|
||||
}
|
||||
|
||||
if (user.role === 'parent' && targetChildId) {
|
||||
const isParent = user.children.some(c => c.id === targetChildId);
|
||||
if (!isParent) {
|
||||
return NextResponse.json({ error: 'Sem permissão' }, { status: 403 });
|
||||
}
|
||||
}
|
||||
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - days);
|
||||
|
||||
const progress = await prisma.progress.findMany({
|
||||
where: {
|
||||
childId: targetChildId || undefined,
|
||||
date: { gte: startDate },
|
||||
},
|
||||
orderBy: { date: 'asc' },
|
||||
});
|
||||
|
||||
// Agrupar por categoria e calcular evolução
|
||||
const categories = ['comunicacao', 'habilidades_sociais', 'autonomia', 'regulacao_emocional'];
|
||||
const progressByCategory: Record<string, { dates: string[]; values: number[] }> = {};
|
||||
|
||||
categories.forEach(cat => {
|
||||
const catProgress = progress.filter(p => p.category === cat);
|
||||
progressByCategory[cat] = {
|
||||
dates: catProgress.map(p => p.date.toISOString().split('T')[0]),
|
||||
values: catProgress.map(p => p.value),
|
||||
};
|
||||
});
|
||||
|
||||
// Calcular último valor de cada categoria
|
||||
const currentProgress = categories.map(cat => {
|
||||
const catProgress = progress.filter(p => p.category === cat);
|
||||
const lastValue = catProgress.length > 0 ? catProgress[catProgress.length - 1].value : 0;
|
||||
return { category: cat, value: lastValue };
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
progressByCategory,
|
||||
currentProgress,
|
||||
child: user.children.find(c => c.id === targetChildId),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar progresso:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erro ao buscar progresso' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Registrar progresso (terapeuta)
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth();
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Não autenticado' }, { status: 401 });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
});
|
||||
|
||||
if (!user || (user.role !== 'therapist' && user.role !== 'admin')) {
|
||||
return NextResponse.json({ error: 'Sem permissão' }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { childId, category, value, notes } = body;
|
||||
|
||||
if (!childId || !category || value === undefined) {
|
||||
return NextResponse.json(
|
||||
{ error: 'childId, category e value são obrigatórios' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const progress = await prisma.progress.create({
|
||||
data: {
|
||||
childId,
|
||||
category,
|
||||
value: Math.min(100, Math.max(0, value)), // Clamp 0-100
|
||||
notes,
|
||||
date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(progress);
|
||||
} catch (error) {
|
||||
console.error('Erro ao registrar progresso:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erro ao registrar progresso' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
321
src/app/dashboard/relatorios/page.tsx
Normal file
321
src/app/dashboard/relatorios/page.tsx
Normal file
@@ -0,0 +1,321 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import {
|
||||
ArrowLeft, FileText, Download, Calendar, TrendingUp,
|
||||
BarChart3, PieChart, Loader2
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend,
|
||||
ResponsiveContainer, RadarChart, PolarGrid, PolarAngleAxis,
|
||||
PolarRadiusAxis, Radar, AreaChart, Area
|
||||
} from 'recharts';
|
||||
|
||||
interface ProgressData {
|
||||
progressByCategory: Record<string, { dates: string[]; values: number[] }>;
|
||||
currentProgress: { category: string; value: number }[];
|
||||
child?: { id: string; name: string };
|
||||
}
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
comunicacao: 'Comunicação',
|
||||
habilidades_sociais: 'Habilidades Sociais',
|
||||
autonomia: 'Autonomia',
|
||||
regulacao_emocional: 'Regulação Emocional',
|
||||
};
|
||||
|
||||
const categoryColors: Record<string, string> = {
|
||||
comunicacao: '#3b82f6',
|
||||
habilidades_sociais: '#22c55e',
|
||||
autonomia: '#eab308',
|
||||
regulacao_emocional: '#a855f7',
|
||||
};
|
||||
|
||||
export default function RelatoriosPage() {
|
||||
const { data: session } = useSession();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [data, setData] = useState<ProgressData | null>(null);
|
||||
const [period, setPeriod] = useState<'7' | '30' | '90'>('30');
|
||||
|
||||
useEffect(() => {
|
||||
fetchProgress();
|
||||
}, [period]);
|
||||
|
||||
const fetchProgress = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch(`/api/progress?days=${period}`);
|
||||
if (response.ok) {
|
||||
const progressData = await response.json();
|
||||
setData(progressData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar progresso:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const generatePDF = async () => {
|
||||
// Importar dinamicamente para evitar SSR issues
|
||||
const html2canvas = (await import('html2canvas')).default;
|
||||
const { jsPDF } = await import('jspdf');
|
||||
|
||||
const content = document.getElementById('report-content');
|
||||
if (!content) return;
|
||||
|
||||
const canvas = await html2canvas(content, { scale: 2 });
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
|
||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
const imgWidth = 210;
|
||||
const pageHeight = 295;
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
let heightLeft = imgHeight;
|
||||
let position = 0;
|
||||
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
while (heightLeft >= 0) {
|
||||
position = heightLeft - imgHeight;
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
pdf.save(`relatorio-iris-${new Date().toISOString().split('T')[0]}.pdf`);
|
||||
};
|
||||
|
||||
// Preparar dados para gráficos
|
||||
const prepareLineChartData = () => {
|
||||
if (!data) return [];
|
||||
|
||||
const allDates = new Set<string>();
|
||||
Object.values(data.progressByCategory).forEach(cat => {
|
||||
cat.dates.forEach(d => allDates.add(d));
|
||||
});
|
||||
|
||||
const sortedDates = Array.from(allDates).sort();
|
||||
|
||||
return sortedDates.map(date => {
|
||||
const point: Record<string, any> = { date: date.slice(5) }; // MM-DD format
|
||||
|
||||
Object.entries(data.progressByCategory).forEach(([cat, catData]) => {
|
||||
const idx = catData.dates.indexOf(date);
|
||||
point[cat] = idx !== -1 ? catData.values[idx] : null;
|
||||
});
|
||||
|
||||
return point;
|
||||
});
|
||||
};
|
||||
|
||||
const prepareRadarData = () => {
|
||||
if (!data) return [];
|
||||
|
||||
return data.currentProgress.map(p => ({
|
||||
category: categoryLabels[p.category] || p.category,
|
||||
value: p.value,
|
||||
fullMark: 100,
|
||||
}));
|
||||
};
|
||||
|
||||
const lineChartData = prepareLineChartData();
|
||||
const radarData = prepareRadarData();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white border-b border-gray-200 px-4 lg:px-8 py-4">
|
||||
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/dashboard" className="text-gray-400 hover:text-gray-600">
|
||||
<ArrowLeft className="w-6 h-6" />
|
||||
</Link>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-indigo-100 text-indigo-600 flex items-center justify-center">
|
||||
<FileText className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-gray-900">Relatórios</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
{data?.child ? `Progresso de ${data.child.name}` : 'Acompanhamento de progresso'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Period selector */}
|
||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
||||
{[
|
||||
{ value: '7', label: '7 dias' },
|
||||
{ value: '30', label: '30 dias' },
|
||||
{ value: '90', label: '90 dias' },
|
||||
].map(p => (
|
||||
<button
|
||||
key={p.value}
|
||||
onClick={() => setPeriod(p.value as any)}
|
||||
className={`px-3 py-1.5 text-sm rounded-md transition ${
|
||||
period === p.value
|
||||
? 'bg-white shadow text-indigo-600'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{p.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={generatePDF}
|
||||
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-indigo-700 transition"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Exportar PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="max-w-7xl mx-auto p-4 lg:p-8" id="report-content">
|
||||
{loading ? (
|
||||
<div className="flex justify-center py-20">
|
||||
<Loader2 className="w-12 h-12 text-indigo-500 animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{/* Current Progress Cards */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{data?.currentProgress.map(p => (
|
||||
<div key={p.category} className="bg-white rounded-xl shadow-sm p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-sm font-medium text-gray-500">
|
||||
{categoryLabels[p.category]}
|
||||
</span>
|
||||
<TrendingUp
|
||||
className="w-5 h-5"
|
||||
style={{ color: categoryColors[p.category] }}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-3xl font-bold text-gray-900 mb-2">
|
||||
{p.value}%
|
||||
</div>
|
||||
<div className="h-2 bg-gray-200 rounded-full">
|
||||
<div
|
||||
className="h-2 rounded-full transition-all"
|
||||
style={{
|
||||
width: `${p.value}%`,
|
||||
backgroundColor: categoryColors[p.category],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Charts */}
|
||||
<div className="grid lg:grid-cols-2 gap-8">
|
||||
{/* Line Chart */}
|
||||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2">
|
||||
<BarChart3 className="w-5 h-5 text-indigo-600" />
|
||||
Evolução ao Longo do Tempo
|
||||
</h3>
|
||||
|
||||
{lineChartData.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<AreaChart data={lineChartData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis domain={[0, 100]} />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
{Object.keys(categoryLabels).map(cat => (
|
||||
<Area
|
||||
key={cat}
|
||||
type="monotone"
|
||||
dataKey={cat}
|
||||
name={categoryLabels[cat]}
|
||||
stroke={categoryColors[cat]}
|
||||
fill={categoryColors[cat]}
|
||||
fillOpacity={0.1}
|
||||
connectNulls
|
||||
/>
|
||||
))}
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className="h-[300px] flex items-center justify-center text-gray-500">
|
||||
Sem dados para o período selecionado
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Radar Chart */}
|
||||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2">
|
||||
<PieChart className="w-5 h-5 text-indigo-600" />
|
||||
Visão Geral das Áreas
|
||||
</h3>
|
||||
|
||||
{radarData.length > 0 ? (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<RadarChart data={radarData}>
|
||||
<PolarGrid />
|
||||
<PolarAngleAxis dataKey="category" />
|
||||
<PolarRadiusAxis domain={[0, 100]} />
|
||||
<Radar
|
||||
name="Progresso"
|
||||
dataKey="value"
|
||||
stroke="#6366f1"
|
||||
fill="#6366f1"
|
||||
fillOpacity={0.5}
|
||||
/>
|
||||
</RadarChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className="h-[300px] flex items-center justify-center text-gray-500">
|
||||
Sem dados para exibir
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Summary */}
|
||||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Resumo do Período
|
||||
</h3>
|
||||
<div className="prose prose-sm max-w-none text-gray-600">
|
||||
<p>
|
||||
Nos últimos <strong>{period} dias</strong>, observamos progresso em todas as áreas de desenvolvimento.
|
||||
</p>
|
||||
{data?.currentProgress && data.currentProgress.length > 0 && (
|
||||
<>
|
||||
<p>
|
||||
A área com melhor desempenho é <strong>
|
||||
{categoryLabels[data.currentProgress.reduce((a, b) =>
|
||||
a.value > b.value ? a : b
|
||||
).category]}
|
||||
</strong> com {Math.max(...data.currentProgress.map(p => p.value))}% de progresso.
|
||||
</p>
|
||||
<p>
|
||||
Recomendamos focar em <strong>
|
||||
{categoryLabels[data.currentProgress.reduce((a, b) =>
|
||||
a.value < b.value ? a : b
|
||||
).category]}
|
||||
</strong> nas próximas sessões para equilibrar o desenvolvimento.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--iris-purple: #6366f1;
|
||||
--iris-purple-dark: #4f46e5;
|
||||
--iris-blue: #3b82f6;
|
||||
--iris-green: #10b981;
|
||||
--iris-yellow: #f59e0b;
|
||||
--iris-red: #ef4444;
|
||||
--iris-rainbow: linear-gradient(90deg, #ef4444, #f59e0b, #eab308, #22c55e, #3b82f6, #8b5cf6);
|
||||
@theme {
|
||||
--color-iris-purple: #6366f1;
|
||||
--color-iris-purple-dark: #4f46e5;
|
||||
--color-iris-blue: #3b82f6;
|
||||
--color-iris-green: #10b981;
|
||||
--color-iris-yellow: #f59e0b;
|
||||
--color-iris-red: #ef4444;
|
||||
}
|
||||
|
||||
@source "../**/*.{js,ts,jsx,tsx,mdx}";
|
||||
|
||||
body {
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
.gradient-rainbow {
|
||||
background: var(--iris-rainbow);
|
||||
background: linear-gradient(90deg, #ef4444, #f59e0b, #eab308, #22c55e, #3b82f6, #8b5cf6);
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
@@ -33,25 +34,3 @@ body {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chat-bubble::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 20px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 10px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.chat-bubble-right::after {
|
||||
left: auto;
|
||||
right: 20px;
|
||||
border-top-color: #6366f1;
|
||||
}
|
||||
|
||||
132
src/app/lancamento/page.tsx
Normal file
132
src/app/lancamento/page.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Sparkles, Mail, ArrowRight, Check, Heart, Brain, Users, Calendar } from 'lucide-react';
|
||||
|
||||
export default function LancamentoPage() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setStatus('loading');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
setStatus('success');
|
||||
setEmail('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-indigo-900 via-purple-900 to-indigo-800 text-white">
|
||||
<div className="max-w-4xl mx-auto px-4 py-16 min-h-screen flex flex-col justify-center">
|
||||
{/* Logo */}
|
||||
<div className="text-center mb-12">
|
||||
<div className="inline-flex items-center gap-3 mb-6">
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-pink-500 via-purple-500 to-indigo-500 flex items-center justify-center shadow-2xl">
|
||||
<Sparkles className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
<span className="text-5xl font-bold">IrisTEA</span>
|
||||
</div>
|
||||
|
||||
<div className="inline-block px-4 py-2 bg-white/10 rounded-full text-sm font-medium mb-8">
|
||||
🚀 Em breve
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 leading-tight">
|
||||
Terapia ABA acessível<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-yellow-400">
|
||||
para seu filho com autismo
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-xl text-white/80 max-w-2xl mx-auto mb-12">
|
||||
Combinamos inteligência artificial com terapeutas BCBA certificados
|
||||
para oferecer tratamento de qualidade por uma fração do preço.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12">
|
||||
<div className="text-center p-4 bg-white/5 rounded-xl backdrop-blur">
|
||||
<Brain className="w-8 h-8 mx-auto mb-2 text-pink-400" />
|
||||
<div className="font-semibold">IA 24/7</div>
|
||||
<div className="text-sm text-white/60">Suporte contínuo</div>
|
||||
</div>
|
||||
<div className="text-center p-4 bg-white/5 rounded-xl backdrop-blur">
|
||||
<Users className="w-8 h-8 mx-auto mb-2 text-pink-400" />
|
||||
<div className="font-semibold">BCBAs</div>
|
||||
<div className="text-sm text-white/60">Certificados</div>
|
||||
</div>
|
||||
<div className="text-center p-4 bg-white/5 rounded-xl backdrop-blur">
|
||||
<Calendar className="w-8 h-8 mx-auto mb-2 text-pink-400" />
|
||||
<div className="font-semibold">Flexível</div>
|
||||
<div className="text-sm text-white/60">Sua agenda</div>
|
||||
</div>
|
||||
<div className="text-center p-4 bg-white/5 rounded-xl backdrop-blur">
|
||||
<Heart className="w-8 h-8 mx-auto mb-2 text-pink-400" />
|
||||
<div className="font-semibold">R$297</div>
|
||||
<div className="text-sm text-white/60">A partir de</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Waitlist Form */}
|
||||
<div className="max-w-md mx-auto w-full">
|
||||
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 shadow-2xl">
|
||||
<h2 className="text-2xl font-bold text-center mb-2">
|
||||
Entre na lista de espera
|
||||
</h2>
|
||||
<p className="text-white/70 text-center mb-6">
|
||||
Seja o primeiro a saber quando lançarmos
|
||||
</p>
|
||||
|
||||
{status === 'success' ? (
|
||||
<div className="text-center py-4">
|
||||
<div className="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Check className="w-8 h-8" />
|
||||
</div>
|
||||
<p className="text-xl font-semibold">Você está na lista! 🎉</p>
|
||||
<p className="text-white/70 mt-2">Avisaremos quando lançarmos.</p>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="relative">
|
||||
<Mail className="w-5 h-5 text-white/50 absolute left-4 top-1/2 -translate-y-1/2" />
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Seu melhor e-mail"
|
||||
required
|
||||
className="w-full pl-12 pr-4 py-4 bg-white/10 border border-white/20 rounded-xl focus:ring-2 focus:ring-pink-500 focus:border-transparent outline-none text-white placeholder-white/50"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === 'loading'}
|
||||
className="w-full bg-gradient-to-r from-pink-500 to-purple-600 py-4 rounded-xl font-semibold flex items-center justify-center gap-2 hover:from-pink-600 hover:to-purple-700 transition disabled:opacity-50"
|
||||
>
|
||||
{status === 'loading' ? (
|
||||
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Quero ser avisado
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-center text-white/50 text-sm mt-6">
|
||||
+500 famílias já na lista de espera
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="text-center mt-16 text-white/50 text-sm">
|
||||
© 2026 IrisTEA · Terapia ABA Inteligente
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
208
src/app/planos/page.tsx
Normal file
208
src/app/planos/page.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, Suspense } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Sparkles, Check, ArrowRight, AlertCircle } from 'lucide-react';
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: 'essencial',
|
||||
name: 'Essencial',
|
||||
price: 297,
|
||||
sessions: 1,
|
||||
features: [
|
||||
'IA ilimitada 24/7',
|
||||
'1 sessão BCBA/mês',
|
||||
'Dashboard de progresso',
|
||||
'Relatórios automáticos',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'completo',
|
||||
name: 'Completo',
|
||||
price: 497,
|
||||
sessions: 2,
|
||||
popular: true,
|
||||
features: [
|
||||
'Tudo do Essencial',
|
||||
'2 sessões BCBA/mês',
|
||||
'Grupo de pais',
|
||||
'Prioridade no suporte',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'intensivo',
|
||||
name: 'Intensivo',
|
||||
price: 897,
|
||||
sessions: 4,
|
||||
features: [
|
||||
'Tudo do Completo',
|
||||
'4 sessões BCBA/mês',
|
||||
'Equipe multidisciplinar',
|
||||
'Plano personalizado',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function PlanosContent() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const canceled = searchParams.get('canceled');
|
||||
const [loading, setLoading] = useState<string | null>(null);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleSelectPlan = async (planId: string) => {
|
||||
setLoading(planId);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/checkout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ plan: planId }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
router.push('/login?redirect=/planos');
|
||||
return;
|
||||
}
|
||||
throw new Error(data.error || 'Erro ao processar');
|
||||
}
|
||||
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Erro ao iniciar pagamento. Tente novamente.');
|
||||
setLoading(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{canceled && (
|
||||
<div className="max-w-md mx-auto mb-8 p-4 bg-yellow-50 border border-yellow-200 rounded-xl text-yellow-800 flex items-center gap-3">
|
||||
<AlertCircle className="w-5 h-5 flex-shrink-0" />
|
||||
<p>Pagamento cancelado. Escolha um plano para continuar.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="max-w-md mx-auto mb-8 p-4 bg-red-50 border border-red-200 rounded-xl text-red-700 text-center">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Plans Grid */}
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{plans.map((plan) => (
|
||||
<div
|
||||
key={plan.id}
|
||||
className={`relative bg-white rounded-2xl shadow-xl p-8 ${
|
||||
plan.popular ? 'ring-2 ring-indigo-600' : ''
|
||||
}`}
|
||||
>
|
||||
{plan.popular && (
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-indigo-600 text-white px-4 py-1 rounded-full text-sm font-medium">
|
||||
Mais Popular
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
{plan.name}
|
||||
</h3>
|
||||
<div className="flex items-baseline justify-center gap-1">
|
||||
<span className="text-4xl font-bold text-indigo-600">
|
||||
R$ {plan.price}
|
||||
</span>
|
||||
<span className="text-gray-500">/mês</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
{plan.sessions} {plan.sessions === 1 ? 'sessão' : 'sessões'} BCBA/mês
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-4 mb-8">
|
||||
{plan.features.map((feature, idx) => (
|
||||
<li key={idx} className="flex items-center gap-3">
|
||||
<div className="w-5 h-5 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0">
|
||||
<Check className="w-3 h-3 text-green-600" />
|
||||
</div>
|
||||
<span className="text-gray-700">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
onClick={() => handleSelectPlan(plan.id)}
|
||||
disabled={loading !== null}
|
||||
className={`w-full py-3 rounded-xl font-medium flex items-center justify-center gap-2 transition ${
|
||||
plan.popular
|
||||
? 'bg-indigo-600 text-white hover:bg-indigo-700'
|
||||
: 'bg-gray-100 text-gray-900 hover:bg-gray-200'
|
||||
} disabled:opacity-50`}
|
||||
>
|
||||
{loading === plan.id ? (
|
||||
<div className="w-5 h-5 border-2 border-current/30 border-t-current rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Assinar agora
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PlanosPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-indigo-50 to-white">
|
||||
{/* Header */}
|
||||
<header className="bg-white border-b border-gray-100">
|
||||
<div className="max-w-6xl mx-auto px-4 py-4">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="w-10 h-10 rounded-xl gradient-rainbow flex items-center justify-center">
|
||||
<Sparkles className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<span className="text-2xl font-bold text-gradient">Íris</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content */}
|
||||
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||
Escolha seu plano
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600">
|
||||
Comece a transformar o desenvolvimento do seu filho hoje
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Suspense fallback={<div className="text-center">Carregando...</div>}>
|
||||
<PlanosContent />
|
||||
</Suspense>
|
||||
|
||||
{/* FAQ */}
|
||||
<div className="mt-16 text-center">
|
||||
<p className="text-gray-500">
|
||||
Dúvidas? Entre em contato pelo WhatsApp{' '}
|
||||
<a href="https://wa.me/5511999999999" className="text-indigo-600 hover:underline">
|
||||
(11) 99999-9999
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +1,49 @@
|
||||
import Stripe from 'stripe';
|
||||
|
||||
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
||||
// @ts-expect-error - Use latest API version
|
||||
// @ts-expect-error - API version from installed stripe package
|
||||
apiVersion: '2024-12-18.acacia',
|
||||
typescript: true,
|
||||
});
|
||||
|
||||
// Planos da IrisTEA
|
||||
export const PLANS = {
|
||||
essencial: {
|
||||
name: 'Essencial',
|
||||
price: 29700, // em centavos (R$ 297)
|
||||
sessions: 2,
|
||||
description: '2 sessões de teleterapia/mês + Chat IA 24/7',
|
||||
price: 29700, // R$ 297,00 em centavos
|
||||
priceId: process.env.STRIPE_PRICE_ESSENCIAL,
|
||||
sessions: 1,
|
||||
features: [
|
||||
'2 sessões de 50min com BCBA',
|
||||
'Assistente IA 24/7',
|
||||
'Relatórios básicos mensais',
|
||||
'Grupo de apoio online',
|
||||
'IA ilimitada 24/7',
|
||||
'1 sessão BCBA/mês',
|
||||
'Dashboard de progresso',
|
||||
'Relatórios automáticos',
|
||||
],
|
||||
},
|
||||
completo: {
|
||||
name: 'Completo',
|
||||
price: 49700, // R$ 497
|
||||
sessions: 4,
|
||||
description: '4 sessões de teleterapia/mês + Supervisão + Chat IA 24/7',
|
||||
price: 49700, // R$ 497,00
|
||||
priceId: process.env.STRIPE_PRICE_COMPLETO,
|
||||
sessions: 2,
|
||||
popular: true,
|
||||
features: [
|
||||
'4 sessões de 50min com BCBA',
|
||||
'Assistente IA 24/7 avançado',
|
||||
'Relatórios detalhados semanais',
|
||||
'Supervisão parental mensal',
|
||||
'Grupo de apoio online',
|
||||
'Materiais exclusivos',
|
||||
'Tudo do Essencial',
|
||||
'2 sessões BCBA/mês',
|
||||
'Grupo de pais',
|
||||
'Prioridade no suporte',
|
||||
],
|
||||
},
|
||||
intensivo: {
|
||||
name: 'Intensivo',
|
||||
price: 89700, // R$ 897
|
||||
sessions: 8,
|
||||
description: '8 sessões de teleterapia/mês + Supervisão + Suporte prioritário',
|
||||
price: 89700, // R$ 897,00
|
||||
priceId: process.env.STRIPE_PRICE_INTENSIVO,
|
||||
sessions: 4,
|
||||
features: [
|
||||
'8 sessões de 50min com BCBA',
|
||||
'Assistente IA 24/7 premium',
|
||||
'Relatórios detalhados semanais',
|
||||
'Supervisão parental quinzenal',
|
||||
'Suporte prioritário WhatsApp',
|
||||
'Consultas emergenciais',
|
||||
'Materiais exclusivos',
|
||||
'Desconto em cursos',
|
||||
'Tudo do Completo',
|
||||
'4 sessões BCBA/mês',
|
||||
'Equipe multidisciplinar',
|
||||
'Plano personalizado',
|
||||
],
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type PlanId = keyof typeof PLANS;
|
||||
export type PlanType = keyof typeof PLANS;
|
||||
|
||||
29
tailwind.config.ts
Normal file
29
tailwind.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
iris: {
|
||||
purple: '#6366f1',
|
||||
'purple-dark': '#4f46e5',
|
||||
blue: '#3b82f6',
|
||||
green: '#10b981',
|
||||
yellow: '#f59e0b',
|
||||
red: '#ef4444',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
export default config
|
||||
Reference in New Issue
Block a user