diff --git a/locales/sk/settings.ftl b/locales/sk/settings.ftl index eb083854dec..811023d8dc7 100644 --- a/locales/sk/settings.ftl +++ b/locales/sk/settings.ftl @@ -9,7 +9,7 @@ settings-page-title = Nastavenia { -product-short-name(case: "gen") } ## Breach alert preferences -settings-alert-email-preferences-title = Predvoľby e-mailov +settings-alert-email-preferences-title = Predvoľby e‑mailov settings-alert-email-preferences-subtitle = Povedzte nám, aké e‑maily chcete dostávať. settings-alert-preferences-allow-breach-alerts-title = Okamžité upozornenia na únik settings-alert-preferences-allow-breach-alerts-subtitle = Tieto upozornenia sa odosielajú okamžite po zistení úniku údajov diff --git a/package-lock.json b/package-lock.json index 3c16f805346..95396f17e55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,13 +57,13 @@ "@faker-js/faker": "^9.4.0", "@google-cloud/bigquery": "^7.9.1", "@playwright/test": "^1.50.0", - "@storybook/addon-a11y": "^8.5.2", - "@storybook/addon-actions": "^8.5.2", - "@storybook/addon-essentials": "^8.5.2", - "@storybook/addon-interactions": "^8.5.2", - "@storybook/addon-links": "^8.5.2", - "@storybook/nextjs": "^8.5.2", - "@storybook/react": "^8.5.2", + "@storybook/addon-a11y": "^8.5.3", + "@storybook/addon-actions": "^8.5.3", + "@storybook/addon-essentials": "^8.5.3", + "@storybook/addon-interactions": "^8.5.3", + "@storybook/addon-links": "^8.5.3", + "@storybook/nextjs": "^8.5.3", + "@storybook/react": "^8.5.3", "@storybook/test": "^8.5.2", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", @@ -8621,14 +8621,14 @@ } }, "node_modules/@storybook/addon-a11y": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.5.2.tgz", - "integrity": "sha512-GhZrDfqhZ9l6egFcyAgjO6g0iaTJCDO/H0NOAadLrw55aO1apo07H12YoWtJeA00wUqvuufmh5DGo/CExLvgSQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.5.3.tgz", + "integrity": "sha512-5dUzcLdStpv39mTemW1uLc47RZH3SzieOWUNdQfg0cW2O5LeV9Nr698YDr3Lnf9PveZ9tpNXvTS8vNdRHTj0bw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-highlight": "8.5.2", - "@storybook/test": "8.5.2", + "@storybook/addon-highlight": "8.5.3", + "@storybook/test": "8.5.3", "axe-core": "^4.2.0", "vitest-axe": "^0.1.0" }, @@ -8637,13 +8637,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-actions": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.5.2.tgz", - "integrity": "sha512-g0gLesVSFgstUq5QphsLeC1vEdwNHgqo2TE0m+STM47832xbxBwmK6uvBeqi416xZvnt1TTKaaBr4uCRRQ64Ww==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.5.3.tgz", + "integrity": "sha512-7a+SD4EZdZocm+NG1Kx4yV6Aw7+YUlRIyGvKcxsGtYMOLaqrUewApqveXF83+FbYWMoezXcoZCLQFROtS/Z6Fw==", "dev": true, "license": "MIT", "dependencies": { @@ -8658,7 +8658,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-actions/node_modules/uuid": { @@ -8675,9 +8675,9 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.5.2.tgz", - "integrity": "sha512-l9WkI4QHfINeFQkW9K0joaM7WweKktwIIyUPEvyoupHT4n9ccJHAlWjH4SBmzwI1j1Zt0G3t+bq8mVk/YK6Fsg==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.5.3.tgz", + "integrity": "sha512-sZcw8/C/HIIgbRBY+0ZYTBc5Py8xvw3bt6lzSVQEXA2aygfJpO/jiQJlmOXTmK3g5F5pjFKaaCodfXT7V/9mzw==", "dev": true, "license": "MIT", "dependencies": { @@ -8690,13 +8690,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-controls": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.5.2.tgz", - "integrity": "sha512-wkzw2vRff4zkzdvC/GOlB2PlV0i973u8igSLeg34TWNEAa4bipwVHnFfIojRuP9eN1bZL/0tjuU5pKnbTqH7aQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.5.3.tgz", + "integrity": "sha512-A4UVQhPyC7FvV+fM50xvEZO26/2uE41Ns0TN0qq7U5EH0Dlj43Salgay6qT8fve6XAI4SgVjkujPVCSbLg/yVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8709,20 +8709,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-docs": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.5.2.tgz", - "integrity": "sha512-pRLJ/Qb/3XHpjS7ZAMaOZYtqxOuI8wPxVKYQ6n5rfMSj2jFwt5tdDsEJdhj2t5lsY8HrzEZi8ExuW5I5RoUoIQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.5.3.tgz", + "integrity": "sha512-XVcQlHX963nuoeRkb7qQg89t/9CThdT46UV7jX3FFn08NEMhmDEa+4iVA4l+4xNgJ+Av6uX+u6yRGnM/910mLg==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.5.2", - "@storybook/csf-plugin": "8.5.2", - "@storybook/react-dom-shim": "8.5.2", + "@storybook/blocks": "8.5.3", + "@storybook/csf-plugin": "8.5.3", + "@storybook/react-dom-shim": "8.5.3", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" @@ -8732,7 +8732,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-docs/node_modules/react": { @@ -8773,21 +8773,21 @@ } }, "node_modules/@storybook/addon-essentials": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.5.2.tgz", - "integrity": "sha512-MfojJKxDg0bnjOE0MfLSaPweAud1Esjaf1D9M8EYnpeFnKGZApcGJNRpHCDiHrS5BMr8hHa58RDVc7ObFTI4Dw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.5.3.tgz", + "integrity": "sha512-0zbEWQQZCiYRUxMo6FrfwQER/vi+B8mCLLivdjbSVSvZsjmlpcaBA5uBjbsXfIRcedHlou4QiJXn+nR8thDlKA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-actions": "8.5.2", - "@storybook/addon-backgrounds": "8.5.2", - "@storybook/addon-controls": "8.5.2", - "@storybook/addon-docs": "8.5.2", - "@storybook/addon-highlight": "8.5.2", - "@storybook/addon-measure": "8.5.2", - "@storybook/addon-outline": "8.5.2", - "@storybook/addon-toolbars": "8.5.2", - "@storybook/addon-viewport": "8.5.2", + "@storybook/addon-actions": "8.5.3", + "@storybook/addon-backgrounds": "8.5.3", + "@storybook/addon-controls": "8.5.3", + "@storybook/addon-docs": "8.5.3", + "@storybook/addon-highlight": "8.5.3", + "@storybook/addon-measure": "8.5.3", + "@storybook/addon-outline": "8.5.3", + "@storybook/addon-toolbars": "8.5.3", + "@storybook/addon-viewport": "8.5.3", "ts-dedent": "^2.0.0" }, "funding": { @@ -8795,13 +8795,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.5.2.tgz", - "integrity": "sha512-QjJfY+8e1bi6FeGfVlgxzv/I8DUyC83lZq8zfTY7nDUCVdmKi8VzmW0KgDo5PaEOFKs8x6LKJa+s5O0gFQaJMw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.5.3.tgz", + "integrity": "sha512-xhsr3W6KTvlOIIe+8JE9/sEOAgkW0yjMZzs47A+bWcxKwcFhAUgVLbAgEzjJ0u248rjGKlCJ2pswWefO+ZKJeg==", "dev": true, "license": "MIT", "dependencies": { @@ -8812,19 +8812,19 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-interactions": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.5.2.tgz", - "integrity": "sha512-Gn9Egk2OS0BkkHd671Y0pIqBr4noAOLUfnpxhHE8r0Tt7FmJFeVSN+dqK7hQeUmKL5jdSY25FTYROg65JmtGOA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.5.3.tgz", + "integrity": "sha512-nQuP65iFGgqfVp/O8NxNDUwLTWmQBW4bofUFaT4wzYn7Jk9zobOZYtgQvdqBZtNzBDYmLrfrCutEBj5jVPRyuQ==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.5.2", - "@storybook/test": "8.5.2", + "@storybook/instrumenter": "8.5.3", + "@storybook/test": "8.5.3", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, @@ -8833,13 +8833,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-links": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.5.2.tgz", - "integrity": "sha512-eDKOQoAKKUQo0JqeLNzMLu6fm1s3oxwZ6O+rAWS6n5bsrjZS2Ul8esKkRriFVwHtDtqx99wneqOscS8IzE/ENw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.5.3.tgz", + "integrity": "sha512-MRhIif4tCoIucLgGX14dI7yptF9bYH2UaJasyywshzQZKAEjOfX19Aw5fwp2zJt6kukAF6mUxMtWKcQMH2XOmw==", "dev": true, "license": "MIT", "dependencies": { @@ -8853,7 +8853,7 @@ }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.5.2" + "storybook": "^8.5.3" }, "peerDependenciesMeta": { "react": { @@ -8862,9 +8862,9 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.5.2.tgz", - "integrity": "sha512-g7Kvrx8dqzeYWetpWYVVu4HaRzLAZVlOAlZYNfCH/aJHcFKp/p5zhPXnZh8aorxeCLHW1QSKcliaA4BNPEvTeg==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.5.3.tgz", + "integrity": "sha512-unb0bRsnISXWiCBBECxNUUdM12hHpV+1uJUu5OJHtKb26YpiQvewDFLTLjuZJ3NIAfw+F5232Q7K88AWJV6weg==", "dev": true, "license": "MIT", "dependencies": { @@ -8876,13 +8876,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-outline": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.5.2.tgz", - "integrity": "sha512-laMVLT1xluSqMa2mMzmS1kdKcjX0HI9Fw+7pM3r4drtGWtxpyBT32YFqKfWFIBhcd364ti2tDUz9FlygGQ1rKw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.5.3.tgz", + "integrity": "sha512-e1MkGN6XVdeRh2oUKGdqEDyAo2TD/47ashAAxw8DEiLRWgBMbQ+KBVH4EOG+dn5395jxh7YgRLJn/miqNnfN5g==", "dev": true, "license": "MIT", "dependencies": { @@ -8894,13 +8894,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.5.2.tgz", - "integrity": "sha512-gHQtVCiq7HRqdYQLOmX8nhtV1Lqz4tOCj4BVodwwf8fUcHyNor+2FvGlQjngV2pIeCtxiM/qmG63UpTBp57ZMA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.5.3.tgz", + "integrity": "sha512-AWr9Per9WDrbFtNlbVlj6CiEwKOvOyoBt3bCuMHuRfTdqKwkwInEtyUi4//T8U+c1qs7KJBpsWV2vhIuc5sODg==", "dev": true, "license": "MIT", "funding": { @@ -8908,13 +8908,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.5.2.tgz", - "integrity": "sha512-W+7nrMQmxHcUNGsXjmb/fak1mD0a5vf4y1hBhSM7/131t8KBsvEu4ral8LTUhc4ZzuU1eIUM0Qth7SjqHqm5bA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.5.3.tgz", + "integrity": "sha512-OkLJ2B8+PiOEAd2HtRG6XewVjtw6AkBMgoSbfKCMr6TWSbuKrOeiwIMqqieAAPVNfsOQ8hTK6JGhr/KPRCKgRA==", "dev": true, "license": "MIT", "dependencies": { @@ -8925,13 +8925,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/blocks": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.5.2.tgz", - "integrity": "sha512-C6Bz/YTG5ZuyAzglqgqozYUWaS39j1PnkVuMNots6S3Fp8ZJ6iZOlQ+rpumiuvnbfD5rkEZG+614RWNyNlFy7g==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.5.3.tgz", + "integrity": "sha512-a/PpHFmeBtVB9Q/6cNAnqfeCqMowsrI8nGka0Nl7BB3x1eJnS3I1Qo3Skht0LBEsmXOgXk4dwWxpeQL3qHMRkw==", "dev": true, "license": "MIT", "dependencies": { @@ -8946,7 +8946,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": "^8.5.2" + "storybook": "^8.5.3" }, "peerDependenciesMeta": { "react": { @@ -8958,13 +8958,13 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.5.2.tgz", - "integrity": "sha512-P4zpavhy9cL1GtITlFp1amTgNSfaQyi60jJwi7joUj0z4RRyBr8YpGi5il9PlaxiY2HROsCdKJftTNzWn058yA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.5.3.tgz", + "integrity": "sha512-5d892u2pWIN9Xp5i6ZoSYJ799C0voscmkGOLrjaWC/gqFJ6AT697z/xu3HMTFkt/mS+0cz/yFimaN8nzXwktrw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.5.2", + "@storybook/core-webpack": "8.5.3", "@types/semver": "^7.3.4", "browser-assert": "^1.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", @@ -8994,7 +8994,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" }, "peerDependenciesMeta": { "typescript": { @@ -9003,9 +9003,9 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -9023,9 +9023,9 @@ "license": "MIT" }, "node_modules/@storybook/components": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.5.2.tgz", - "integrity": "sha512-o5vNN30sGLTJBeGk5SKyekR4RfTpBTGs2LDjXGAmpl2MRhzd62ix8g+KIXSR0rQ55TCvKUl5VR2i99ttlRcEKw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.5.3.tgz", + "integrity": "sha512-iC9VbpM8Equ8wXI2syBzov+8wys4sGYW7Xfz67LdSVbCMhsH9FRtvgbDppJQC/ZDCofg4sTAHhWpDV/KAQ385A==", "dev": true, "license": "MIT", "funding": { @@ -9037,9 +9037,9 @@ } }, "node_modules/@storybook/core": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.2.tgz", - "integrity": "sha512-rCOpXZo2XbdKVnZiv8oC9FId/gLkStpKGGL7hhdg/RyjcyUyTfhsvaf7LXKZH2A0n/UpwFxhF3idRfhgc1XiSg==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.3.tgz", + "integrity": "sha512-ZLlr2pltbj/hmC54lggJTnh09FCAJR62lIdiXNwa+V+/eJz0CfD8tfGmZGKPSmaQeZBpMwAOeRM97k2oLPF+0w==", "dev": true, "license": "MIT", "dependencies": { @@ -9069,9 +9069,9 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.5.2.tgz", - "integrity": "sha512-r+s3zNojxl370CCCmvj0A+N27fW6zjRODQ7jsHWGSQzTDIz5Vj68rJBIOffr/27nN9r/JtbXbwxuO2UfqMqcqA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.5.3.tgz", + "integrity": "sha512-r1Ogdk3/cHUUbGG7QcGUwygCYfFt+4R59jx5NGrrQ2dXNANINVgPY9EATuqfIkLjFq5yvHxAQ/C5qtLfJi1SSg==", "dev": true, "license": "MIT", "dependencies": { @@ -9082,13 +9082,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/core/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -9108,9 +9108,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.5.2.tgz", - "integrity": "sha512-EEQ3Vc9qIUbLH8tunzN/GSoyP3zPpNPKegZooYQbgVqA582Pel4Jnpn4uxGaOWtFCLhXMETV05X/7chGZtEujA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.5.3.tgz", + "integrity": "sha512-u5oyXTFg3KIy4h9qoNyiCG2mJF3OpkLO/AcM4lMAwQVnBvz8pwITvr4jDZByVjGmcIbgKJQnWX+BwdK2NI4yAw==", "dev": true, "license": "MIT", "dependencies": { @@ -9121,7 +9121,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/global": { @@ -9145,10 +9145,11 @@ } }, "node_modules/@storybook/instrumenter": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.5.2.tgz", - "integrity": "sha512-BbaUw9GXVzRg3Km95t2mRu4W6C1n1erjzll5maBaVe2+lV9MbCvBcdYwGUgjFNlQ/ETgq6vLfLOEtziycq/B6g==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.5.3.tgz", + "integrity": "sha512-pxaTbGeju8MkwouIiaWX5DMWtpRruxqig8W3nZPOvzoSCCbQY+sLMQoyXxFlpGxLBjcvXivkL7AMVBKps5sFEQ==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@vitest/utils": "^2.1.1" @@ -9158,13 +9159,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/manager-api": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.5.2.tgz", - "integrity": "sha512-Cn+oINA6BOO2GmGHinGsOWnEpoBnurlZ9ekMq7H/c1SYMvQWNg5RlELyrhsnyhNd83fqFZy9Asb0RXI8oqz7DQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.5.3.tgz", + "integrity": "sha512-JtfuMgQpKIPU0ARn1jNPce8FmknpM0Ap0mppWl+KGAWWGadJPDaX/nrY/19dT1kRgIhyOnbX6tgJxII4E9dE5w==", "dev": true, "license": "MIT", "funding": { @@ -9176,9 +9177,9 @@ } }, "node_modules/@storybook/nextjs": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.5.2.tgz", - "integrity": "sha512-dvpoTWhVqeKAWDbCJtiH3oTLIw68Dn1v3PJsrJLRj8OFYEAAOQuUI8OlzVEz9R88d0D560y8F7xWJ7MGpo6Ifw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.5.3.tgz", + "integrity": "sha512-QP5+biHhPRWIOtEWspvNbGAHWzx/utyTioEwMu/P0jhpcJwDSbjmDmRy0ImDxyyRVO2jCrHOpBHU3sksiMMccw==", "dev": true, "license": "MIT", "dependencies": { @@ -9196,10 +9197,10 @@ "@babel/preset-typescript": "^7.24.1", "@babel/runtime": "^7.24.4", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/builder-webpack5": "8.5.2", - "@storybook/preset-react-webpack": "8.5.2", - "@storybook/react": "8.5.2", - "@storybook/test": "8.5.2", + "@storybook/builder-webpack5": "8.5.3", + "@storybook/preset-react-webpack": "8.5.3", + "@storybook/react": "8.5.3", + "@storybook/test": "8.5.3", "@types/semver": "^7.3.4", "babel-loader": "^9.1.3", "css-loader": "^6.7.3", @@ -9234,7 +9235,7 @@ "next": "^13.5.0 || ^14.0.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": "^8.5.2", + "storybook": "^8.5.3", "webpack": "^5.0.0" }, "peerDependenciesMeta": { @@ -9308,14 +9309,14 @@ "dev": true }, "node_modules/@storybook/preset-react-webpack": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.5.2.tgz", - "integrity": "sha512-CpRunaOl4tB7Z+1dQEG/LSAdwnCZCaKdfn+Q71k6Pk1vpR6aFlhVbbVP5kgt47vimHDKYKYBQKudP+5IjJNvFA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.5.3.tgz", + "integrity": "sha512-+I6uFcmR2dy3J8OagQLfLMCyQ/zo4O5FrzbCd5TaLxJ2q+VeWp8EIaVxracN02JabmpwhD8NcjXGD+ykUha2cw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.5.2", - "@storybook/react": "8.5.2", + "@storybook/core-webpack": "8.5.3", + "@storybook/react": "8.5.3", "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", "@types/semver": "^7.3.4", "find-up": "^5.0.0", @@ -9336,7 +9337,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": "^8.5.2" + "storybook": "^8.5.3" }, "peerDependenciesMeta": { "typescript": { @@ -9345,9 +9346,9 @@ } }, "node_modules/@storybook/preset-react-webpack/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -9358,9 +9359,9 @@ } }, "node_modules/@storybook/preview-api": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.5.2.tgz", - "integrity": "sha512-AOOaBjwnkFU40Fi68fvAnK0gMWPz6o/AmH44yDGsHgbI07UgqxLBKCTpjCGPlyQd5ezEjmGwwFTmcmq5dG8DKA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.5.3.tgz", + "integrity": "sha512-dUsuXW+KgDg4tWXOB6dk5j5gwwRUzbPvicHAY9mzbpSVScbWXuE5T/S/9hHlbtfkhFroWQgPx2eB8z3rai+7RQ==", "dev": true, "license": "MIT", "funding": { @@ -9372,18 +9373,18 @@ } }, "node_modules/@storybook/react": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.5.2.tgz", - "integrity": "sha512-hWzw9ZllfzsaBJdAoEqPQ2GdVNV4c7PkvIWM6z67epaOHqsdsKScbTMe+YAvFMPtLtOO8KblIrtU5PeD4KyMgw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.5.3.tgz", + "integrity": "sha512-QIdBSjsnwV/J919i4Fi7DlwxDKHU815t0c4B/w2KTMtKKBkk+Bge+vgVi0/lNqD3eF4w3yjVWGbkzUQZ63yiPg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.5.2", + "@storybook/components": "8.5.3", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.5.2", - "@storybook/preview-api": "8.5.2", - "@storybook/react-dom-shim": "8.5.2", - "@storybook/theming": "8.5.2" + "@storybook/manager-api": "8.5.3", + "@storybook/preview-api": "8.5.3", + "@storybook/react-dom-shim": "8.5.3", + "@storybook/theming": "8.5.3" }, "engines": { "node": ">=18.0.0" @@ -9393,10 +9394,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.5.2", + "@storybook/test": "8.5.3", "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": "^8.5.2", + "storybook": "^8.5.3", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { @@ -9429,9 +9430,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.5.2.tgz", - "integrity": "sha512-lt7XoaeWI8iPlWnWzIm/Wam9TpRFhlqP0KZJoKwDyHiCByqkeMrw5MJREyWq626nf34bOW8D6vkuyTzCHGTxKg==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.5.3.tgz", + "integrity": "sha512-kNIGk6mpXW3Wy+uS9pH9b9w/54EPJnH+QXA6MX4EQgmxhMQlGlS/l/YZp+3jsVQW4YgTmqe740qB+ccJAKZxBQ==", "dev": true, "license": "MIT", "funding": { @@ -9441,18 +9442,19 @@ "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": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/test": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.5.2.tgz", - "integrity": "sha512-F5WfD75m25ZRS19cSxCzHWJ/rH8jWwIjhBlhU+UW+5xjnTS1cJuC1yPT/5Jw0/0Aj9zG1atyfBUYnNHYtsBDYQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.5.3.tgz", + "integrity": "sha512-2smoDbtU6Qh4yk0uD18qGfW6ll7lZBzKlF58Ha1CgWR4o+jpeeTQcfDLH9gG6sNrpojF7AVzMh/aN9BDHD+Chg==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "0.1.12", "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.5.2", + "@storybook/instrumenter": "8.5.3", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", @@ -9464,7 +9466,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.5.2" + "storybook": "^8.5.3" } }, "node_modules/@storybook/test/node_modules/@testing-library/jest-dom": { @@ -9507,9 +9509,9 @@ "dev": true }, "node_modules/@storybook/theming": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.5.2.tgz", - "integrity": "sha512-vro8vJx16rIE0UehawEZbxFFA4/VGYS20PMKP6Y6Fpsce0t2/cF/U9qg3jOzVb/XDwfx+ne3/V+8rjfWx8wwJw==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.5.3.tgz", + "integrity": "sha512-Jvzw+gT1HNarkJo21WZBq5pU89qDN8u/pD3woSh/1c2h5RS6UylWjQHotPFpcBIQiUSrDFtvCU9xugJm4MD0+w==", "dev": true, "license": "MIT", "funding": { @@ -13041,9 +13043,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -15312,9 +15314,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -23688,13 +23690,13 @@ "peer": true }, "node_modules/storybook": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.2.tgz", - "integrity": "sha512-pf84emQ7Pd5jBdT2gzlNs4kRaSI3pq0Lh8lSfV+YqIVXztXIHU+Lqyhek2Lhjb7btzA1tExrhJrgQUsIji7i7A==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.3.tgz", + "integrity": "sha512-2WtNBZ45u1AhviRU+U+ld588tH8gDa702dNSq5C8UBaE9PlOsazGsyp90dw1s9YRvi+ejrjKAupQAU0GwwUiVg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.5.2" + "@storybook/core": "8.5.3" }, "bin": { "getstorybook": "bin/index.cjs", diff --git a/package.json b/package.json index 4ba9a2d2859..d56254dee13 100644 --- a/package.json +++ b/package.json @@ -120,13 +120,13 @@ "@faker-js/faker": "^9.4.0", "@google-cloud/bigquery": "^7.9.1", "@playwright/test": "^1.50.0", - "@storybook/addon-a11y": "^8.5.2", - "@storybook/addon-actions": "^8.5.2", - "@storybook/addon-essentials": "^8.5.2", - "@storybook/addon-interactions": "^8.5.2", - "@storybook/addon-links": "^8.5.2", - "@storybook/nextjs": "^8.5.2", - "@storybook/react": "^8.5.2", + "@storybook/addon-a11y": "^8.5.3", + "@storybook/addon-actions": "^8.5.3", + "@storybook/addon-essentials": "^8.5.3", + "@storybook/addon-interactions": "^8.5.3", + "@storybook/addon-links": "^8.5.3", + "@storybook/nextjs": "^8.5.3", + "@storybook/react": "^8.5.3", "@storybook/test": "^8.5.2", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", diff --git a/public/images/email/mozilla-logo-bw.png b/public/images/email/mozilla-logo-bw.png index 7065549c23d..d30afab8864 100644 Binary files a/public/images/email/mozilla-logo-bw.png and b/public/images/email/mozilla-logo-bw.png differ diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/actions.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/actions.tsx index 579c67f9095..68e02bcac64 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/actions.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/actions.tsx @@ -9,17 +9,37 @@ import { deleteSubscriberChurns, } from "../../../../../../db/tables/subscriber_churns"; import { SubscriberChurnRow } from "knex/types/tables"; +import { getServerSession } from "../../../../../functions/server/getServerSession"; +import { isAdmin } from "../../../../../api/utils/auth"; + +/** + * Helper function to perform session + admin check. + * Returns true if the current session belongs to an admin user. + */ +async function isAuthorized(): Promise { + const session = await getServerSession(); + return Boolean(session?.user?.email && isAdmin(session.user.email)); +} export async function getAllChurns() { + if (!(await isAuthorized())) { + return null; + } return getAllSubscriberChurns(); } export async function upsertAllChurns( churningSubscribers: SubscriberChurnRow[], ) { + if (!(await isAuthorized())) { + return null; + } return upsertSubscriberChurns(churningSubscribers); } export async function clearAllChurns() { + if (!(await isAuthorized())) { + return null; + } return deleteSubscriberChurns(); } diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/page.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/page.tsx index 62e508d1202..b989eead384 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/page.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/admin/churn-subscribers/page.tsx @@ -18,7 +18,7 @@ export default async function DevPage() { return ( ); diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/PlusExpiration.test.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/PlusExpiration.test.tsx index 1e86331681e..1138ab00f65 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/PlusExpiration.test.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/PlusExpiration.test.tsx @@ -32,10 +32,15 @@ it("includes a link to the terms on the renewal page", () => { it("confirms the renewal after it's applied", async () => { const user = userEvent.setup(); + const applyCouponPromise = new Promise((resolve) => { + resolve({ + success: true, + }); + }); const PlusExpirationView = composeStory(HappyPath, Meta); render( , ); @@ -49,6 +54,7 @@ it("confirms the renewal after it's applied", async () => { name: "Renew subscription", }); await user.click(renewalButton); + await applyCouponPromise; expect( screen.getByRole("heading", { @@ -59,16 +65,20 @@ it("confirms the renewal after it's applied", async () => { it("shows an error if applying the coupon failed", async () => { const user = userEvent.setup(); + const applyCouponPromise = new Promise((resolve) => { + resolve({ + success: false, + error: "apply_renewal_coupon_error", + }); + }); const PlusExpirationView = composeStory(HappyPath, Meta); render( , ); + await applyCouponPromise; expect( screen.queryByText(/Couldn’t renew subscription./), ).not.toBeInTheDocument(); @@ -83,13 +93,16 @@ it("shows an error if applying the coupon failed", async () => { it("hides the error after dismissing it", async () => { const user = userEvent.setup(); + const applyCouponErrorPromise = new Promise((resolve) => { + resolve({ + success: false, + error: "apply_renewal_coupon_error", + }); + }); const PlusExpirationView = composeStory(HappyPath, Meta); render( , ); @@ -97,6 +110,7 @@ it("hides the error after dismissing it", async () => { name: "Renew subscription", }); await user.click(renewalButton); + await applyCouponErrorPromise; expect(screen.getByText(/Couldn’t renew subscription./)).toBeInTheDocument(); @@ -110,15 +124,23 @@ it("hides the error after dismissing it", async () => { it("allows retrying after an error", async () => { const user = userEvent.setup(); const PlusExpirationView = composeStory(HappyPath, Meta); + const applyCouponErrorPromise = new Promise((resolve) => { + resolve({ + success: false, + error: "apply_renewal_coupon_error", + }); + }); + const applyCouponSuccessPromise = new Promise((resolve) => { + resolve({ + success: true, + }); + }); render( , ); @@ -126,11 +148,13 @@ it("allows retrying after an error", async () => { name: "Renew subscription", }); await user.click(renewalButton); + await applyCouponErrorPromise; expect(screen.getByText(/Couldn’t renew subscription./)).toBeInTheDocument(); const retryButton = screen.getByRole("button", { name: "Try again" }); await user.click(retryButton); + await applyCouponSuccessPromise; expect( screen.queryByText(/Couldn’t renew subscription./), ).not.toBeInTheDocument(); diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/page.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/page.tsx index cbc551cb798..caafcf682b4 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/page.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/plus-expiration/page.tsx @@ -11,7 +11,7 @@ import { logger } from "../../../../../functions/server/logging"; import { checkChurnCouponCode } from "../../../../../functions/server/applyCoupon"; import { applyRenewalCoupon } from "./actions"; import { getEnabledFeatureFlags } from "../../../../../../db/tables/featureFlags"; -import { getChurnsToEmail } from "../../../../../../db/tables/subscriber_churns"; +import { getUpcomingChurns } from "../../../../../../db/tables/subscriber_churns"; export default async function PlusExpirationPage() { const session = await getServerSession(); @@ -41,7 +41,7 @@ export default async function PlusExpirationPage() { } const couponCheckResult = await checkChurnCouponCode(subscriber); - const subscribersToEmail = await getChurnsToEmail(); + const expiringSubscriptions = await getUpcomingChurns(); return ( - subscriberToEmail.userid === subscriber.fxa_uid, + typeof expiringSubscriptions.find( + (expiringSubscription) => + expiringSubscription.userid === subscriber.fxa_uid, ) !== "undefined" } /> diff --git a/src/app/(proper_react)/images/mozilla-logo.svg b/src/app/(proper_react)/images/mozilla-logo.svg index 46bbaabb5dd..9959aa61379 100644 --- a/src/app/(proper_react)/images/mozilla-logo.svg +++ b/src/app/(proper_react)/images/mozilla-logo.svg @@ -1,7 +1,2 @@ - - - - - - - + + diff --git a/src/app/components/client/csat_survey/CsatSurvey.test.tsx b/src/app/components/client/csat_survey/CsatSurvey.test.tsx index 13c66c7f871..df3a3e066c9 100644 --- a/src/app/components/client/csat_survey/CsatSurvey.test.tsx +++ b/src/app/components/client/csat_survey/CsatSurvey.test.tsx @@ -39,7 +39,7 @@ describe("CSAT survey banner: Automatic Removal", () => { expect(answerButton).toBeInTheDocument(); }); - it.each([90, 180, 351])( + it.each([90, 180, 351, 716])( "displays the survey to users with automatic data removal enabled for at least n days", (dayCount) => { const ComposedCsatSurvey = composeStory(CsatSurveyAutomaticRemoval, Meta); @@ -115,7 +115,7 @@ describe("CSAT survey banner: Automatic Removal", () => { it("shows the correct follow-up feedback link for response “Satisfied”", async () => { const user = userEvent.setup(); const ComposedCsatSurvey = composeStory(CsatSurveyAutomaticRemoval, Meta); - render(); + render(); const answerButton = screen.getByRole("button", { name: "Satisfied", @@ -128,7 +128,7 @@ describe("CSAT survey banner: Automatic Removal", () => { expect(feedbackLink).toBeInTheDocument(); expect(feedbackLink).toHaveAttribute( "href", - "https://survey.alchemer.com/s3/7718223/fbbb597a762a", + "https://survey.alchemer.com/s3/8176616/091e554aa6ab", ); }); diff --git a/src/app/components/client/csat_survey/surveys/automaticRemovalCsatSurvey.ts b/src/app/components/client/csat_survey/surveys/automaticRemovalCsatSurvey.ts index 4cecc2d4f9a..5e85c960fa3 100644 --- a/src/app/components/client/csat_survey/surveys/automaticRemovalCsatSurvey.ts +++ b/src/app/components/client/csat_survey/surveys/automaticRemovalCsatSurvey.ts @@ -14,7 +14,7 @@ import { } from "./csatSurvey"; export type AutomaticRemovalVariation = { - id: "initial" | "3-months" | "6-months" | "12-months"; + id: "initial" | "3-months" | "6-months" | "12-months" | "24-months"; showForUser: UserType[]; showOnTab: TabType[]; daysThreshold: number; @@ -87,6 +87,20 @@ const surveyData: SurveyData = { "very-satisfied": "https://survey.alchemer.com/s3/7718562/002e20b6b82f", }, }, + { + id: "24-months", + showForUser: ["plus-user"], + showOnTab: ["fixed"], + daysThreshold: 716, + followUpSurveyOptions: { + "very-dissatisfied": + "https://survey.alchemer.com/s3/8176616/edb0c3f18778", + dissatisfied: "https://survey.alchemer.com/s3/8176616/9b6ca1d007a5", + neutral: "https://survey.alchemer.com/s3/8176616/37955cef0c5e", + satisfied: "https://survey.alchemer.com/s3/8176616/091e554aa6ab", + "very-satisfied": "https://survey.alchemer.com/s3/8176616/7ac85d1249cd", + }, + }, ], }; diff --git a/src/db/tables/subscriber_churns.ts b/src/db/tables/subscriber_churns.ts index 5772694a5b1..2789a5a9ed9 100644 --- a/src/db/tables/subscriber_churns.ts +++ b/src/db/tables/subscriber_churns.ts @@ -59,6 +59,29 @@ async function getAllSubscriberChurns(): Promise { } } +async function getUpcomingChurns(): Promise> { + try { + const res = await knex("subscriber_churns") + .select("*") + .where("intervl", "year") + .whereNotNull("current_period_end") + .where(knex.raw("current_period_end::timestamptz"), ">=", knex.fn.now()) + .where( + knex.raw("current_period_end::timestamptz"), + "<=", + knex.raw("CURRENT_TIMESTAMP + interval '7 days'"), + ); + + logger.info("get_upcoming_churns_success", { count: res.length }); + return res; + } catch (e) { + logger.error("get_upcoming_churns_error", { + error: JSON.stringify(e), + }); + throw e; + } +} + async function getChurnsToEmail(): Promise< Array > { @@ -92,5 +115,6 @@ export { upsertSubscriberChurns, getAllSubscriberChurns, deleteSubscriberChurns, + getUpcomingChurns, getChurnsToEmail, };