Skip to content

Commit

Permalink
feat(dashboard): Log in, reset password, and accept invite pages (#6310)
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperkristensen authored Feb 5, 2024
1 parent b1276cf commit 73fd92a
Show file tree
Hide file tree
Showing 23 changed files with 1,342 additions and 128 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-goats-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/ui": minor
---

feat(ui): Add Alert component.
6 changes: 4 additions & 2 deletions packages/admin-next/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
},
Expand All @@ -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",
Expand Down
40 changes: 40 additions & 0 deletions packages/admin-next/dashboard/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,43 @@
"createdBy": "Created by",
"revokedBy": "Revoked by"
},
"login": {
"forgotPassword": "Forgot password? - <0>Reset</0>",
"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 in</0>",
"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 in</0>",
"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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./logo-box"
Original file line number Diff line number Diff line change
@@ -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 (
<div
className={clx(
"size-14 bg-ui-button-neutral shadow-buttons-neutral relative flex items-center justify-center rounded-xl",
"after:button-neutral-gradient after:inset-0 after:content-['']",
className
)}
>
{checked && (
<motion.div
className="size-5 absolute -right-[5px] -top-1 flex items-center justify-center rounded-full border-[0.5px] border-[rgba(3,7,18,0.2)] bg-[#3B82F6] bg-gradient-to-b from-white/0 to-white/20 shadow-[0px_1px_2px_0px_rgba(3,7,18,0.12),0px_1px_2px_0px_rgba(255,255,255,0.10)_inset,0px_-1px_5px_0px_rgba(255,255,255,0.10)_inset,0px_0px_0px_0px_rgba(3,7,18,0.06)_inset]"
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
transition={containerTransition}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<motion.path
d="M5.8335 10.4167L9.16683 13.75L14.1668 6.25"
stroke="white"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={pathTransition}
/>
</svg>
</motion.div>
)}
<svg
width="36"
height="38"
viewBox="0 0 36 38"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M30.85 6.16832L22.2453 1.21782C19.4299 -0.405941 15.9801 -0.405941 13.1648 1.21782L4.52043 6.16832C1.74473 7.79208 0 10.802 0 14.0099V23.9505C0 27.198 1.74473 30.1683 4.52043 31.7921L13.1251 36.7822C15.9405 38.4059 19.3903 38.4059 22.2056 36.7822L30.8103 31.7921C33.6257 30.1683 35.3307 27.198 35.3307 23.9505V14.0099C35.41 10.802 33.6653 7.79208 30.85 6.16832ZM17.6852 27.8317C12.8079 27.8317 8.8426 23.8713 8.8426 19C8.8426 14.1287 12.8079 10.1683 17.6852 10.1683C22.5625 10.1683 26.5674 14.1287 26.5674 19C26.5674 23.8713 22.6022 27.8317 17.6852 27.8317Z"
className="fill-ui-button-inverted relative drop-shadow-sm"
/>
</svg>
</div>
)
}
46 changes: 28 additions & 18 deletions packages/admin-next/dashboard/src/components/layout/shell/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -35,7 +36,7 @@ export const Shell = ({ children }: PropsWithChildren) => {
<MobileSidebarContainer>{children}</MobileSidebarContainer>
<DesktopSidebarContainer>{children}</DesktopSidebarContainer>
</div>
<div className="flex flex-col h-screen w-full overflow-auto">
<div className="flex h-screen w-full flex-col overflow-auto">
<Topbar />
<main className="flex h-full w-full flex-col items-center overflow-y-auto">
<Gutter>
Expand Down Expand Up @@ -73,7 +74,7 @@ const Breadcrumbs = () => {
})

return (
<ol className={clx("text-ui-fg-muted flex items-center select-none")}>
<ol className={clx("text-ui-fg-muted flex select-none items-center")}>
{crumbs.map((crumb, index) => {
const isLast = index === crumbs.length - 1
const isSingle = crumbs.length === 1
Expand Down Expand Up @@ -106,7 +107,7 @@ const Breadcrumbs = () => {
</div>
)}
{/* {!isLast && <TriangleRightMini className="-mt-0.5 mx-2" />} */}
{!isLast && <span className="-mt-0.5 mx-2"></span>}
{!isLast && <span className="mx-2 -mt-0.5"></span>}
</li>
)
})}
Expand All @@ -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 (
<button className="shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5">
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-[9px] w-[70px]" />
</button>
)
}

if (isError) {
throw error
}
Expand All @@ -136,13 +141,13 @@ const UserBadge = () => {
<button
disabled={!user}
className={clx(
"shadow-borders-base flex max-w-[192px] items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5 select-none"
"shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5"
)}
>
{fallback ? (
<Avatar size="xsmall" fallback={fallback} />
) : (
<Skeleton className="w-5 h-5 rounded-full" />
<Skeleton className="h-5 w-5 rounded-full" />
)}
{displayName ? (
<Text
Expand All @@ -154,7 +159,7 @@ const UserBadge = () => {
{displayName}
</Text>
) : (
<Skeleton className="w-[70px] h-[9px]" />
<Skeleton className="h-[9px] w-[70px]" />
)}
</button>
</DropdownMenu.Trigger>
Expand Down Expand Up @@ -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")
},
})
Expand Down Expand Up @@ -297,7 +307,7 @@ const Searchbar = () => {
return (
<button
onClick={toggleSearch}
className="shadow-borders-base bg-ui-bg-subtle hover:bg-ui-bg-subtle-hover transition-fg focus-visible:shadow-borders-focus text-ui-fg-muted flex w-full max-w-[280px] items-center gap-x-2 rounded-full py-1.5 pl-2 pr-1.5 outline-none select-none"
className="shadow-borders-base bg-ui-bg-subtle hover:bg-ui-bg-subtle-hover transition-fg focus-visible:shadow-borders-focus text-ui-fg-muted flex w-full max-w-[280px] select-none items-center gap-x-2 rounded-full py-1.5 pl-2 pr-1.5 outline-none"
>
<MagnifyingGlass />
<div className="flex-1 text-left">
Expand Down Expand Up @@ -335,7 +345,7 @@ const ToggleSidebar = () => {

const Topbar = () => {
return (
<div className="w-full grid-cols-3 border-b p-3 grid">
<div className="grid w-full grid-cols-3 border-b p-3">
<div className="flex items-center gap-x-1.5">
<ToggleSidebar />
<Breadcrumbs />
Expand Down Expand Up @@ -374,8 +384,8 @@ const MobileSidebarContainer = ({ children }: PropsWithChildren) => {
return (
<Dialog.Root open={mobile} onOpenChange={() => toggle("mobile")}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-ui-bg-overlay" />
<Dialog.Content className="h-screen fixed left-0 inset-y-0 w-[220px] border-r bg-ui-bg-subtle">
<Dialog.Overlay className="bg-ui-bg-overlay fixed inset-0" />
<Dialog.Content className="bg-ui-bg-subtle fixed inset-y-0 left-0 h-screen w-[220px] border-r">
{children}
</Dialog.Content>
</Dialog.Portal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
import { ProtectedRoute } from "../../components/authentication/require-auth"
import { ErrorBoundary } from "../../components/error/error-boundary"
import { MainLayout } from "../../components/layout/main-layout"
import { PublicLayout } from "../../components/layout/public-layout"
import { SettingsLayout } from "../../components/layout/settings-layout"

import routes from "medusa-admin:routes/pages"
Expand Down Expand Up @@ -46,14 +45,28 @@ const settingsExtensions: RouteObject[] = settings.pages.map((ext) => {

const router = createBrowserRouter([
{
element: <PublicLayout />,
path: "/login",
lazy: () => import("../../routes/login"),
},
{
path: "/reset-password",
element: <Outlet />,
children: [
{
path: "/login",
lazy: () => import("../../routes/login"),
index: true,
lazy: () =>
import("../../routes/reset-password/reset-password-request"),
},
{
path: ":token",
lazy: () => import("../../routes/reset-password/reset-password-token"),
},
],
},
{
path: "/invite",
lazy: () => import("../../routes/invite"),
},
{
element: <ProtectedRoute />,
errorElement: <ErrorBoundary />,
Expand Down
1 change: 1 addition & 0 deletions packages/admin-next/dashboard/src/routes/invite/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Invite as Component } from "./invite"
Loading

0 comments on commit 73fd92a

Please sign in to comment.