From 360a15d5a707ed53d0aae968780ececc07682b42 Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Wed, 22 Oct 2025 22:05:34 +0200 Subject: [PATCH] add trial choice template --- package-lock.json | 309 ++++++++---------- package.json | 12 +- .../admin/builder/Canvas/constants.ts | 1 + .../admin/builder/dialogs/AddScreenDialog.tsx | 9 + .../TrialChoiceTemplate.stories.tsx | 42 +++ .../TrialChoiceTemplate.tsx | 50 +++ .../templates/TrialChoiceTemplate/index.ts | 1 + src/components/funnel/templates/index.ts | 1 + src/lib/admin/builder/state/defaults/index.ts | 1 + .../builder/state/defaults/trialChoice.ts | 30 ++ src/lib/admin/builder/state/utils.ts | 3 + src/lib/funnel/screenRenderer.tsx | 25 ++ src/lib/funnel/types.ts | 14 +- src/lib/models/Funnel.ts | 1 + 14 files changed, 327 insertions(+), 172 deletions(-) create mode 100644 src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.stories.tsx create mode 100644 src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.tsx create mode 100644 src/components/funnel/templates/TrialChoiceTemplate/index.ts create mode 100644 src/lib/admin/builder/state/defaults/trialChoice.ts diff --git a/package-lock.json b/package-lock.json index 7fada9f..c53c0a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,11 +38,11 @@ "devDependencies": { "@chromatic-com/storybook": "^4.1.1", "@eslint/eslintrc": "^3", - "@storybook/addon-a11y": "^9.1.6", - "@storybook/addon-docs": "^9.1.6", + "@storybook/addon-a11y": "^9.1.13", + "@storybook/addon-docs": "^9.1.13", "@storybook/addon-styling-webpack": "^2.0.0", - "@storybook/addon-vitest": "^9.1.6", - "@storybook/nextjs-vite": "^9.1.6", + "@storybook/addon-vitest": "^9.1.13", + "@storybook/nextjs-vite": "^9.1.13", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -51,9 +51,9 @@ "@vitest/coverage-v8": "^3.2.4", "eslint": "^9", "eslint-config-next": "15.5.3", - "eslint-plugin-storybook": "^9.1.6", + "eslint-plugin-storybook": "^9.1.13", "playwright": "^1.55.0", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "tailwindcss": "^4", "tw-animate-css": "^1.3.8", "typescript": "^5", @@ -1605,53 +1605,6 @@ } } }, - "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3109,9 +3062,9 @@ "license": "MIT" }, "node_modules/@storybook/addon-a11y": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-9.1.6.tgz", - "integrity": "sha512-jpuzbZlT8G1hx4N6nhhmxy6Lu+Xnz1oeGb2/pm+rKx2fZ4oy7yGRliRNOvpTy8MbkpnfMoLLrcqc66s/kfdf3A==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-9.1.13.tgz", + "integrity": "sha512-4enIl1h2XSZnFKUQJJoZbp1X40lzdj7f5JE15ZhU1al4z6hHWp7i2zD7ySyDpEbMypBCz1xnLvyiyw79m1fp7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3123,20 +3076,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^9.1.6" + "storybook": "^9.1.13" } }, "node_modules/@storybook/addon-docs": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.6.tgz", - "integrity": "sha512-4ZE/T2Ayw77/v2ersAk/VM7vlvqV2zCNFwt0uvOzUR1VZ9VqZCHhsfy/IyBPeKt6Otax3EpfE1LkH4slfceB0g==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.13.tgz", + "integrity": "sha512-V1nCo7bfC3kQ5VNVq0VDcHsIhQf507m+BxMA5SIYiwdJHljH2BXpW2fL3FFn9gv9Wp57AEEzhm+wh4zANaJgkg==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/csf-plugin": "9.1.6", + "@storybook/csf-plugin": "9.1.13", "@storybook/icons": "^1.4.0", - "@storybook/react-dom-shim": "9.1.6", + "@storybook/react-dom-shim": "9.1.13", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -3146,7 +3099,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^9.1.6" + "storybook": "^9.1.13" } }, "node_modules/@storybook/addon-styling-webpack": { @@ -3161,9 +3114,9 @@ } }, "node_modules/@storybook/addon-vitest": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-vitest/-/addon-vitest-9.1.6.tgz", - "integrity": "sha512-I5kev4ZfJFP4ScTV7cfA1PMqSantcfNBio5xzO0edoMXBWugPrD22M2Z2kF+odieHGYXsQy8bc56F4KdAUiu4w==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/addon-vitest/-/addon-vitest-9.1.13.tgz", + "integrity": "sha512-g/wkQ8i1GGlsoHEe6bjWic+ESokWhuMBxAa9FDLW9KDf0L1DMyQqFFJFnGoo99zCNRVJcSXgzZTFp6SCt3FKog==", "dev": true, "license": "MIT", "dependencies": { @@ -3179,7 +3132,7 @@ "peerDependencies": { "@vitest/browser": "^3.0.0", "@vitest/runner": "^3.0.0", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "vitest": "^3.0.0" }, "peerDependenciesMeta": { @@ -3195,13 +3148,13 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.6.tgz", - "integrity": "sha512-AUoSjXr4MvtkFQkfFfZSXrqVM0z80DX0sebm80nODu/qFhsJIU5trNP+XDYY8ClODERXd5QSZJyOyH9nOz60SA==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.13.tgz", + "integrity": "sha512-pmtIjU02ASJOZKdL8DoxWXJgZnpTDgD5WmMnjKJh9FaWmc2YiCW2Y6VRxPox96OM655jYHQe5+UIbk3Cwtwb4A==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "9.1.6", + "@storybook/csf-plugin": "9.1.13", "ts-dedent": "^2.0.0" }, "funding": { @@ -3209,14 +3162,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^9.1.6", + "storybook": "^9.1.13", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/csf-plugin": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.6.tgz", - "integrity": "sha512-cz4Y+OYCtuovFNwoLkIKk0T62clrRTYf26Bbo1gdIGuX/W3JPP/LnN97sP2/0nfF6heZqCdEwb47k7RubkxXZg==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.13.tgz", + "integrity": "sha512-EMpzYuyt9FDcxxfBChWzfId50y8QMpdenviEQ8m+pa6c+ANx3pC5J6t7y0khD8TQu815sTy+nc6cc8PC45dPUA==", "dev": true, "license": "MIT", "dependencies": { @@ -3227,7 +3180,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^9.1.6" + "storybook": "^9.1.13" } }, "node_modules/@storybook/global": { @@ -3252,17 +3205,17 @@ } }, "node_modules/@storybook/nextjs-vite": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/nextjs-vite/-/nextjs-vite-9.1.6.tgz", - "integrity": "sha512-BdzgPiuOKTl5q+NgCJjpwuFYL/me0u69zpZ7w4EhVLs4pxyJtUO7JRW8OW5VyBp3/M2ryoqWU0dPAfQ4WzrHAA==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/nextjs-vite/-/nextjs-vite-9.1.13.tgz", + "integrity": "sha512-iUQbfAndUag5ehPPldvVCM8T4GylOEZqf13EEHvjgh8F33vOiANq7WTGbvO+aBpBNug3x+1pG1hmKXRKVmA6xQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-vite": "9.1.6", - "@storybook/react": "9.1.6", - "@storybook/react-vite": "9.1.6", + "@storybook/builder-vite": "9.1.13", + "@storybook/react": "9.1.13", + "@storybook/react-vite": "9.1.13", "styled-jsx": "5.1.6", - "vite-plugin-storybook-nextjs": "^2.0.5" + "vite-plugin-storybook-nextjs": "^2.0.7" }, "engines": { "node": ">=20.0.0" @@ -3275,7 +3228,7 @@ "next": "^14.1.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "peerDependenciesMeta": { @@ -3285,14 +3238,14 @@ } }, "node_modules/@storybook/react": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-9.1.6.tgz", - "integrity": "sha512-BGf3MQaXj6LmYnYpSwHUoWH0RP6kaqBoPc2u5opSU2ajw34enIL5w2sFaXzL+k2ap0aHnCYYlyBINBBvtD6NIA==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-9.1.13.tgz", + "integrity": "sha512-B0UpYikKf29t8QGcdmumWojSQQ0phSDy/Ne2HYdrpNIxnUvHHUVOlGpq4lFcIDt52Ip5YG5GuAwJg3+eR4LCRg==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "9.1.6" + "@storybook/react-dom-shim": "9.1.13" }, "engines": { "node": ">=20.0.0" @@ -3304,7 +3257,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "typescript": ">= 4.9.x" }, "peerDependenciesMeta": { @@ -3314,9 +3267,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.6.tgz", - "integrity": "sha512-Px4duzPMTPqI3kes6eUyYjWpEeJ0AOCCeSDCBDm9rzlf4a+eXlxfhkcVWft3viCDiIkc0vtYagb2Yu7bcSIypg==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.13.tgz", + "integrity": "sha512-/tMr9TmV3+98GEQO0S03k4gtKHGCpv9+k9Dmnv+TJK3TBz7QsaFEzMwe3gCgoTaebLACyVveDiZkWnCYAWB6NA==", "dev": true, "license": "MIT", "funding": { @@ -3326,20 +3279,20 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^9.1.6" + "storybook": "^9.1.13" } }, "node_modules/@storybook/react-vite": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-9.1.6.tgz", - "integrity": "sha512-YNKQZcz5Vtv8OdHUJ65Wx4PbfZMrPPbtL+OYAR0We+EEoTDofi3VogXyOUw99Jppp1HIq5IiDF5qyZPEpC5k0A==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-9.1.13.tgz", + "integrity": "sha512-mV1bZ1bpkNQygnuDo1xMGAS5ZXuoXFF0WGmr/BzNDGmRhZ1K1HQh42kC0w3PklckFBUwCFxmP58ZwTFzf+/dJA==", "dev": true, "license": "MIT", "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.6.1", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "9.1.6", - "@storybook/react": "9.1.6", + "@storybook/builder-vite": "9.1.13", + "@storybook/react": "9.1.13", "find-up": "^7.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", @@ -3356,23 +3309,10 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, - "node_modules/@storybook/react-vite/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@storybook/react-vite/node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", @@ -3462,28 +3402,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@storybook/react-vite/node_modules/react-docgen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.1.tgz", - "integrity": "sha512-kQKsqPLplY3Hx4jGnM3jpQcG3FQDt7ySz32uTHt3C9HAe45kNXG+3o16Eqn3Fw1GtMfHoN3b4J/z2e6cZJCmqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", - "@types/babel__core": "^7.20.5", - "@types/babel__traverse": "^7.20.7", - "@types/doctrine": "^0.0.9", - "@types/resolve": "^1.20.2", - "doctrine": "^3.0.0", - "resolve": "^1.22.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": "^20.9.0 || >=22" - } - }, "node_modules/@storybook/react-vite/node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -6856,9 +6774,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.6.tgz", - "integrity": "sha512-4NLf8lOT7Nl+m9aipVHJczyt/Dp6BzHzyNq4nhaEUjoZFGKMhPa52vSbuLyQYX7IrcrYPlM37X8dFGo/EIE9JA==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.13.tgz", + "integrity": "sha512-kPuhbtGDiJLB5OLZuwFZAxgzWakNDw64sJtXUPN8g0+VAeXfHyZEmsE28qIIETHxtal71lPKVm8QNnERaJHPJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6869,7 +6787,7 @@ }, "peerDependencies": { "eslint": ">=8", - "storybook": "^9.1.6" + "storybook": "^9.1.13" } }, "node_modules/eslint-scope": { @@ -7357,6 +7275,27 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7378,6 +7317,32 @@ "license": "BSD-2-Clause", "peer": true }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -9696,6 +9661,28 @@ "react": ">=0.14.0" } }, + "node_modules/react-docgen": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.2.tgz", + "integrity": "sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.7", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": "^20.9.0 || >=22" + } + }, "node_modules/react-docgen-typescript": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz", @@ -9706,6 +9693,19 @@ "typescript": ">= 4.3.x" } }, + "node_modules/react-docgen/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/react-dom": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", @@ -10598,9 +10598,9 @@ } }, "node_modules/storybook": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.6.tgz", - "integrity": "sha512-iIcMaDKkjR5nN+JYBy9hhoxZhjX4TXhyJgUBed+toJOlfrl+QvxpBjImAi7qKyLR3hng3uoigEP0P8+vYtXpOg==", + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.13.tgz", + "integrity": "sha512-G3KZ36EVzXyHds72B/qtWiJnhUpM0xOUeYlDcO9DSHL1bDTv15cW4+upBl+mcBZrDvU838cn7Bv4GpF+O5MCfw==", "dev": true, "license": "MIT", "dependencies": { @@ -10854,9 +10854,9 @@ } }, "node_modules/strip-indent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.0.tgz", - "integrity": "sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", "dev": true, "license": "MIT", "engines": { @@ -11086,27 +11086,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/test-exclude/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", diff --git a/package.json b/package.json index 9dd3064..899d30e 100644 --- a/package.json +++ b/package.json @@ -50,11 +50,11 @@ "devDependencies": { "@chromatic-com/storybook": "^4.1.1", "@eslint/eslintrc": "^3", - "@storybook/addon-a11y": "^9.1.6", - "@storybook/addon-docs": "^9.1.6", + "@storybook/addon-a11y": "^9.1.13", + "@storybook/addon-docs": "^9.1.13", "@storybook/addon-styling-webpack": "^2.0.0", - "@storybook/addon-vitest": "^9.1.6", - "@storybook/nextjs-vite": "^9.1.6", + "@storybook/addon-vitest": "^9.1.13", + "@storybook/nextjs-vite": "^9.1.13", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -63,9 +63,9 @@ "@vitest/coverage-v8": "^3.2.4", "eslint": "^9", "eslint-config-next": "15.5.3", - "eslint-plugin-storybook": "^9.1.6", + "eslint-plugin-storybook": "^9.1.13", "playwright": "^1.55.0", - "storybook": "^9.1.6", + "storybook": "^9.1.13", "tailwindcss": "^4", "tw-animate-css": "^1.3.8", "typescript": "^5", diff --git a/src/components/admin/builder/Canvas/constants.ts b/src/components/admin/builder/Canvas/constants.ts index 3b68fae..59134a7 100644 --- a/src/components/admin/builder/Canvas/constants.ts +++ b/src/components/admin/builder/Canvas/constants.ts @@ -13,6 +13,7 @@ export const TEMPLATE_TITLES: Record = { loaders: "Загрузка", soulmate: "Портрет партнера", trialPayment: "Trial Payment", + trialChoice: "Trial Choice", specialOffer: "Special Offer", }; diff --git a/src/components/admin/builder/dialogs/AddScreenDialog.tsx b/src/components/admin/builder/dialogs/AddScreenDialog.tsx index d63f5bd..59c2778 100644 --- a/src/components/admin/builder/dialogs/AddScreenDialog.tsx +++ b/src/components/admin/builder/dialogs/AddScreenDialog.tsx @@ -12,6 +12,7 @@ import { Mail, CreditCard, Gift, + Layers, } from "lucide-react"; import { Button } from "@/components/ui/button"; @@ -99,6 +100,14 @@ const TEMPLATE_OPTIONS = [ color: "bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-400", }, + { + template: "trialChoice" as const, + title: "Trial Choice", + description: "Экран выбора вариантов пробного периода", + icon: Layers, + color: + "bg-lime-50 text-lime-700 dark:bg-lime-900/20 dark:text-lime-400", + }, { template: "specialOffer" as const, title: "Special Offer", diff --git a/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.stories.tsx b/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.stories.tsx new file mode 100644 index 0000000..64dbd0f --- /dev/null +++ b/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.stories.tsx @@ -0,0 +1,42 @@ +import { Meta, StoryObj } from "@storybook/nextjs-vite"; +import { TrialChoiceTemplate } from "./TrialChoiceTemplate"; +import { fn } from "storybook/test"; +import { buildTrialChoiceDefaults } from "@/lib/admin/builder/state/defaults/trialChoice"; +import type { TrialChoiceScreenDefinition } from "@/lib/funnel/types"; + +// Используем дефолтные значения из builder, чтобы сторибук совпадал с админкой +const defaultScreen = buildTrialChoiceDefaults( + "trial-choice-story" +) as TrialChoiceScreenDefinition; + +/** TrialChoiceTemplate — базовый экран с выбором пробного периода */ +const meta: Meta = { + title: "Funnel Templates/TrialChoiceTemplate", + component: TrialChoiceTemplate, + tags: ["autodocs"], + parameters: { + layout: "fullscreen", + }, + args: { + screen: defaultScreen, + onContinue: fn(), + canGoBack: true, + onBack: fn(), + defaultTexts: { + continueButton: "Next", + }, + answers: {}, + }, + argTypes: { + screen: { control: { type: "object" } }, + screenProgress: { control: { type: "object" } }, + onContinue: { action: "continue" }, + onBack: { action: "back" }, + }, +}; + +export default meta; +type Story = StoryObj; + +/** Базовый Trial Choice экран */ +export const Default: Story = {}; diff --git a/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.tsx b/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.tsx new file mode 100644 index 0000000..7f9d337 --- /dev/null +++ b/src/components/funnel/templates/TrialChoiceTemplate/TrialChoiceTemplate.tsx @@ -0,0 +1,50 @@ +"use client"; + +import type { + TrialChoiceScreenDefinition, + DefaultTexts, + FunnelAnswers, +} from "@/lib/funnel/types"; +import { TemplateLayout } from "@/components/funnel/templates/layouts/TemplateLayout"; +import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers"; + +interface TrialChoiceTemplateProps { + screen: TrialChoiceScreenDefinition; + onContinue: () => void; + canGoBack: boolean; + onBack: () => void; + screenProgress?: { current: number; total: number }; + defaultTexts?: DefaultTexts; + answers: FunnelAnswers; +} + +export function TrialChoiceTemplate( + props: TrialChoiceTemplateProps +) { + const { + screen, + onContinue, + canGoBack, + onBack, + screenProgress, + defaultTexts, + } = props; + const layoutProps = createTemplateLayoutProps( + screen, + { canGoBack, onBack }, + screenProgress, + { + preset: "center", + actionButton: { + defaultText: + screen.bottomActionButton?.text || + defaultTexts?.continueButton || + "Next", + disabled: false, + onClick: onContinue, + }, + } + ); + + return {null}; +} diff --git a/src/components/funnel/templates/TrialChoiceTemplate/index.ts b/src/components/funnel/templates/TrialChoiceTemplate/index.ts new file mode 100644 index 0000000..ee67497 --- /dev/null +++ b/src/components/funnel/templates/TrialChoiceTemplate/index.ts @@ -0,0 +1 @@ +export { TrialChoiceTemplate } from "./TrialChoiceTemplate"; diff --git a/src/components/funnel/templates/index.ts b/src/components/funnel/templates/index.ts index 712cc30..061237a 100644 --- a/src/components/funnel/templates/index.ts +++ b/src/components/funnel/templates/index.ts @@ -8,6 +8,7 @@ export { CouponTemplate } from "./CouponTemplate"; export { LoadersTemplate } from "./LoadersTemplate"; export { SoulmatePortraitTemplate } from "./SoulmatePortraitTemplate"; export { TrialPaymentTemplate } from "./TrialPaymentTemplate/index"; +export { TrialChoiceTemplate } from "./TrialChoiceTemplate"; export { SpecialOfferTemplate } from "./SpecialOffer/index"; // Layout Templates diff --git a/src/lib/admin/builder/state/defaults/index.ts b/src/lib/admin/builder/state/defaults/index.ts index 95b5523..74dd117 100644 --- a/src/lib/admin/builder/state/defaults/index.ts +++ b/src/lib/admin/builder/state/defaults/index.ts @@ -31,3 +31,4 @@ export { buildLoadersDefaults } from "./loaders"; export { buildSoulmateDefaults } from "./soulmate"; export { buildTrialPaymentDefaults } from "./trialPayment"; export { buildSpecialOfferDefaults } from "./specialOffer"; +export { buildTrialChoiceDefaults } from "./trialChoice"; diff --git a/src/lib/admin/builder/state/defaults/trialChoice.ts b/src/lib/admin/builder/state/defaults/trialChoice.ts new file mode 100644 index 0000000..f7018bf --- /dev/null +++ b/src/lib/admin/builder/state/defaults/trialChoice.ts @@ -0,0 +1,30 @@ +import type { BuilderScreen } from "@/lib/admin/builder/types"; +import { + buildDefaultHeader, + buildDefaultTitle, + buildDefaultSubtitle, + buildDefaultBottomActionButton, + buildDefaultNavigation, +} from "./blocks"; + +export function buildTrialChoiceDefaults(id: string): BuilderScreen { + return { + id, + template: "trialChoice", + header: buildDefaultHeader({ + showProgress: false, + }), + title: buildDefaultTitle({ + show: false, + text: undefined, + }), + subtitle: buildDefaultSubtitle({ + show: false, + text: undefined, + }), + bottomActionButton: buildDefaultBottomActionButton({ + text: "Next", + }), + navigation: buildDefaultNavigation(), + } as BuilderScreen; +} diff --git a/src/lib/admin/builder/state/utils.ts b/src/lib/admin/builder/state/utils.ts index 939fb94..1ecb7e5 100644 --- a/src/lib/admin/builder/state/utils.ts +++ b/src/lib/admin/builder/state/utils.ts @@ -11,6 +11,7 @@ import { buildLoadersDefaults } from "./defaults/loaders"; import { buildSoulmateDefaults } from "./defaults/soulmate"; import { buildTrialPaymentDefaults } from "./defaults/trialPayment"; import { buildSpecialOfferDefaults } from "./defaults/specialOffer"; +import { buildTrialChoiceDefaults } from "./defaults/trialChoice"; /** * Marks the state as dirty if it has changed @@ -66,6 +67,8 @@ export function createScreenByTemplate( return buildTrialPaymentDefaults(id); case "specialOffer": return buildSpecialOfferDefaults(id); + case "trialChoice": + return buildTrialChoiceDefaults(id); default: throw new Error(`Unknown template: ${template}`); } diff --git a/src/lib/funnel/screenRenderer.tsx b/src/lib/funnel/screenRenderer.tsx index 0962ac7..ed5be3f 100644 --- a/src/lib/funnel/screenRenderer.tsx +++ b/src/lib/funnel/screenRenderer.tsx @@ -13,6 +13,7 @@ import { SoulmatePortraitTemplate, TrialPaymentTemplate, SpecialOfferTemplate, + TrialChoiceTemplate, } from "@/components/funnel/templates"; import type { ListScreenDefinition, @@ -29,6 +30,7 @@ import type { FunnelDefinition, FunnelAnswers, SpecialOfferScreenDefinition, + TrialChoiceScreenDefinition, } from "@/lib/funnel/types"; export interface ScreenRenderProps { @@ -353,6 +355,29 @@ const TEMPLATE_REGISTRY: Record< /> ); }, + trialChoice: ({ + screen, + onContinue, + canGoBack, + onBack, + screenProgress, + defaultTexts, + answers, + }) => { + const trialChoiceScreen = screen as TrialChoiceScreenDefinition; + + return ( + + ); + }, }; export function renderScreen(props: ScreenRenderProps): JSX.Element { diff --git a/src/lib/funnel/types.ts b/src/lib/funnel/types.ts index 8b97dae..18b13cb 100644 --- a/src/lib/funnel/types.ts +++ b/src/lib/funnel/types.ts @@ -557,6 +557,17 @@ export interface SpecialOfferScreenDefinition { variants?: ScreenVariantDefinition[]; } +export interface TrialChoiceScreenDefinition { + id: string; + template: "trialChoice"; + header?: HeaderDefinition; + title: TitleDefinition; + subtitle?: SubtitleDefinition; + bottomActionButton?: BottomActionButtonDefinition; + navigation?: NavigationDefinition; + variants?: ScreenVariantDefinition[]; +} + export type ScreenDefinition = | InfoScreenDefinition | DateScreenDefinition @@ -567,7 +578,8 @@ export type ScreenDefinition = | LoadersScreenDefinition | SoulmatePortraitScreenDefinition | TrialPaymentScreenDefinition - | SpecialOfferScreenDefinition; + | SpecialOfferScreenDefinition + | TrialChoiceScreenDefinition; export interface FunnelMetaDefinition { id: string; diff --git a/src/lib/models/Funnel.ts b/src/lib/models/Funnel.ts index eb2e4e1..64ab103 100644 --- a/src/lib/models/Funnel.ts +++ b/src/lib/models/Funnel.ts @@ -184,6 +184,7 @@ const ScreenDefinitionSchema = new Schema( "soulmate", "trialPayment", "specialOffer", + "trialChoice", ], required: true, },