Internationalization, Spacing ArrowDown, Language Bug for changing a Password, Dates to Course Page, Business Address Checking in Settings

This commit is contained in:
Rajpreet0 2026-07-12 14:56:02 +02:00
parent da330cc206
commit a756c79e34
17 changed files with 964 additions and 197 deletions

16
messages/de.json Normal file
View file

@ -0,0 +1,16 @@
{
"LoginView": {
"sidebarTitle": "Deine Reise beginnt hier",
"sidebarDescription": "Werde Teil einer Plattform, die Trainer und Lernende zusammenbringt.",
"heading": "Login",
"emailLabel": "E-mail Adresse",
"passwordLabel": "Passwort",
"rememberMe": "Beim nächsten Mal an mich erinnern",
"forgotPassword": "Passwort vergessen?",
"submit": "Anmelden",
"loginWithGoogle": "Login mit Google",
"loginWithFacebook": "Login mit Facebook",
"noAccount": "Ich habe noch keinen Account",
"signUp": "Sign Up"
}
}

16
messages/en.json Normal file
View file

@ -0,0 +1,16 @@
{
"LoginView": {
"sidebarTitle": "Your journey starts here",
"sidebarDescription": "Become part of a platform that brings trainers and learners together.",
"heading": "Login",
"emailLabel": "Email address",
"passwordLabel": "Password",
"rememberMe": "Remember me next time",
"forgotPassword": "Forgot password?",
"submit": "Sign in",
"loginWithGoogle": "Login with Google",
"loginWithFacebook": "Login with Facebook",
"noAccount": "I don't have an account yet",
"signUp": "Sign Up"
}
}

View file

@ -1,7 +1,10 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
allowedDevOrigins: ["192.168.2.111"], allowedDevOrigins: ["192.168.2.111", "192.168.2.64", "192.168.178.26", "192.168.178.178"],
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
@ -16,4 +19,4 @@ const nextConfig: NextConfig = {
}, },
}; };
export default nextConfig; export default withNextIntl(nextConfig);

783
package-lock.json generated
View file

@ -33,6 +33,7 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"next": "16.2.6", "next": "16.2.6",
"next-intl": "^4.13.2",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nodemailer": "^8.0.5", "nodemailer": "^8.0.5",
"prisma": "^6.19.0", "prisma": "^6.19.0",
@ -302,7 +303,7 @@
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -312,7 +313,7 @@
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -346,7 +347,7 @@
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.29.7" "@babel/types": "^7.29.7"
@ -438,7 +439,7 @@
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.29.7", "@babel/helper-string-parser": "^7.29.7",
@ -837,6 +838,36 @@
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@formatjs/fast-memoize": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz",
"integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==",
"license": "MIT"
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "3.5.14",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.14.tgz",
"integrity": "sha512-jDvgtoLqe3U6yzoBlToMTkWBe38qSi7LN7kFlnXzd5ig8nn+4tSlED0xtEtdYakZVZGJJY2rW1D5xS3BFZh6kA==",
"license": "MIT",
"dependencies": {
"@formatjs/icu-skeleton-parser": "2.1.11"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz",
"integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==",
"license": "MIT"
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.8.12",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.12.tgz",
"integrity": "sha512-5H3r5ZJ2jZqHEv9K343lvHmeMDKMxssawAVD2H4J9xtu0ZXb6MlNxwLqdwBxJSHFU0C24KSZnffgmAi+59mK4A==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "3.1.7"
}
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.2", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
@ -1777,6 +1808,301 @@
"url": "https://github.com/sponsors/Boshen" "url": "https://github.com/sponsors/Boshen"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
"node-addon-api": "^7.0.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.6",
"@parcel/watcher-darwin-arm64": "2.5.6",
"@parcel/watcher-darwin-x64": "2.5.6",
"@parcel/watcher-freebsd-x64": "2.5.6",
"@parcel/watcher-linux-arm-glibc": "2.5.6",
"@parcel/watcher-linux-arm-musl": "2.5.6",
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
"@parcel/watcher-linux-arm64-musl": "2.5.6",
"@parcel/watcher-linux-x64-glibc": "2.5.6",
"@parcel/watcher-linux-x64-musl": "2.5.6",
"@parcel/watcher-win32-arm64": "2.5.6",
"@parcel/watcher-win32-ia32": "2.5.6",
"@parcel/watcher-win32-x64": "2.5.6"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
"integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
"integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
"integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
"integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
"integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
"integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
"integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
"integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
"integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
"integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
"integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
"integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
"integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@prisma/client": { "node_modules/@prisma/client": {
"version": "6.19.3", "version": "6.19.3",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz",
@ -3671,6 +3997,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@schummar/icu-type-parser": {
"version": "1.21.5",
"resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
"license": "MIT"
},
"node_modules/@standard-schema/spec": { "node_modules/@standard-schema/spec": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
@ -3686,66 +4018,6 @@
"node": ">=12.16" "node": ">=12.16"
} }
}, },
"node_modules/@supabase/auth-js": {
"version": "2.108.2",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.108.2.tgz",
"integrity": "sha512-tNaQmBgodDZwgB40mRwVbxFy8IDYwjdpcZ0BYrWiwlULCSQoJj4QoG4zgJT7QRPXcqipefNOzvO/qAu4dF98ag==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.108.2",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.108.2.tgz",
"integrity": "sha512-RNUX8EiBy3iLwAX19jtRzLyePnl11/fHcgwDHLnpKcDSXt/5qBnh3LUwAtIjT21Q66QsmNUR2esrHziLCpNubw==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/phoenix": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.4.tgz",
"integrity": "sha512-Gt0pqoXuIqX/8dvG0OKp/wMCobXNH3klNbUPBNyOfN0YA1IswrM3HyWFMOPk1Jy+BRaIyDPcFx4jLBwHNmlyfQ==",
"license": "MIT",
"peer": true
},
"node_modules/@supabase/postgrest-js": {
"version": "2.108.2",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.108.2.tgz",
"integrity": "sha512-GQ28/Y8hk3CFmkb3kXH1h/AQx6JIYSQfO0CJMRVBcEKZoNy6C45cXAZ4fcJvRC5Id0cs6xnkUV0+c0rIocigsw==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.108.2",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.108.2.tgz",
"integrity": "sha512-aAGxCSUemZvQIibnCdvNvgaKib28I4rfrNjKbQ9cG1uBLwUsI7hVpGXgEbypCCDhLjQlDTAiJlu7rgljYUT73g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@supabase/phoenix": "^0.4.2",
"tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@supabase/ssr": { "node_modules/@supabase/ssr": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.7.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.7.0.tgz",
@ -3758,37 +4030,244 @@
"@supabase/supabase-js": "^2.43.4" "@supabase/supabase-js": "^2.43.4"
} }
}, },
"node_modules/@supabase/storage-js": { "node_modules/@swc/core": {
"version": "2.108.2", "version": "1.15.43",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.108.2.tgz", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.43.tgz",
"integrity": "sha512-TVZPQxXGxY2+A6yTtm77zUHsh70lBhYUEaJL8RQC+BghcX/ygiMG/rmXrNVBce30/WAeNPa8FiG8HbqlGeV05g==", "integrity": "sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==",
"license": "MIT", "hasInstallScript": true,
"peer": true, "license": "Apache-2.0",
"dependencies": { "dependencies": {
"iceberg-js": "^0.8.1", "@swc/counter": "^0.1.3",
"tslib": "2.8.1" "@swc/types": "^0.1.27"
}, },
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.15.43",
"@swc/core-darwin-x64": "1.15.43",
"@swc/core-linux-arm-gnueabihf": "1.15.43",
"@swc/core-linux-arm64-gnu": "1.15.43",
"@swc/core-linux-arm64-musl": "1.15.43",
"@swc/core-linux-ppc64-gnu": "1.15.43",
"@swc/core-linux-s390x-gnu": "1.15.43",
"@swc/core-linux-x64-gnu": "1.15.43",
"@swc/core-linux-x64-musl": "1.15.43",
"@swc/core-win32-arm64-msvc": "1.15.43",
"@swc/core-win32-ia32-msvc": "1.15.43",
"@swc/core-win32-x64-msvc": "1.15.43"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
},
"peerDependenciesMeta": {
"@swc/helpers": {
"optional": true
}
} }
}, },
"node_modules/@supabase/supabase-js": { "node_modules/@swc/core-darwin-arm64": {
"version": "2.108.2", "version": "1.15.43",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.108.2.tgz", "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.43.tgz",
"integrity": "sha512-hFhnPveb5JQg4a0QYicM0swT253YHMdfeRAl2BKHOlI5VAzuHxUGSr8RbwNLYNPauWOgQMS1H8sz8bvYlgwUfQ==", "integrity": "sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==",
"license": "MIT", "cpu": [
"peer": true, "arm64"
"dependencies": { ],
"@supabase/auth-js": "2.108.2", "license": "Apache-2.0 AND MIT",
"@supabase/functions-js": "2.108.2", "optional": true,
"@supabase/postgrest-js": "2.108.2", "os": [
"@supabase/realtime-js": "2.108.2", "darwin"
"@supabase/storage-js": "2.108.2" ],
},
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=10"
} }
}, },
"node_modules/@swc/core-darwin-x64": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.43.tgz",
"integrity": "sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.43.tgz",
"integrity": "sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.43.tgz",
"integrity": "sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.43.tgz",
"integrity": "sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-ppc64-gnu": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.43.tgz",
"integrity": "sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==",
"cpu": [
"ppc64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-s390x-gnu": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.43.tgz",
"integrity": "sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==",
"cpu": [
"s390x"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.43.tgz",
"integrity": "sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.43.tgz",
"integrity": "sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.43.tgz",
"integrity": "sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.43.tgz",
"integrity": "sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==",
"cpu": [
"ia32"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.15.43",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.43.tgz",
"integrity": "sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
"license": "Apache-2.0"
},
"node_modules/@swc/helpers": { "node_modules/@swc/helpers": {
"version": "0.5.15", "version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@ -3798,6 +4277,15 @@
"tslib": "^2.8.0" "tslib": "^2.8.0"
} }
}, },
"node_modules/@swc/types": {
"version": "0.1.27",
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz",
"integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==",
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3"
}
},
"node_modules/@tabby_ai/hijri-converter": { "node_modules/@tabby_ai/hijri-converter": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz",
@ -4725,7 +5213,7 @@
"version": "20.19.43", "version": "20.19.43",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
@ -4745,6 +5233,7 @@
"version": "19.2.17", "version": "19.2.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
@ -4754,6 +5243,7 @@
"version": "19.2.3", "version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
@ -6418,6 +6908,7 @@
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/damerau-levenshtein": { "node_modules/damerau-levenshtein": {
@ -6624,7 +7115,6 @@
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -8116,14 +8606,19 @@
"url": "https://github.com/sponsors/typicode" "url": "https://github.com/sponsors/typicode"
} }
}, },
"node_modules/iceberg-js": { "node_modules/icu-minify": {
"version": "0.8.1", "version": "4.13.2",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.13.2.tgz",
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", "integrity": "sha512-XhYQTEnBXBCyF6ERiwItFweoOXDUciujaGjIWCA7RhOCEPDfhVSTtuwfRq6HdVk7tKnYJh+yaurP96zGnaKsPg==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT", "license": "MIT",
"peer": true, "dependencies": {
"engines": { "@formatjs/icu-messageformat-parser": "^3.4.0"
"node": ">=20.0.0"
} }
}, },
"node_modules/ignore": { "node_modules/ignore": {
@ -8209,6 +8704,16 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/intl-messageformat": {
"version": "11.2.11",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.11.tgz",
"integrity": "sha512-aDG5bvFRbQvRoT2Bh9FV6yV8t7o0MjEGknZ6pnin5Wt52PJwaBOHDfvz+oPEe78Pl3InQYKugBgCdXijLj6viQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@formatjs/fast-memoize": "3.1.7",
"@formatjs/icu-messageformat-parser": "3.5.14"
}
},
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@ -8387,7 +8892,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -8449,7 +8953,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
@ -9612,6 +10115,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/next": { "node_modules/next": {
"version": "16.2.6", "version": "16.2.6",
"resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
@ -9665,6 +10177,43 @@
} }
} }
}, },
"node_modules/next-intl": {
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.13.2.tgz",
"integrity": "sha512-iCYycEP7/PE+1ue4MWBuz1qns6ESA+SGzuXxNMNN1qiYUh2fLhJPiZvHW1zZC7zMTn44aJUglOVZxUb1+hUz6g==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "^0.8.1",
"@parcel/watcher": "^2.4.1",
"@swc/core": "^1.15.2",
"icu-minify": "^4.13.2",
"negotiator": "^1.0.0",
"next-intl-swc-plugin-extractor": "^4.13.2",
"po-parser": "^2.1.1",
"use-intl": "^4.13.2"
},
"peerDependencies": {
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/next-intl-swc-plugin-extractor": {
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.13.2.tgz",
"integrity": "sha512-O30N/Y4ifzRe5Sz80jD1Qkg4VY6Zfef4SbHNNE166QkHswBf3/Kygpdd1X52sUUwTCk6uvdisO8ybfAN/VYJHQ==",
"license": "MIT"
},
"node_modules/next-themes": { "node_modules/next-themes": {
"version": "0.4.6", "version": "0.4.6",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
@ -9675,6 +10224,12 @@
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
} }
}, },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/node-exports-info": { "node_modules/node-exports-info": {
"version": "1.6.2", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz",
@ -10063,7 +10618,6 @@
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -10083,6 +10637,12 @@
"pathe": "^2.0.3" "pathe": "^2.0.3"
} }
}, },
"node_modules/po-parser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
"integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==",
"license": "MIT"
},
"node_modules/possible-typed-array-names": { "node_modules/possible-typed-array-names": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@ -11902,7 +12462,7 @@
"version": "5.9.3", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
@ -11959,7 +12519,7 @@
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"devOptional": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unrs-resolver": { "node_modules/unrs-resolver": {
@ -12072,6 +12632,27 @@
} }
} }
}, },
"node_modules/use-intl": {
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.2.tgz",
"integrity": "sha512-p6/gCromeBoec+wEuOIkPaytH77RBjW94KWv8MRsNrbI4UOx8QgwNhgl9lbPOKM/b0dpanLhCYztLEH5yQUjtg==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "^3.1.0",
"@schummar/icu-type-parser": "1.21.5",
"icu-minify": "^4.13.2",
"intl-messageformat": "^11.1.0"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/use-sidecar": { "node_modules/use-sidecar": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",

View file

@ -41,6 +41,7 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"next": "16.2.6", "next": "16.2.6",
"next-intl": "^4.13.2",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nodemailer": "^8.0.5", "nodemailer": "^8.0.5",
"prisma": "^6.19.0", "prisma": "^6.19.0",

View file

@ -1,5 +1,7 @@
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import localFont from "next/font/local"; import localFont from "next/font/local";
import { NextIntlClientProvider } from "next-intl";
import { getLocale } from "next-intl/server";
import "./globals.css"; import "./globals.css";
import { Toaster } from "../components/ui/sonner"; import { Toaster } from "../components/ui/sonner";
import { AuthInitializer } from "../components/AuthInitializer"; import { AuthInitializer } from "../components/AuthInitializer";
@ -19,25 +21,29 @@ export const viewport: Viewport = {
initialScale: 1, initialScale: 1,
}; };
export default function RootLayout({ export default async function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const locale = await getLocale();
return ( return (
<html lang="en" className={salmondFont.className}> <html lang={locale} className={salmondFont.className}>
<body className={`antialiased bg-muted`}> <body className={`antialiased bg-muted`}>
<NavigationProgress /> <NextIntlClientProvider>
<AuthInitializer /> <NavigationProgress />
<main>{children}</main> <AuthInitializer />
<Toaster <main>{children}</main>
toastOptions={{ <Toaster
style: { toastOptions={{
background: "#1E293B", style: {
color: "#F4C00C", background: "#1E293B",
}, color: "#F4C00C",
}} },
/> }}
/>
</NextIntlClientProvider>
</body> </body>
</html> </html>
); );

View file

@ -0,0 +1,38 @@
"use client";
import { useLocale } from "next-intl";
import { useTransition } from "react";
import { setLocale } from "@/src/i18n/actions";
import { locales, type Locale } from "@/src/i18n/config";
const localeLabels: Record<Locale, string> = {
de: "DE",
en: "EN",
};
const LocaleSwitcher = () => {
const activeLocale = useLocale();
const [pending, startTransition] = useTransition();
return (
<div className="flex items-center gap-1 text-sm">
{locales.map((locale) => (
<button
key={locale}
type="button"
disabled={pending || locale === activeLocale}
onClick={() => startTransition(() => setLocale(locale))}
className={`px-2 py-1 rounded-md transition ${
locale === activeLocale
? "font-semibold text-blue"
: "text-gray-400 hover:text-blue cursor-pointer"
}`}
>
{localeLabels[locale]}
</button>
))}
</div>
);
};
export default LocaleSwitcher;

View file

@ -197,7 +197,7 @@ function CalendarDayButton({
ref={ref} ref={ref}
variant="ghost" variant="ghost"
size="icon" size="icon"
data-day={day.date.toLocaleDateString()} data-day={day.date.toLocaleDateString("en-US")}
data-selected-single={ data-selected-single={
modifiers.selected && modifiers.selected &&
!modifiers.range_start && !modifiers.range_start &&

18
src/i18n/actions.ts Normal file
View file

@ -0,0 +1,18 @@
"use server";
import { cookies } from "next/headers";
import { revalidatePath } from "next/cache";
import { isLocale, localeCookieName, type Locale } from "./config";
export async function setLocale(locale: Locale): Promise<void> {
if (!isLocale(locale)) return;
const cookieStore = await cookies();
cookieStore.set(localeCookieName, locale, {
maxAge: 60 * 60 * 24 * 365,
path: "/",
sameSite: "lax",
});
revalidatePath("/");
}

10
src/i18n/config.ts Normal file
View file

@ -0,0 +1,10 @@
export const locales = ["de", "en"] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = "de";
export const localeCookieName = "KUMASY_LOCALE";
export function isLocale(value: string | undefined | null): value is Locale {
return !!value && (locales as readonly string[]).includes(value);
}

16
src/i18n/request.ts Normal file
View file

@ -0,0 +1,16 @@
import { getRequestConfig } from "next-intl/server";
import { cookies } from "next/headers";
import { defaultLocale, isLocale, localeCookieName } from "./config";
// No locale-prefixed routing: the active locale is read from a cookie
// (set via setLocale()) and defaults to German when absent.
export default getRequestConfig(async () => {
const cookieStore = await cookies();
const cookieLocale = cookieStore.get(localeCookieName)?.value;
const locale = isLocale(cookieLocale) ? cookieLocale : defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
};
});

View file

@ -8,8 +8,10 @@ import { Label } from "@/src/components/ui/label";
import { Checkbox } from "@/src/components/ui/checkbox"; import { Checkbox } from "@/src/components/ui/checkbox";
import { useState } from "react"; import { useState } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { Spinner } from "@/src/components/ui/spinner"; import { Spinner } from "@/src/components/ui/spinner";
import { useAuth } from "@/src/hooks/useAuth"; import { useAuth } from "@/src/hooks/useAuth";
import LocaleSwitcher from "@/src/components/layout/LocaleSwitcher";
const LoginView = () => { const LoginView = () => {
// ------------- States ------------- // ------------- States -------------
@ -20,6 +22,7 @@ const LoginView = () => {
// ------------- Hooks ------------- // ------------- Hooks -------------
const { loginUser, errors, loading } = useAuth(); const { loginUser, errors, loading } = useAuth();
const router = useRouter(); const router = useRouter();
const t = useTranslations("LoginView");
/** /**
* Login of a Manager/User/Employee using loginUser Function from useAuth * Login of a Manager/User/Employee using loginUser Function from useAuth
@ -39,8 +42,8 @@ const LoginView = () => {
<div className="hidden lg:block lg:w-[40%]"> <div className="hidden lg:block lg:w-[40%]">
<div className="hidden lg:block fixed left-0 top-0 h-screen w-[40%] p-2 z-10"> <div className="hidden lg:block fixed left-0 top-0 h-screen w-[40%] p-2 z-10">
<AuthSidebarComponent <AuthSidebarComponent
title="Deine Reise beginnt hier" title={t("sidebarTitle")}
description="Werde Teil einer Plattform, die Trainer und Lernende zusammenbringt." description={t("sidebarDescription")}
footer="info@since3.de" footer="info@since3.de"
/> />
</div> </div>
@ -49,12 +52,15 @@ const LoginView = () => {
{/* Login-Formular */} {/* Login-Formular */}
<div className="w-full lg:w-[60%] lg:ml-auto flex justify-center items-center min-h-screen px-4"> <div className="w-full lg:w-[60%] lg:ml-auto flex justify-center items-center min-h-screen px-4">
<div className="w-full max-w-lg flex flex-col gap-4 relative z-20"> <div className="w-full max-w-lg flex flex-col gap-4 relative z-20">
<h1 className="text-2xl font-extrabold text-blue mb-8 ">Login</h1> <div className="flex justify-end">
<LocaleSwitcher />
</div>
<h1 className="text-2xl font-extrabold text-blue mb-8 ">{t("heading")}</h1>
<form onSubmit={handleLogin}> <form onSubmit={handleLogin}>
<div className="space-y-5"> <div className="space-y-5">
<InputComponent <InputComponent
isLabel isLabel
label="E-mail Adresse" label={t("emailLabel")}
type="email" type="email"
id="login-email" id="login-email"
value={email} value={email}
@ -63,7 +69,7 @@ const LoginView = () => {
/> />
<InputComponent <InputComponent
isLabel isLabel
label="Passwort" label={t("passwordLabel")}
type="password" type="password"
id="login-password" id="login-password"
value={password} value={password}
@ -81,19 +87,19 @@ const LoginView = () => {
className="shadow-none h-5 w-5 bg-white border-blue cursor-pointer" className="shadow-none h-5 w-5 bg-white border-blue cursor-pointer"
/> />
<Label htmlFor="remembering" className="cursor-pointer"> <Label htmlFor="remembering" className="cursor-pointer">
Beim nächsten Mal an mich erinnern {t("rememberMe")}
</Label> </Label>
</div> </div>
<p <p
className="hover:underline cursor-pointer text-blue" className="hover:underline cursor-pointer text-blue"
onClick={() => router.push("/forgot-password")} onClick={() => router.push("/forgot-password")}
> >
Passwort vergessen? {t("forgotPassword")}
</p> </p>
</div> </div>
<Button type="submit" disabled={loading} className="w-full mt-6"> <Button type="submit" disabled={loading} className="w-full mt-6">
{loading ? <Spinner /> : "Anmelden"} {loading ? <Spinner /> : t("submit")}
</Button> </Button>
</form> </form>
@ -101,17 +107,17 @@ const LoginView = () => {
{/* Social Login Buttons */} {/* Social Login Buttons */}
<div className="flex flex-col sm:flex-row gap-4 mt-4"> <div className="flex flex-col sm:flex-row gap-4 mt-4">
<AuthButton Icon={FaGoogle} label="Login mit Google" /> <AuthButton Icon={FaGoogle} label={t("loginWithGoogle")} />
<AuthButton Icon={FaFacebook} label="Login mit Facebook" /> <AuthButton Icon={FaFacebook} label={t("loginWithFacebook")} />
</div> </div>
<div className="flex flex-col sm:flex-row gap-2 items-center mt-16 text-sm"> <div className="flex flex-col sm:flex-row gap-2 items-center mt-16 text-sm">
<p>Ich habe noch keinen Account</p> <p>{t("noAccount")}</p>
<p <p
className="text-blue underline cursor-pointer" className="text-blue underline cursor-pointer"
onClick={() => router.push("/register/user")} onClick={() => router.push("/register/user")}
> >
Sign Up {t("signUp")}
</p> </p>
</div> </div>
</div> </div>

View file

@ -15,6 +15,7 @@ import {
AccordionTrigger, AccordionTrigger,
} from "@/src/components/ui/accordion"; } from "@/src/components/ui/accordion";
import { Slider } from "@/src/components/ui/slider"; import { Slider } from "@/src/components/ui/slider";
import { ChevronDown } from "lucide-react";
import React, { useState } from "react"; import React, { useState } from "react";
type FilterStatus = "all" | "draft" | "published"; type FilterStatus = "all" | "draft" | "published";
@ -200,49 +201,58 @@ const CourseFilter: React.FC<CourseFilterProps> = ({
{/* Trainer */} {/* Trainer */}
<div className="space-y-1"> <div className="space-y-1">
<label className="font-medium">Trainer</label> <label className="font-medium">Trainer</label>
<select <div className="relative">
value={localTrainer} <select
onChange={(e) => setLocalTrainer(e.target.value)} value={localTrainer}
className="h-10 w-full px-3 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition" onChange={(e) => setLocalTrainer(e.target.value)}
> className="h-10 w-full pl-3 pr-9 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition appearance-none"
<option value="all">Alle</option> >
{uniqueTrainers.map((trainer) => ( <option value="all">Alle</option>
<option key={trainer.value} value={trainer.value}> {uniqueTrainers.map((trainer) => (
{trainer.label} <option key={trainer.value} value={trainer.value}>
</option> {trainer.label}
))} </option>
</select> ))}
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" />
</div>
</div> </div>
{/* Status */} {/* Status */}
<div className="space-y-1"> <div className="space-y-1">
<label className="font-medium">Status</label> <label className="font-medium">Status</label>
<select <div className="relative">
value={localStatus} <select
onChange={(e) => setLocalStatus(e.target.value as FilterStatus)} value={localStatus}
className="h-10 w-full px-3 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition" onChange={(e) => setLocalStatus(e.target.value as FilterStatus)}
> className="h-10 w-full pl-3 pr-9 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition appearance-none"
<option value="all">Alle</option> >
<option value="draft">Entwurf</option> <option value="all">Alle</option>
<option value="published">Veröffentlicht</option> <option value="draft">Entwurf</option>
</select> <option value="published">Veröffentlicht</option>
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" />
</div>
</div> </div>
{/* Sport */} {/* Sport */}
<div className="space-y-1"> <div className="space-y-1">
<label className="font-medium">Sportart</label> <label className="font-medium">Sportart</label>
<select <div className="relative">
value={localSport} <select
onChange={(e) => setLocalSport(e.target.value)} value={localSport}
className="h-10 w-full px-3 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition" onChange={(e) => setLocalSport(e.target.value)}
> className="h-10 w-full pl-3 pr-9 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition appearance-none"
<option value="all">Alle</option> >
{uniqueSports.map((sport) => ( <option value="all">Alle</option>
<option key={sport} value={sport}> {uniqueSports.map((sport) => (
{sport} <option key={sport} value={sport}>
</option> {sport}
))} </option>
</select> ))}
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" />
</div>
</div> </div>
{/* Price */} {/* Price */}
@ -275,34 +285,40 @@ const CourseFilter: React.FC<CourseFilterProps> = ({
{/* Room */} {/* Room */}
<div className="space-y-1"> <div className="space-y-1">
<label className="font-medium">Raum</label> <label className="font-medium">Raum</label>
<select <div className="relative">
value={localRoom} <select
onChange={(e) => setLocalRoom(e.target.value)} value={localRoom}
className="h-10 w-full px-3 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition" onChange={(e) => setLocalRoom(e.target.value)}
> className="h-10 w-full pl-3 pr-9 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition appearance-none"
<option value="all">Alle</option> >
{uniqueRooms.map((room) => ( <option value="all">Alle</option>
<option key={room} value={room}> {uniqueRooms.map((room) => (
{room} <option key={room} value={room}>
</option> {room}
))} </option>
</select> ))}
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" />
</div>
</div> </div>
{/* Niveau */} {/* Niveau */}
<div className="space-y-1"> <div className="space-y-1">
<label className="font-medium">Niveau</label> <label className="font-medium">Niveau</label>
<select <div className="relative">
value={localLevel} <select
onChange={(e) => setLocalLevel(e.target.value)} value={localLevel}
className="h-10 w-full px-3 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition" onChange={(e) => setLocalLevel(e.target.value)}
> className="h-10 w-full pl-3 pr-9 rounded-lg border border-gray-300 bg-gray-50 focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-300 transition appearance-none"
<option value="all">Alle</option> >
<option value="any">Jedes Niveau</option> <option value="all">Alle</option>
<option value="beginner">Anfänger</option> <option value="any">Jedes Niveau</option>
<option value="advanced">Fortgeschrittene</option> <option value="beginner">Anfänger</option>
<option value="pro">Profi</option> <option value="advanced">Fortgeschrittene</option>
</select> <option value="pro">Profi</option>
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" />
</div>
</div> </div>
{/* Uhrzeit */} {/* Uhrzeit */}

View file

@ -19,6 +19,7 @@ interface CourseListItemProps {
courseId?: string; // ID des Kurses für den Buchungslink courseId?: string; // ID des Kurses für den Buchungslink
courseName: string; // Name des Kurses courseName: string; // Name des Kurses
room: string; // Raum, in dem der Kurs stattfindet room: string; // Raum, in dem der Kurs stattfindet
date: Date; // Datum des Kurses
timeFrom: string; // Uhrzeit am Start des Kurses timeFrom: string; // Uhrzeit am Start des Kurses
timeTo: string; // Uhrzeit am Ende des Kurses timeTo: string; // Uhrzeit am Ende des Kurses
currentParticipants: number; // Aktuelle Anzahl der Teilnehmer currentParticipants: number; // Aktuelle Anzahl der Teilnehmer
@ -43,6 +44,7 @@ const CourseListItem: React.FC<CourseListItemProps> = ({
courseId, courseId,
courseName, courseName,
room, room,
date,
timeFrom, timeFrom,
timeTo, timeTo,
currentParticipants, currentParticipants,
@ -139,8 +141,15 @@ const CourseListItem: React.FC<CourseListItemProps> = ({
</AbstractTooltip> </AbstractTooltip>
); );
})} })}
{/* Time */} {/* Date & Time */}
<div className="w-full flex justify-end"> <div className="w-full flex flex-col items-end">
<p className="text-xs text-gray-400 font-light">
{new Date(date).toLocaleDateString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "numeric",
})}
</p>
<p className="text-gray-500 text-lg"> <p className="text-gray-500 text-lg">
{timeFrom} - {timeTo} {timeFrom} - {timeTo}
</p> </p>

View file

@ -411,6 +411,7 @@ const CourseListView = ({
courseId={course.id} courseId={course.id}
courseName={course.name} courseName={course.name}
room={roomsMap[course.room] || course.room} room={roomsMap[course.room] || course.room}
date={course.date}
timeFrom={course.timeFrom} timeFrom={course.timeFrom}
timeTo={course.timeTo} timeTo={course.timeTo}
currentParticipants={course._count?.bookings || 0} currentParticipants={course._count?.bookings || 0}

View file

@ -177,7 +177,13 @@ export async function updatePassword(
const { error } = await supabase.auth.updateUser({ password: newPassword }); const { error } = await supabase.auth.updateUser({ password: newPassword });
if (error) { if (error) {
return { success: false, error: error.message }; if (error.message.includes("different from the old password")) {
return {
success: false,
error: "Das neue Passwort muss sich vom aktuellen Passwort unterscheiden.",
};
}
return { success: false, error: "Fehler beim Aktualisieren des Passworts." };
} }
return { success: true }; return { success: true };

View file

@ -2,11 +2,26 @@
import { type ReactNode, useState, useTransition } from "react"; import { type ReactNode, useState, useTransition } from "react";
import InputComponent from "@/src/components/layout/InputComponent"; import InputComponent from "@/src/components/layout/InputComponent";
import AddressAutocompleteComponent, {
ParsedAddress,
} from "@/src/components/layout/AddressAutocompleteComponent";
import { Button } from "@/src/components/ui/button"; import { Button } from "@/src/components/ui/button";
import { updateBusinessInfo } from "../../actions/business-settings-actions"; import { updateBusinessInfo } from "../../actions/business-settings-actions";
import { updateProfileInfo } from "../../actions/personal-settings-actions"; import { updateProfileInfo } from "../../actions/personal-settings-actions";
import { toast } from "sonner"; import { toast } from "sonner";
// Parses "Musterstraße 1, 12345 Musterstadt" (the format used at registration)
// back into its parts so the address can be edited via AddressAutocompleteComponent.
function parseAddress(address: string): ParsedAddress {
const [streetPart, rest = ""] = address.split(",").map((part) => part.trim());
const match = rest.match(/^(\d{4,5})\s*(.*)$/);
return {
street: streetPart ?? "",
plz: match?.[1] ?? "",
place: match?.[2] ?? rest,
};
}
export type BusinessSettingsFormProps = { export type BusinessSettingsFormProps = {
businessId: string; businessId: string;
name: string; name: string;
@ -50,7 +65,10 @@ const BusinessSettingsForm = ({
}: BusinessSettingsFormProps) => { }: BusinessSettingsFormProps) => {
const [pending, startTransition] = useTransition(); const [pending, startTransition] = useTransition();
const [address, setAddress] = useState(initialAddress); const initialParsed = parseAddress(initialAddress);
const [street, setStreet] = useState(initialParsed.street);
const [plz, setPlz] = useState(initialParsed.plz);
const [place, setPlace] = useState(initialParsed.place);
const [email, setEmail] = useState(initialEmail); const [email, setEmail] = useState(initialEmail);
const [title, setTitle] = useState(initialTitle); const [title, setTitle] = useState(initialTitle);
const [ustId, setUstId] = useState(initialUstId ?? ""); const [ustId, setUstId] = useState(initialUstId ?? "");
@ -60,6 +78,7 @@ const BusinessSettingsForm = ({
const handleBusinessSave = () => { const handleBusinessSave = () => {
startTransition(async () => { startTransition(async () => {
try { try {
const address = `${street}, ${plz} ${place}`;
const result = await updateBusinessInfo({ const result = await updateBusinessInfo({
businessId, businessId,
address, address,
@ -119,14 +138,19 @@ const BusinessSettingsForm = ({
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
/> />
<InputComponent <AddressAutocompleteComponent
label="Adresse" streetValue={street}
type="text" plzValue={plz}
id="businessAddress" placeValue={place}
isLabel onStreetChange={setStreet}
placeholder="Musterstraße 1, 12345 Musterstadt" onPlzChange={setPlz}
value={address} onPlaceChange={setPlace}
onChange={(e) => setAddress(e.target.value)} onSelect={(parsed: ParsedAddress) => {
setStreet(parsed.street);
setPlz(parsed.plz);
setPlace(parsed.place);
}}
idPrefix="business-address"
/> />
</div> </div>
<div className="pt-1"> <div className="pt-1">