diff --git a/.changeset/itchy-goats-juggle.md b/.changeset/itchy-goats-juggle.md
new file mode 100644
index 0000000000000..07e96e930c7b7
--- /dev/null
+++ b/.changeset/itchy-goats-juggle.md
@@ -0,0 +1,5 @@
+---
+"@medusajs/ui": minor
+---
+
+feat(ui): Add Alert component.
diff --git a/packages/admin-next/dashboard/package.json b/packages/admin-next/dashboard/package.json
index db37624a6a5f2..74fa8052d7a5f 100644
--- a/packages/admin-next/dashboard/package.json
+++ b/packages/admin-next/dashboard/package.json
@@ -28,6 +28,7 @@
"@uiw/react-json-view": "2.0.0-alpha.10",
"cmdk": "^0.2.0",
"date-fns": "^3.2.0",
+ "framer-motion": "^11.0.3",
"i18next": "23.7.11",
"i18next-browser-languagedetector": "7.2.0",
"i18next-http-backend": "2.4.2",
@@ -36,6 +37,7 @@
"react-dom": "18.2.0",
"react-hook-form": "7.49.1",
"react-i18next": "13.5.0",
+ "react-jwt": "^1.2.0",
"react-router-dom": "6.20.1",
"zod": "3.22.4"
},
@@ -48,8 +50,8 @@
"@types/react": "18.2.43",
"@types/react-dom": "18.2.17",
"@vitejs/plugin-react": "4.2.1",
- "autoprefixer": "10.4.16",
- "postcss": "8.4.32",
+ "autoprefixer": "^10.4.17",
+ "postcss": "^8.4.33",
"prettier": "^3.1.1",
"tailwindcss": "^3.4.1",
"typescript": "5.2.2",
diff --git a/packages/admin-next/dashboard/public/locales/en/translation.json b/packages/admin-next/dashboard/public/locales/en/translation.json
index 713a2c3355155..b84fd893ee27b 100644
--- a/packages/admin-next/dashboard/public/locales/en/translation.json
+++ b/packages/admin-next/dashboard/public/locales/en/translation.json
@@ -239,6 +239,43 @@
"createdBy": "Created by",
"revokedBy": "Revoked by"
},
+ "login": {
+ "forgotPassword": "Forgot password? - <0>Reset0>",
+ "title": "Log in",
+ "hint": "to continue to Medusa"
+ },
+ "invite": {
+ "title": "Create your account",
+ "hint": "to continue to Medusa",
+ "createAccount": "Create account",
+ "alreadyHaveAccount": "Already have an account? - <0>Log in0>",
+ "emailTooltip": "Your email cannot be changed. If you would like to use another email, a new invite must be sent.",
+ "invalidInvite": "The invite is invalid or has expired.",
+ "successTitle": "Your account has been created",
+ "successHint": "Get started with Medusa Admin right away.",
+ "successAction": "Start using Medusa",
+ "invalidTokenTitle": "Your invite token is invalid",
+ "invalidTokenHint": "Try requesting a new invite link."
+ },
+ "resetPassword": {
+ "title": "Reset password",
+ "hint": "Enter your email below, and we will send you instructions on how to reset your password.",
+ "email": "Email",
+ "sendResetInstructions": "Send reset instructions",
+ "backToLogin": "You can always go back - <0>Log in0>",
+ "newPasswordHint": "Choose a new password below.",
+ "invalidTokenTitle": "Your reset token is invalid",
+ "invalidTokenHint": "Try requesting a new reset link.",
+ "expiredTokenTitle": "Your reset token has expired",
+ "goToResetPassword": "Go to Reset Password",
+ "resetPassword": "Reset password",
+ "tokenExpiresIn": "Token expires in <0>{{time}}0> minutes",
+ "successfulRequest": "We have sent you an email with instructions on how to reset your password. If you don't receive an email, please check your spam folder or try again."
+ },
+ "errors": {
+ "serverError": "Server error - Try again later.",
+ "invalidCredentials": "Wrong email or password"
+ },
"fields": {
"name": "Name",
"lastName": "Last Name",
@@ -247,7 +284,10 @@
"description": "Description",
"email": "Email",
"password": "Password",
+ "repeatPassword": "Repeat Password",
"confirmPassword": "Confirm Password",
+ "newPassword": "New Password",
+ "repeatNewPassword": "Repeat New Password",
"categories": "Categories",
"category": "Category",
"collection": "Collection",
diff --git a/packages/admin-next/dashboard/src/components/common/form/form.tsx b/packages/admin-next/dashboard/src/components/common/form/form.tsx
index d6c1d5201f10f..945bbd9b544b3 100644
--- a/packages/admin-next/dashboard/src/components/common/form/form.tsx
+++ b/packages/admin-next/dashboard/src/components/common/form/form.tsx
@@ -177,7 +177,7 @@ const ErrorMessage = forwardRef<
const { error, formErrorMessageId } = useFormField()
const msg = error ? String(error?.message) : children
- if (!msg) {
+ if (!msg || msg === "undefined") {
return null
}
diff --git a/packages/admin-next/dashboard/src/components/common/logo-box/index.ts b/packages/admin-next/dashboard/src/components/common/logo-box/index.ts
new file mode 100644
index 0000000000000..ee20f53faab37
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/common/logo-box/index.ts
@@ -0,0 +1 @@
+export * from "./logo-box"
diff --git a/packages/admin-next/dashboard/src/components/common/logo-box/logo-box.tsx b/packages/admin-next/dashboard/src/components/common/logo-box/logo-box.tsx
new file mode 100644
index 0000000000000..a8d417c008722
--- /dev/null
+++ b/packages/admin-next/dashboard/src/components/common/logo-box/logo-box.tsx
@@ -0,0 +1,74 @@
+import { clx } from "@medusajs/ui"
+import { Transition, motion } from "framer-motion"
+
+type LogoBoxProps = {
+ className?: string
+ checked?: boolean
+ containerTransition?: Transition
+ pathTransition?: Transition
+}
+
+export const LogoBox = ({
+ className,
+ checked,
+ containerTransition = {
+ duration: 0.8,
+ delay: 0.5,
+ ease: [0, 0.71, 0.2, 1.01],
+ },
+ pathTransition = {
+ duration: 0.8,
+ delay: 0.6,
+ ease: [0.1, 0.8, 0.2, 1.01],
+ },
+}: LogoBoxProps) => {
+ return (
+
+ {checked && (
+
+
+
+ )}
+
+
+ )
+}
diff --git a/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx b/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx
index 7150224afb904..a0bcf40fbcd78 100644
--- a/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx
+++ b/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx
@@ -24,6 +24,7 @@ import {
import { Skeleton } from "../../common/skeleton"
+import { queryClient } from "../../../lib/medusa"
import { useSearch } from "../../../providers/search-provider"
import { useSidebar } from "../../../providers/sidebar-provider"
import { useTheme } from "../../../providers/theme-provider"
@@ -35,7 +36,7 @@ export const Shell = ({ children }: PropsWithChildren) => {
{children}
{children}
-
+
@@ -73,7 +74,7 @@ const Breadcrumbs = () => {
})
return (
-
+
{crumbs.map((crumb, index) => {
const isLast = index === crumbs.length - 1
const isSingle = crumbs.length === 1
@@ -106,7 +107,7 @@ const Breadcrumbs = () => {
)}
{/* {!isLast &&
} */}
- {!isLast &&
›}
+ {!isLast &&
›}
)
})}
@@ -115,18 +116,22 @@ const Breadcrumbs = () => {
}
const UserBadge = () => {
- const { user, isError, error } = useAdminGetSession()
+ const { user, isLoading, isError, error } = useAdminGetSession()
- const displayName = user
- ? user.first_name && user.last_name
- ? `${user.first_name} ${user.last_name}`
- : user.first_name
- ? user.first_name
- : user.email
- : null
+ const name = [user?.first_name, user?.last_name].filter(Boolean).join(" ")
+ const displayName = name || user?.email
const fallback = displayName ? displayName[0].toUpperCase() : null
+ if (isLoading) {
+ return (
+
+ )
+ }
+
if (isError) {
throw error
}
@@ -136,13 +141,13 @@ const UserBadge = () => {
@@ -203,6 +208,11 @@ const Logout = () => {
const handleLayout = async () => {
await logoutMutation(undefined, {
onSuccess: () => {
+ /**
+ * When the user logs out, we want to clear the query cache
+ */
+ queryClient.clear()
+
navigate("/login")
},
})
@@ -297,7 +307,7 @@ const Searchbar = () => {
return (