From 5892389de878399fa9ad58a6cb996a3fee00b5b7 Mon Sep 17 00:00:00 2001 From: Shouryan Nikam Date: Sat, 9 Mar 2024 17:07:11 -0800 Subject: [PATCH 1/6] create onboarding template --- apps/www/app/onboarding/page.tsx | 44 + apps/www/components/onboarding/beams.tsx | 139 +++ .../components/onboarding/connect-account.tsx | 155 +++ .../components/onboarding/financial-goals.tsx | 88 ++ .../onboarding/onboarding-constants.tsx | 4 + apps/www/components/onboarding/welcome.tsx | 70 ++ package-lock.json | 924 ++++++++++++++++++ package.json | 7 +- pnpm-lock.yaml | 4 + tooling/tailwind/index.ts | 17 +- 10 files changed, 1449 insertions(+), 3 deletions(-) create mode 100644 apps/www/app/onboarding/page.tsx create mode 100644 apps/www/components/onboarding/beams.tsx create mode 100644 apps/www/components/onboarding/connect-account.tsx create mode 100644 apps/www/components/onboarding/financial-goals.tsx create mode 100644 apps/www/components/onboarding/onboarding-constants.tsx create mode 100644 apps/www/components/onboarding/welcome.tsx create mode 100644 package-lock.json diff --git a/apps/www/app/onboarding/page.tsx b/apps/www/app/onboarding/page.tsx new file mode 100644 index 00000000..8ce33a8d --- /dev/null +++ b/apps/www/app/onboarding/page.tsx @@ -0,0 +1,44 @@ +"use client"; + +import Welcome from "@/components/onboarding/welcome"; +import { AnimatePresence, motion } from "framer-motion"; +import { ArrowLeft } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import FinancialGoals from "@/components/onboarding/financial-goals"; +import ConnectAccount from "@/components/onboarding/connect-account"; +import { Toaster } from "@/components/ui/toaster"; +import Link from "next/link"; + +export default function Intro() { + const router = useRouter(); + const searchParams = useSearchParams() + + const step = searchParams.get('step') + + return ( +
+ + + {step ? ( + + ) : ( + + )} + {step === "financial-goals" && } + {step === "connect-accounts" && } + {step === "done" && ( +
+

+ Done! +

+ Go to Dashboard +
)} +
+
+ ); +} diff --git a/apps/www/components/onboarding/beams.tsx b/apps/www/components/onboarding/beams.tsx new file mode 100644 index 00000000..ad022fc8 --- /dev/null +++ b/apps/www/components/onboarding/beams.tsx @@ -0,0 +1,139 @@ +"use client"; +import React from "react"; +import { motion } from "framer-motion"; +import { cn } from "@/lib/utils"; + +export const BackgroundBeams = React.memo( + ({ className }: { className?: string }) => { + const paths = [ + "M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875", + "M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867", + "M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859", + "M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851", + "M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843", + "M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835", + "M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827", + "M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819", + "M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811", + "M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803", + "M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795", + "M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787", + "M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779", + "M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771", + "M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763", + "M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755", + "M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747", + "M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739", + "M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731", + "M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723", + "M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715", + "M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707", + "M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699", + "M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691", + "M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683", + "M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675", + "M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667", + "M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659", + "M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651", + "M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643", + "M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635", + "M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627", + "M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619", + "M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611", + "M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603", + "M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595", + "M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587", + "M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579", + "M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571", + "M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563", + "M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555", + "M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547", + "M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539", + "M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531", + "M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523", + "M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515", + "M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507", + "M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499", + "M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491", + "M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483", + ]; + return ( +
+ + + + {paths.map((path, index) => ( + + ))} + + {paths.map((path, index) => ( + + + + + + + ))} + + + + + + + + +
+ ); + } +); + +BackgroundBeams.displayName = "BackgroundBeams"; diff --git a/apps/www/components/onboarding/connect-account.tsx b/apps/www/components/onboarding/connect-account.tsx new file mode 100644 index 00000000..a4ac5666 --- /dev/null +++ b/apps/www/components/onboarding/connect-account.tsx @@ -0,0 +1,155 @@ +"use client"; +import { useMotionValue } from "framer-motion"; +import React, { useState, useEffect, useCallback } from "react"; +import { useMotionTemplate, motion } from "framer-motion"; +import { cn } from "@/lib/utils"; +import Link from "next/link"; +import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; +import { Button } from "../ui/button"; +import { useRouter, useSearchParams } from "next/navigation"; + + +export default function ConnectAccount() { + return ( + + +

+ Connect Accounts +

+

+ Securely connect your bank accounts with Badget. Powered by Plaid. +

+
+ + + +
+ ) +} + + +export const SecureCard = ({ + className, +}: { + className?: string; +}) => { + let mouseX = useMotionValue(0); + let mouseY = useMotionValue(0); + + const router = useRouter(); + const searchParams = useSearchParams() + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()) + params.set(name, value) + + return params.toString() + }, + [searchParams] + ) + + const [randomString, setRandomString] = useState(""); + + useEffect(() => { + let str = generateRandomString(1500); + setRandomString(str); + }, []); + + function onMouseMove({ currentTarget, clientX, clientY }: any) { + let { left, top } = currentTarget.getBoundingClientRect(); + mouseX.set(clientX - left); + mouseY.set(clientY - top); + + const str = generateRandomString(1500); + setRandomString(str); + } + + return ( +
+
+ +
+
+
+ + + +
+
+
+
+ ); +}; + +export function CardPattern({ mouseX, mouseY, randomString }: any) { + let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`; + let style = { maskImage, WebkitMaskImage: maskImage }; + + return ( +
+
+ + +

+ {randomString} +

+
+
+ ); +} + +const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +export const generateRandomString = (length: number) => { + let result = ""; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +}; diff --git a/apps/www/components/onboarding/financial-goals.tsx b/apps/www/components/onboarding/financial-goals.tsx new file mode 100644 index 00000000..466b44bb --- /dev/null +++ b/apps/www/components/onboarding/financial-goals.tsx @@ -0,0 +1,88 @@ +import { motion } from "framer-motion"; +import { + BrainCircuit, + AreaChart, + PiggyBank, +} from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; +import { useCallback } from "react"; + +export default function FinancialGoals() { + const router = useRouter(); + const searchParams = useSearchParams() + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()) + params.set(name, value) + + return params.toString() + }, + [searchParams] + ) + + return ( + + +

+ Badget +

+

+ What brings you here? +

+
+ + + + + +
+ ); +} diff --git a/apps/www/components/onboarding/onboarding-constants.tsx b/apps/www/components/onboarding/onboarding-constants.tsx new file mode 100644 index 00000000..7dc30f29 --- /dev/null +++ b/apps/www/components/onboarding/onboarding-constants.tsx @@ -0,0 +1,4 @@ +export const STAGGER_CHILD_VARIANTS = { + hidden: { opacity: 0, y: 20 }, + show: { opacity: 1, y: 0, transition: { duration: 0.4, type: "spring" } }, +}; diff --git a/apps/www/components/onboarding/welcome.tsx b/apps/www/components/onboarding/welcome.tsx new file mode 100644 index 00000000..c2485291 --- /dev/null +++ b/apps/www/components/onboarding/welcome.tsx @@ -0,0 +1,70 @@ +import { motion } from "framer-motion"; +import { Button } from "../ui/button"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useCallback } from "react"; +import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; +import { BackgroundBeams } from "./beams"; + +export default function Welcome() { + const router = useRouter(); + const searchParams = useSearchParams() + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()) + params.set(name, value) + + return params.toString() + }, + [searchParams] + ) + + return ( + + + + Welcome to{" "} + Badget + + + Badget gives you the power to securely track your finances using AI, enabling smarter decisions. + + + + + + + + ); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c94af2ff --- /dev/null +++ b/package-lock.json @@ -0,0 +1,924 @@ +{ + "name": "badget", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "badget", + "version": "0.1.0", + "license": "AGPL-3.0", + "dependencies": { + "framer-motion": "^11.0.8" + }, + "devDependencies": { + "@projectx/prettier-config": "^0.1.0", + "prettier": "^3.2.5", + "turbo": "^1.12.4", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=v20.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "license": "MIT", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "license": "MIT", + "optional": true + }, + "node_modules/@ianvs/prettier-plugin-sort-imports": { + "version": "4.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.21.8", + "@babel/generator": "^7.21.5", + "@babel/parser": "^7.21.8", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5", + "semver": "^7.5.2" + }, + "peerDependencies": { + "@vue/compiler-sfc": ">=3.0.0", + "prettier": "2 || 3" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@projectx/prettier-config": { + "resolved": "tooling/prettier", + "link": true + }, + "node_modules/browserslist": { + "version": "4.22.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001580", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.645", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/framer-motion": { + "version": "11.0.8", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.8.tgz", + "integrity": "sha512-1KSGNuqe1qZkS/SWQlDnqK2VCVzRVEoval379j0FiUBJAZoqgwyvqFkfvJbgW2IPFo4wX16K+M0k5jO23lCIjA==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/prettier": { + "version": "3.2.5", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.11", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, + "node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "license": "0BSD" + }, + "node_modules/turbo": { + "version": "1.12.4", + "dev": true, + "license": "MPL-2.0", + "bin": { + "turbo": "bin/turbo" + }, + "optionalDependencies": { + "turbo-darwin-64": "1.12.4", + "turbo-darwin-arm64": "1.12.4", + "turbo-linux-64": "1.12.4", + "turbo-linux-arm64": "1.12.4", + "turbo-windows-64": "1.12.4", + "turbo-windows-arm64": "1.12.4" + } + }, + "node_modules/turbo-darwin-arm64": { + "version": "1.12.4", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/typescript": { + "version": "5.3.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "tooling/prettier": { + "name": "@projectx/prettier-config", + "version": "0.1.0", + "dev": true, + "dependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.1.1", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.11" + }, + "devDependencies": { + "@projectx/tsconfig": "^0.1.0", + "typescript": "^5.3.3" + } + }, + "tooling/prettier/node_modules/@projectx/tsconfig": { + "resolved": "tooling/typescript", + "link": true + }, + "tooling/typescript": { + "name": "@projectx/tsconfig", + "version": "0.1.0", + "dev": true + } + } +} diff --git a/package.json b/package.json index cae1907f..99a8b1ff 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "turbo": "^1.12.4", "typescript": "^5.3.3" }, - "prettier": "@projectx/prettier-config" -} \ No newline at end of file + "prettier": "@projectx/prettier-config", + "dependencies": { + "framer-motion": "^11.0.8" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcc11913..2a630f6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + framer-motion: + specifier: ^11.0.8 + version: 11.0.8(react-dom@18.2.0)(react@18.2.0) devDependencies: '@projectx/prettier-config': specifier: ^0.1.0 diff --git a/tooling/tailwind/index.ts b/tooling/tailwind/index.ts index 523b4f03..2920ea25 100644 --- a/tooling/tailwind/index.ts +++ b/tooling/tailwind/index.ts @@ -1,6 +1,10 @@ import type { Config } from "tailwindcss"; import { fontFamily } from "tailwindcss/defaultTheme"; +const { + default: flattenColorPalette, +} = require("tailwindcss/lib/util/flattenColorPalette"); + const config = { darkMode: ["class"], content: [ @@ -137,7 +141,18 @@ const config = { }, }, }, - plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], + plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography"), addVariablesForColors], } satisfies Config; +function addVariablesForColors({ addBase, theme }: any) { + let allColors = flattenColorPalette(theme("colors")); + let newVars = Object.fromEntries( + Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) + ); + + addBase({ + ":root": newVars, + }); +} + export default config; From edf3571f35fb43c33a11f2670894fc682d6c737c Mon Sep 17 00:00:00 2001 From: Shouryan Nikam Date: Thu, 14 Mar 2024 10:16:13 -0700 Subject: [PATCH 2/6] make the requsted ui changes --- apps/www/app/onboarding/page.tsx | 28 ++++---- apps/www/components/onboarding/beams.tsx | 10 +-- .../components/onboarding/connect-account.tsx | 66 +++++++++++-------- .../components/onboarding/financial-goals.tsx | 55 +++++++++------- apps/www/components/onboarding/welcome.tsx | 42 ++++++------ 5 files changed, 113 insertions(+), 88 deletions(-) diff --git a/apps/www/app/onboarding/page.tsx b/apps/www/app/onboarding/page.tsx index 8ce33a8d..5bb4893d 100644 --- a/apps/www/app/onboarding/page.tsx +++ b/apps/www/app/onboarding/page.tsx @@ -1,30 +1,31 @@ "use client"; -import Welcome from "@/components/onboarding/welcome"; +import Link from "next/link"; +import { useRouter, useSearchParams } from "next/navigation"; import { AnimatePresence, motion } from "framer-motion"; import { ArrowLeft } from "lucide-react"; -import { useRouter, useSearchParams } from "next/navigation"; -import FinancialGoals from "@/components/onboarding/financial-goals"; -import ConnectAccount from "@/components/onboarding/connect-account"; + import { Toaster } from "@/components/ui/toaster"; -import Link from "next/link"; +import ConnectAccount from "@/components/onboarding/connect-account"; +import FinancialGoals from "@/components/onboarding/financial-goals"; +import Welcome from "@/components/onboarding/welcome"; export default function Intro() { const router = useRouter(); - const searchParams = useSearchParams() + const searchParams = useSearchParams(); - const step = searchParams.get('step') + const step = searchParams.get("step"); return ( -
+
{step ? ( ) : ( @@ -36,8 +37,11 @@ export default function Intro() {

Done!

- Go to Dashboard -
)} + + Go to Dashboard + +
+ )}
); diff --git a/apps/www/components/onboarding/beams.tsx b/apps/www/components/onboarding/beams.tsx index ad022fc8..40c1157b 100644 --- a/apps/www/components/onboarding/beams.tsx +++ b/apps/www/components/onboarding/beams.tsx @@ -1,6 +1,8 @@ "use client"; + import React from "react"; import { motion } from "framer-motion"; + import { cn } from "@/lib/utils"; export const BackgroundBeams = React.memo( @@ -60,12 +62,12 @@ export const BackgroundBeams = React.memo( return (
); - } + }, ); BackgroundBeams.displayName = "BackgroundBeams"; diff --git a/apps/www/components/onboarding/connect-account.tsx b/apps/www/components/onboarding/connect-account.tsx index a4ac5666..565154d1 100644 --- a/apps/www/components/onboarding/connect-account.tsx +++ b/apps/www/components/onboarding/connect-account.tsx @@ -1,13 +1,14 @@ "use client"; -import { useMotionValue } from "framer-motion"; -import React, { useState, useEffect, useCallback } from "react"; -import { useMotionTemplate, motion } from "framer-motion"; -import { cn } from "@/lib/utils"; + +import React, { useCallback, useEffect, useState } from "react"; import Link from "next/link"; -import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; -import { Button } from "../ui/button"; import { useRouter, useSearchParams } from "next/navigation"; +import { motion, useMotionTemplate, useMotionValue } from "framer-motion"; + +import { cn } from "@/lib/utils"; +import { Button } from "../ui/button"; +import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; export default function ConnectAccount() { return ( @@ -35,41 +36,45 @@ export default function ConnectAccount() {

Connect Accounts

-

- Securely connect your bank accounts with Badget. Powered by Plaid. +

+ Securely connect your bank accounts with Badget. Powered by{" "} + Plaid.

- + + + - ) + ); } - export const SecureCard = ({ className, + text, }: { className?: string; + text: string; }) => { let mouseX = useMotionValue(0); let mouseY = useMotionValue(0); const router = useRouter(); - const searchParams = useSearchParams() + const searchParams = useSearchParams(); const createQueryString = useCallback( (name: string, value: string) => { - const params = new URLSearchParams(searchParams.toString()) - params.set(name, value) + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); - return params.toString() + return params.toString(); }, - [searchParams] - ) + [searchParams], + ); const [randomString, setRandomString] = useState(""); @@ -90,13 +95,13 @@ export const SecureCard = ({ return (
-
-
- +
+
@@ -129,14 +137,14 @@ export function CardPattern({ mouseX, mouseY, randomString }: any) {
-

+

{randomString}

diff --git a/apps/www/components/onboarding/financial-goals.tsx b/apps/www/components/onboarding/financial-goals.tsx index 466b44bb..74c1933e 100644 --- a/apps/www/components/onboarding/financial-goals.tsx +++ b/apps/www/components/onboarding/financial-goals.tsx @@ -1,26 +1,23 @@ -import { motion } from "framer-motion"; -import { - BrainCircuit, - AreaChart, - PiggyBank, -} from "lucide-react"; +import { useCallback } from "react"; import { useRouter, useSearchParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { AreaChart, BrainCircuit, PiggyBank } from "lucide-react"; + import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; -import { useCallback } from "react"; export default function FinancialGoals() { const router = useRouter(); - const searchParams = useSearchParams() + const searchParams = useSearchParams(); const createQueryString = useCallback( (name: string, value: string) => { - const params = new URLSearchParams(searchParams.toString()) - params.set(name, value) + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); - return params.toString() + return params.toString(); }, - [searchParams] - ) + [searchParams], + ); return ( diff --git a/apps/www/components/onboarding/welcome.tsx b/apps/www/components/onboarding/welcome.tsx index c2485291..bfecf108 100644 --- a/apps/www/components/onboarding/welcome.tsx +++ b/apps/www/components/onboarding/welcome.tsx @@ -1,27 +1,28 @@ +import { useCallback } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; import { motion } from "framer-motion"; + import { Button } from "../ui/button"; -import { useRouter, useSearchParams } from "next/navigation"; -import { useCallback } from "react"; -import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; import { BackgroundBeams } from "./beams"; +import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; export default function Welcome() { const router = useRouter(); - const searchParams = useSearchParams() + const searchParams = useSearchParams(); const createQueryString = useCallback( (name: string, value: string) => { - const params = new URLSearchParams(searchParams.toString()) - params.set(name, value) + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); - return params.toString() + return params.toString(); }, - [searchParams] - ) + [searchParams], + ); return ( @@ -35,29 +36,30 @@ export default function Welcome() { }} initial="hidden" animate="show" - className="mx-5 flex flex-col items-center space-y-10 text-center sm:mx-auto z-10" + className="z-10 mx-5 flex flex-col items-center space-y-10 text-center sm:mx-auto" > - Welcome to{" "} - Badget + Welcome to Badget - Badget gives you the power to securely track your finances using AI, enabling smarter decisions. + Badget gives you the power to securely track your finances using AI, + enabling smarter decisions. - + + + + + Import File + + Export transactions data as a file from bank's website. + + + + + + ) + } + + return ( + + + + + + + Import File + + Export transactions data as a file from bank's website. + + + + + + + + + + + ) +} + +function UploadForm({ className, banks, setBanks }: { className: string, banks: string[], setBanks: React.Dispatch> }) { + const [selectedBank, setSelectedBank] = React.useState(""); + + const handleBankSelect = (value: string) => { + setSelectedBank(value); + }; + + const handleDeleteBank = (index: number) => { + setBanks(prevBanks => prevBanks.filter((_, i) => i !== index)); + }; + + const router = useRouter(); + const searchParams = useSearchParams(); + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); + + return params.toString(); + }, + [searchParams], + ); + + return ( +
+ + {selectedBank && ( +
+ + +
+ )} + + {banks.length > 0 && ( +
+

Selected Banks:

+
    + {banks.map((bank, index) => ( +
  • + {bank} + +
  • + ))} +
+
+ +
+
+ )} +
+ ) +} diff --git a/apps/www/components/onboarding/banks-chooser.tsx b/apps/www/components/onboarding/banks-chooser.tsx new file mode 100644 index 00000000..e141367e --- /dev/null +++ b/apps/www/components/onboarding/banks-chooser.tsx @@ -0,0 +1,79 @@ +"use client" + +import * as React from "react" +import { Check, ChevronsUpDown, Landmark } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import Image from "next/image" +import banks from "./banks-data/banks-list" + +export function BanksChooser({ onSelect }: { onSelect: (value: string) => void }) { + const [open, setOpen] = React.useState(false) + const [value, setValue] = React.useState("") + + return ( + + + + + + + + + Bank not found
+ Please raise an issue on GitHub +
+ + {banks.map((bank) => ( + { + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + onSelect(bank.label); + }} + className="h-16" + > + +
+ {/* TODO: Add images instead of just the icon */} + +
{bank.label}
+
+
+ ))} +
+
+
+
+ ) +} diff --git a/apps/www/components/onboarding/banks-data/banks-list.tsx b/apps/www/components/onboarding/banks-data/banks-list.tsx new file mode 100644 index 00000000..aef63e41 --- /dev/null +++ b/apps/www/components/onboarding/banks-data/banks-list.tsx @@ -0,0 +1,54 @@ +const banks = [ + { + value: "amex", + label: "American Express", + image: "boa.png", + }, + { + value: "boa", + label: "Bank of America", + image: "boa.png", + }, + { + value: "bmo", + label: "BMO", + image: "boa.png", + }, + { + value: "chase", + label: "Chase", + image: "boa.png", + }, + { + value: "citi", + label: "Citibank", + image: "boa.png", + }, + { + value: "citizens", + label: "Citizens", + image: "boa.png", + }, + { + value: "flagstar", + label: "Flagstar", + image: "boa.png", + }, + { + value: "pnc", + label: "PNC", + image: "boa.png", + }, + { + value: "usbank", + label: "US Bank", + image: "boa.png", + }, + { + value: "wellsfargo", + label: "Wells Fargo", + image: "boa.png", + }, +]; + +export default banks; diff --git a/apps/www/components/onboarding/banks-data/images/boa.png b/apps/www/components/onboarding/banks-data/images/boa.png new file mode 100644 index 0000000000000000000000000000000000000000..d49feb362f75658102cb6554726a32643bfac923 GIT binary patch literal 4148 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 literal 0 HcmV?d00001 diff --git a/apps/www/components/onboarding/connect-account.tsx b/apps/www/components/onboarding/connect-account.tsx index 565154d1..f03e68cb 100644 --- a/apps/www/components/onboarding/connect-account.tsx +++ b/apps/www/components/onboarding/connect-account.tsx @@ -1,16 +1,30 @@ "use client"; import React, { useCallback, useEffect, useState } from "react"; -import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { motion, useMotionTemplate, useMotionValue } from "framer-motion"; import { cn } from "@/lib/utils"; -import { Button } from "../ui/button"; import { STAGGER_CHILD_VARIANTS } from "./onboarding-constants"; +import { BankUploader } from "./bank-uploader"; +import { Badge } from "../ui/badge"; +import { Button } from "../ui/button"; export default function ConnectAccount() { + const router = useRouter(); + const searchParams = useSearchParams(); + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); + + return params.toString(); + }, + [searchParams], + ); + return (

- Securely connect your bank accounts with Badget. Powered by{" "} - Plaid. + Securely connect your bank accounts with Badget. + {/* Powered by{" "} + Plaid. */}

- + {/* - + */} + -
+
); } @@ -111,16 +127,7 @@ export const SecureCard = ({
- +
From 70b473699110c9fe5d4dd4a67238aaac80c0b0c0 Mon Sep 17 00:00:00 2001 From: Shouryan Nikam Date: Sun, 14 Apr 2024 09:39:08 -0700 Subject: [PATCH 4/6] small ui fixes --- .../components/onboarding/bank-uploader.tsx | 36 +++++++------- .../components/onboarding/banks-chooser.tsx | 2 +- .../onboarding/banks-data/banks-list.tsx | 48 +++++++++---------- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/apps/www/components/onboarding/bank-uploader.tsx b/apps/www/components/onboarding/bank-uploader.tsx index 8bbade0e..9afdeab9 100644 --- a/apps/www/components/onboarding/bank-uploader.tsx +++ b/apps/www/components/onboarding/bank-uploader.tsx @@ -26,6 +26,10 @@ import { BanksChooser } from "./banks-chooser" import { toast } from "../ui/use-toast" import { useRouter, useSearchParams } from "next/navigation" import { useCallback } from "react" +import { z } from "zod" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form" export function BankUploader() { const [open, setOpen] = React.useState(false) @@ -101,23 +105,21 @@ function UploadForm({ className, banks, setBanks }: { className: string, banks: return (
- {selectedBank && ( -
- - -
- )} +
+ + +
{banks.length > 0 && (
diff --git a/apps/www/components/onboarding/banks-chooser.tsx b/apps/www/components/onboarding/banks-chooser.tsx index e141367e..44a46364 100644 --- a/apps/www/components/onboarding/banks-chooser.tsx +++ b/apps/www/components/onboarding/banks-chooser.tsx @@ -56,7 +56,7 @@ export function BanksChooser({ onSelect }: { onSelect: (value: string) => void } setOpen(false) onSelect(bank.label); }} - className="h-16" + className="h-16 pr-6" > Date: Sun, 14 Apr 2024 15:16:43 -0700 Subject: [PATCH 5/6] add generic parser --- .../components/onboarding/bank-uploader.tsx | 54 +++++++++++----- .../banks-data/parse-data/amex-credit.tsx | 5 ++ .../parse-data/generic-csv-parser.tsx | 61 +++++++++++++++++++ apps/www/package.json | 1 + pnpm-lock.yaml | 52 ++++++++++++++++ 5 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 apps/www/components/onboarding/banks-data/parse-data/amex-credit.tsx create mode 100644 apps/www/components/onboarding/banks-data/parse-data/generic-csv-parser.tsx diff --git a/apps/www/components/onboarding/bank-uploader.tsx b/apps/www/components/onboarding/bank-uploader.tsx index 9afdeab9..084bcd72 100644 --- a/apps/www/components/onboarding/bank-uploader.tsx +++ b/apps/www/components/onboarding/bank-uploader.tsx @@ -30,6 +30,7 @@ import { z } from "zod" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form" +import CSVParser from "./banks-data/parse-data/generic-csv-parser" export function BankUploader() { const [open, setOpen] = React.useState(false) @@ -42,7 +43,7 @@ export function BankUploader() { - + Import File @@ -60,8 +61,9 @@ export function BankUploader() { - - + + We are still working on mobile support. Badget is currently best experienced on desktop. + {/* Import File Export transactions data as a file from bank's website. @@ -72,7 +74,7 @@ export function BankUploader() { - + */} ) @@ -80,6 +82,8 @@ export function BankUploader() { function UploadForm({ className, banks, setBanks }: { className: string, banks: string[], setBanks: React.Dispatch> }) { const [selectedBank, setSelectedBank] = React.useState(""); + const [file, setFile] = React.useState(null); + const [uploading, setUploading] = React.useState(false); const handleBankSelect = (value: string) => { setSelectedBank(value); @@ -102,20 +106,38 @@ function UploadForm({ className, banks, setBanks }: { className: string, banks: [searchParams], ); + const handleFileChange = (event) => { + const selectedFile = event.target.files[0]; + setFile(selectedFile); + }; + + + const handleUpload = () => { + if (!selectedBank) { + return; + } + + if (selectedBank == "American Express Credit Card") { + AmexCredit({ file }); + } + + setBanks(prevBanks => [...prevBanks, selectedBank]); + toast({ + title: "File uploaded", + description: "Your file has been uploaded successfully.", + }) + }; + return (
- -
- + {/* */} + + {/*
+ + @@ -126,7 +148,7 @@ function UploadForm({ className, banks, setBanks }: { className: string, banks:

Selected Banks:

    {banks.map((bank, index) => ( -
  • +
  • {bank}
  • @@ -145,7 +167,7 @@ function UploadForm({ className, banks, setBanks }: { className: string, banks:
- )} + )} */}
) } diff --git a/apps/www/components/onboarding/banks-data/parse-data/amex-credit.tsx b/apps/www/components/onboarding/banks-data/parse-data/amex-credit.tsx new file mode 100644 index 00000000..caea7954 --- /dev/null +++ b/apps/www/components/onboarding/banks-data/parse-data/amex-credit.tsx @@ -0,0 +1,5 @@ +// TODO: finish individual components later + +function AmexCredit({ file }: { file: File | null }) { + console.log(file); +} diff --git a/apps/www/components/onboarding/banks-data/parse-data/generic-csv-parser.tsx b/apps/www/components/onboarding/banks-data/parse-data/generic-csv-parser.tsx new file mode 100644 index 00000000..cbda03c8 --- /dev/null +++ b/apps/www/components/onboarding/banks-data/parse-data/generic-csv-parser.tsx @@ -0,0 +1,61 @@ +"use client"; + +import React, { useCallback } from "react"; +import ReactDOM from "react-dom"; +import { Importer, ImporterField } from "react-csv-importer"; + +// theme CSS for React CSV Importer +import "react-csv-importer/dist/index.css"; +import { useRouter, useSearchParams } from "next/navigation"; + +export default function CSVParser() { + const router = useRouter(); + const searchParams = useSearchParams(); + + const createQueryString = useCallback( + (name: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()); + params.set(name, value); + + return params.toString(); + }, + [searchParams], + ); + + return ( +
+ { + // required, receives a list of parsed objects based on defined fields and user column mapping; + console.log("Rows processed:", rows); + + // mock timeout to simulate processing + await new Promise((resolve) => setTimeout(resolve, 500)); + }} + chunkSize={10000} // optional, internal parsing chunk size in bytes + defaultNoHeader={false} // optional, keeps "data has headers" checkbox off by default + restartable={true} // optional, lets user choose to upload another file when import is complete + onStart={({ file, fields }) => { + // optional, invoked when user has mapped columns and started import + console.log("starting import of file", file, "with fields", fields); + }} + onComplete={({ file, fields }) => { + // optional, invoked right after import is done (but user did not dismiss/reset the widget yet) + console.log("finished import of file", file, "with fields", fields); + + }} + onClose={() => { + // optional, invoked when import is done and user clicked "Finish" + console.log("importer dismissed"); + router.push( + "/onboarding" + "?" + createQueryString("step", "done"), + ) + }} + > + + + + +
+ ) +} diff --git a/apps/www/package.json b/apps/www/package.json index 7e528a35..d7d9cc34 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -94,6 +94,7 @@ "postmark": "^3.1.1", "prop-types": "^15.8.1", "react": "^18.2.0", + "react-csv-importer": "^0.8.1", "react-day-picker": "^8.9.1", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a630f6c..370d8a11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -252,6 +252,9 @@ importers: react: specifier: ^18.2.0 version: 18.2.0 + react-csv-importer: + specifier: ^0.8.1 + version: 0.8.1(react-dom@18.2.0)(react@18.2.0) react-day-picker: specifier: ^8.9.1 version: 8.10.0(date-fns@2.30.0)(react@18.2.0) @@ -6356,6 +6359,19 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + /@use-gesture/core@10.3.1: + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + dev: false + + /@use-gesture/react@10.3.1(react@18.2.0): + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + peerDependencies: + react: '>= 16.8.0' + dependencies: + '@use-gesture/core': 10.3.1 + react: 18.2.0 + dev: false + /@vercel/analytics@1.1.2: resolution: {integrity: sha512-CodhkLCQ/EHzjX8k+Qg+OzTBY0UadykrcfolfSOJVZZY/ZJM5nbhztm9KdbYvMfqKlasAr1+OYy0ThZnDA/MYA==} dependencies: @@ -8672,6 +8688,13 @@ packages: dependencies: flat-cache: 3.2.0 + /file-selector@0.5.0: + resolution: {integrity: sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==} + engines: {node: '>= 10'} + dependencies: + tslib: 2.6.2 + dev: false + /file-selector@0.6.0: resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} engines: {node: '>= 12'} @@ -11323,6 +11346,10 @@ packages: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} dev: false + /papaparse@5.4.1: + resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -11741,6 +11768,19 @@ packages: strip-json-comments: 2.0.1 dev: false + /react-csv-importer@0.8.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GcFQyeNJFFymqqnPi2YEr+UJWrLy0bdKEZ86n7HMcJ1O3ZGU0KQxB5CnYiKLT40QkWP8eAajXXy7o90lHmSj2Q==} + peerDependencies: + react: ^16.8.0 || >=17.0.0 + react-dom: ^16.8.0 || >=17.0.0 + dependencies: + '@use-gesture/react': 10.3.1(react@18.2.0) + papaparse: 5.4.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-dropzone: 12.1.0(react@18.2.0) + dev: false + /react-day-picker@8.10.0(date-fns@2.30.0)(react@18.2.0): resolution: {integrity: sha512-mz+qeyrOM7++1NCb1ARXmkjMkzWVh2GL9YiPbRjKe0zHccvekk4HE+0MPOZOrosn8r8zTHIIeOUXTmXRqmkRmg==} peerDependencies: @@ -11761,6 +11801,18 @@ packages: scheduler: 0.23.0 dev: false + /react-dropzone@12.1.0(react@18.2.0): + resolution: {integrity: sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog==} + engines: {node: '>= 10.13'} + peerDependencies: + react: '>= 16.8' + dependencies: + attr-accept: 2.2.2 + file-selector: 0.5.0 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + /react-dropzone@14.2.3(react@18.2.0): resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==} engines: {node: '>= 10.13'} From cb216c589338a8f48945e251540748fbf79938bb Mon Sep 17 00:00:00 2001 From: Shouryan Nikam Date: Sun, 14 Apr 2024 15:21:01 -0700 Subject: [PATCH 6/6] add file for local testing --- .../onboarding/banks-data/images/testing.csv | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 apps/www/components/onboarding/banks-data/images/testing.csv diff --git a/apps/www/components/onboarding/banks-data/images/testing.csv b/apps/www/components/onboarding/banks-data/images/testing.csv new file mode 100644 index 00000000..bd21447f --- /dev/null +++ b/apps/www/components/onboarding/banks-data/images/testing.csv @@ -0,0 +1,11 @@ +"Date","Name","Amount" +"2016-01-01","Food","10.00" +"2016-01-01","Transport","5.00" +"2016-01-02","Food","20.00" +"2016-01-02","Transport","10.00" +"2016-01-03","Food","30.00" +"2016-01-03","Transport","15.00" +"2016-01-04","Food","40.00" +"2016-01-04","Transport","20.00" +"2016-01-05","Food","50.00" +"2016-01-05","Transport","25.00"