@stoker-platform/web-app 0.5.118 → 0.5.120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stoker-platform/web-app
2
2
 
3
+ ## 0.5.120
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: improve mobile collection transitions
8
+
9
+ ## 0.5.119
10
+
11
+ ### Patch Changes
12
+
13
+ - fix: fix UI flicker when changing collection on mobile
14
+
3
15
  ## 0.5.118
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoker-platform/web-app",
3
- "version": "0.5.118",
3
+ "version": "0.5.120",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "scripts": {
package/src/App.css CHANGED
@@ -1,3 +1,9 @@
1
+ html,
2
+ body {
3
+ background-color: #000000 !important;
4
+ background-image: none !important;
5
+ }
6
+
1
7
  @media (min-width: 1280px) {
2
8
  html,
3
9
  body {
@@ -8,6 +14,7 @@
8
14
  /* Print */
9
15
 
10
16
  @media print {
17
+ html,
11
18
  body {
12
19
  background-color: white !important;
13
20
  background-image: none !important;
@@ -1687,10 +1687,20 @@ function Collection({
1687
1687
  [recordTitle],
1688
1688
  )
1689
1689
 
1690
+ const [showCollection, setShowCollection] = useState(window.innerWidth > 1280)
1691
+ useEffect(() => {
1692
+ if (isInitialized) {
1693
+ setTimeout(() => {
1694
+ setShowCollection(true)
1695
+ }, 150)
1696
+ }
1697
+ }, [isInitialized])
1698
+
1690
1699
  if (!permissions.collections?.[labels.collection]) return null
1691
1700
 
1692
1701
  return (
1693
- !collection.singleton && (
1702
+ !collection.singleton &&
1703
+ showCollection && (
1694
1704
  <>
1695
1705
  <div
1696
1706
  ref={mainContentRef}
@@ -2535,17 +2545,17 @@ function Collection({
2535
2545
  ) : (
2536
2546
  !relationList &&
2537
2547
  (tab === "list" ? (
2538
- <div className="py-2">
2539
- <Card className="h-[calc(100vh-250px)]"></Card>
2548
+ <div className="pb-2 pt-[88px] lg:py-2">
2549
+ <Card className="min-h-[calc(100vh-88px)] lg:min-h-full lg:h-[calc(100vh-250px)]"></Card>
2540
2550
  </div>
2541
2551
  ) : tab === "map" ? (
2542
- <div className="py-2">
2543
- <Card className="h-[calc(100vh-204px)]"></Card>
2552
+ <div className="pb-2 pt-[88px] lg:py-2">
2553
+ <Card className="min-h-[calc(100vh-88px)] lg:min-h-full lg:h-[calc(100vh-204px)]"></Card>
2544
2554
  </div>
2545
2555
  ) : (
2546
2556
  tab === "calendar" && (
2547
- <div className="py-2">
2548
- <Card className="h-[calc(100vh-186px)]"></Card>
2557
+ <div className="pb-2 pt-[44px] lg:py-2">
2558
+ <Card className="min-h-[calc(100vh-44px)] lg:min-h-full lg:h-[calc(100vh-204px)]"></Card>
2549
2559
  </div>
2550
2560
  )
2551
2561
  ))
package/src/Tenant.tsx CHANGED
@@ -60,7 +60,7 @@ import { getFunctions, httpsCallable } from "firebase/functions"
60
60
  import { getApp } from "firebase/app"
61
61
  import { useTheme } from "./components/theme-provider"
62
62
 
63
- const MOBILE_SHEET_CLOSE_MS = 320
63
+ const MOBILE_SHEET_CLOSE_MS = 400
64
64
 
65
65
  function Tenant() {
66
66
  const [dialogContent, setDialogContent] = useDialog()
@@ -104,6 +104,10 @@ function Tenant() {
104
104
  const [search, setSearch] = useState<string>("")
105
105
  const [searchFocused, setSearchFocused] = useState(false)
106
106
  const [background, setBackground] = useState<Background | undefined>(undefined)
107
+ const [backgroundStyle, setBackgroundStyle] = useState<{
108
+ backgroundColor: string
109
+ backgroundImage: string
110
+ }>({ backgroundColor: "transparent", backgroundImage: "none" })
107
111
  const [sidebarOpen, setSidebarOpen] = useState(false)
108
112
 
109
113
  const { unsubscribe } = useCache()
@@ -242,29 +246,18 @@ function Tenant() {
242
246
 
243
247
  useEffect(() => {
244
248
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches
249
+ const resolveTheme = theme === "system" || theme === undefined ? (prefersDark ? "dark" : "light") : theme
245
250
  if (background) {
246
- if (theme === "light") {
247
- document.body.style.backgroundColor = background?.light?.color || "transparent"
248
- document.body.style.backgroundImage = background?.light?.image || "none"
249
- } else if (theme === "dark") {
250
- document.body.style.backgroundColor = background?.dark?.color || "transparent"
251
- document.body.style.backgroundImage = background?.dark?.image || "none"
252
- } else if (prefersDark) {
253
- document.body.style.backgroundColor = background?.dark?.color || "transparent"
254
- document.body.style.backgroundImage = background?.dark?.image || "none"
255
- } else {
256
- document.body.style.backgroundColor = background?.light?.color || "transparent"
257
- document.body.style.backgroundImage = background?.light?.image || "none"
258
- }
251
+ const variant = resolveTheme === "dark" ? background.dark : background.light
252
+ setBackgroundStyle({
253
+ backgroundColor: variant?.color || "transparent",
254
+ backgroundImage: variant?.image || "none",
255
+ })
259
256
  } else {
260
- if (theme === "dark") {
261
- document.body.style.backgroundColor = "#000000"
262
- } else if (theme === "light") {
263
- document.body.style.backgroundColor = "#ffffff"
264
- } else {
265
- document.body.style.backgroundColor = prefersDark ? "#000000" : "#ffffff"
266
- }
267
- document.body.style.backgroundImage = "none"
257
+ setBackgroundStyle({
258
+ backgroundColor: resolveTheme === "dark" ? "#000000" : "#ffffff",
259
+ backgroundImage: "none",
260
+ })
268
261
  }
269
262
  }, [background, theme])
270
263
 
@@ -297,9 +290,9 @@ function Tenant() {
297
290
 
298
291
  const navigateFromSidebar = useCallback(
299
292
  (path: string) => {
300
- setSidebarOpen(false)
293
+ runViewTransition(() => navigate(path))
301
294
  window.setTimeout(() => {
302
- runViewTransition(() => navigate(path))
295
+ setSidebarOpen(false)
303
296
  }, MOBILE_SHEET_CLOSE_MS)
304
297
  },
305
298
  [navigate],
@@ -504,269 +497,283 @@ function Tenant() {
504
497
  return (
505
498
  appName &&
506
499
  collectionTitles && (
507
- <>
508
- <Helmet>
509
- {meta?.description && <meta name="description" content={meta.description} />}
510
- {meta?.icons ? (
511
- meta.icons.map((icon) => (
512
- <link key={icon.rel} rel={icon.rel} type={icon.type} href={icon.url} />
513
- ))
514
- ) : (
515
- <link key="favicon" rel="icon" type="image/png" href="./favicon.ico" />
516
- )}
517
- </Helmet>
518
-
519
- <Dialog
520
- open={dialogContent !== null}
521
- onOpenChange={() => {
522
- if (!dialogContent?.disableClose) {
523
- setDialogContent(null)
524
- }
525
- }}
526
- >
527
- <DialogContent
528
- onEscapeKeyDown={(e) => {
529
- if (dialogContent?.disableClose) {
530
- e.preventDefault()
500
+ <div className="relative isolate min-h-dvh">
501
+ <div
502
+ aria-hidden="true"
503
+ className="absolute inset-0 z-0 pointer-events-none print:hidden"
504
+ style={backgroundStyle}
505
+ />
506
+ <div className="relative z-10 flex min-h-dvh flex-col">
507
+ <Helmet>
508
+ {meta?.description && <meta name="description" content={meta.description} />}
509
+ {meta?.icons ? (
510
+ meta.icons.map((icon) => (
511
+ <link key={icon.rel} rel={icon.rel} type={icon.type} href={icon.url} />
512
+ ))
513
+ ) : (
514
+ <link key="favicon" rel="icon" type="image/png" href="./favicon.ico" />
515
+ )}
516
+ </Helmet>
517
+
518
+ <Dialog
519
+ open={dialogContent !== null}
520
+ onOpenChange={() => {
521
+ if (!dialogContent?.disableClose) {
522
+ setDialogContent(null)
531
523
  }
532
524
  }}
533
- hideCloseButton={dialogContent?.disableClose}
534
525
  >
535
- <DialogHeader>
536
- <DialogTitle>{dialogContent?.title}</DialogTitle>
537
- <DialogDescription>{dialogContent?.description}</DialogDescription>
538
- </DialogHeader>
539
-
540
- {mfaDialog && (
541
- <div className="flex flex-col gap-4 items-center">
542
- <QRCodeSVG value={mfaDialog.totpUri} size={180} />
543
- <div className="w-full flex flex-col items-center">
544
- <label htmlFor="mfa-code" className="mb-1">
545
- Enter 6-digit code
546
- </label>
547
- <Input
548
- id="mfa-code"
549
- type="text"
550
- inputMode="numeric"
551
- pattern="[0-9]{6}"
552
- maxLength={6}
553
- value={mfaCode}
554
- onChange={(e) => {
555
- const value = e.target.value.replace(/[^0-9]/g, "")
556
- setMfaCode(value)
557
- mfaCodeRef.current = value
558
- setMfaError("")
559
- }}
560
- className="text-center w-32"
561
- />
562
- {mfaError && <div className="text-destructive text-xs mt-1">{mfaError}</div>}
563
- </div>
564
- </div>
565
- )}
566
-
567
- {(!dialogContent?.disableClose || dialogContent?.buttons) && (
568
- <DialogFooter>
569
- <div className="flex gap-2 justify-end">
570
- {dialogContent?.buttons?.map((button) => (
571
- <Button key={button.label} onClick={button.onClick}>
572
- {button.label}
573
- </Button>
574
- ))}
575
- {!dialogContent?.disableClose && (
576
- <Button onClick={() => setDialogContent(null)} variant="outline">
577
- Close
578
- </Button>
579
- )}
580
- </div>
581
- </DialogFooter>
582
- )}
583
- </DialogContent>
584
- </Dialog>
585
-
586
- <header className="sticky top-0 z-50 flex h-16 items-center gap-4 border-b border-[rgb(39,39,42)] bg-black px-4 lg:px-6 print:hidden select-none">
587
- <nav className="hidden h-full flex-col gap-6 text-lg font-medium lg:flex lg:flex-row lg:items-center lg:text-sm lg:gap-6 dark">
588
- <button
589
- className="flex h-full items-center gap-2"
590
- key="home"
591
- onClick={() => {
592
- runViewTransition(() => navigate("/"))
526
+ <DialogContent
527
+ onEscapeKeyDown={(e) => {
528
+ if (dialogContent?.disableClose) {
529
+ e.preventDefault()
530
+ }
593
531
  }}
532
+ hideCloseButton={dialogContent?.disableClose}
594
533
  >
595
- <img src={logo || defaultLogo} alt="Logo" className="h-8 mr-2" />
596
- </button>
597
- {links}
598
- </nav>
599
- <div className="hidden items-center text-sm font-medium gap-4 lg:ml-auto lg:flex lg:gap-4">
600
- {isLoading && connectionStatus === "online" && <LoadingSpinner size={7} dark />}
601
- {connectionStatus === "offline" && <Badge variant="destructive">Offline</Badge>}
602
- {showSearchAll && (
603
- <div className="ml-auto flex-1 lg:flex-initial text-primary dark">
604
- <Popover open={searchFocused && Boolean(search)}>
605
- <PopoverTrigger asChild>
606
- <search className="relative">
607
- <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
608
- <Input
609
- type="search"
610
- value={search}
611
- onChange={(e) => setSearch(e.target.value)}
612
- placeholder="Search all..."
613
- className="pl-8 lg:w-[195px] xl:w-[275px]"
614
- onFocus={() => {
615
- setTimeout(() => {
616
- setSearchFocused(true)
617
- }, 100)
618
- }}
619
- onBlur={() => setSearchFocused(false)}
620
- onKeyDown={(e) => {
621
- if (e.key === "Escape") {
622
- setSearchFocused(false)
623
- } else {
624
- setSearchFocused(true)
625
- }
626
- }}
627
- />
628
- </search>
629
- </PopoverTrigger>
630
- {search && showSearchAll && <SearchAll query={search} />}
631
- </Popover>
632
- </div>
633
- )}
634
- <ModeToggle />
635
- {enableMfa && (mfaActive || mfaEnabled) && !mfaRevoked && (
636
- <DropdownMenu>
637
- <DropdownMenuTrigger asChild>
638
- <Button variant="outline" size="icon">
639
- <User />
640
- </Button>
641
- </DropdownMenuTrigger>
642
- <DropdownMenuContent className="w-56 dark print:hidden">
643
- <DropdownMenuGroup>
644
- <DropdownMenuItem
645
- onClick={() => {
646
- revokeMfa()
534
+ <DialogHeader>
535
+ <DialogTitle>{dialogContent?.title}</DialogTitle>
536
+ <DialogDescription>{dialogContent?.description}</DialogDescription>
537
+ </DialogHeader>
538
+
539
+ {mfaDialog && (
540
+ <div className="flex flex-col gap-4 items-center">
541
+ <QRCodeSVG value={mfaDialog.totpUri} size={180} />
542
+ <div className="w-full flex flex-col items-center">
543
+ <label htmlFor="mfa-code" className="mb-1">
544
+ Enter 6-digit code
545
+ </label>
546
+ <Input
547
+ id="mfa-code"
548
+ type="text"
549
+ inputMode="numeric"
550
+ pattern="[0-9]{6}"
551
+ maxLength={6}
552
+ value={mfaCode}
553
+ onChange={(e) => {
554
+ const value = e.target.value.replace(/[^0-9]/g, "")
555
+ setMfaCode(value)
556
+ mfaCodeRef.current = value
557
+ setMfaError("")
647
558
  }}
648
- >
649
- Revoke MFA
650
- </DropdownMenuItem>
651
- </DropdownMenuGroup>
652
- </DropdownMenuContent>
653
- </DropdownMenu>
654
- )}
655
- <button className="text-muted-foreground hover:text-foreground dark" onClick={signOutUser}>
656
- Sign Out
657
- </button>
658
- </div>
659
-
660
- <Sheet open={sidebarOpen} onOpenChange={setSidebarOpen}>
661
- <SheetTrigger asChild>
662
- <Button size="icon" variant="outline" className="absolute left-4 lg:hidden dark">
663
- <PanelLeft className="h-5 w-5 text-primary" />
664
- <span className="sr-only">Toggle Menu</span>
665
- </Button>
666
- </SheetTrigger>
667
- <SheetContent side="left" className="sm:max-w-xs overflow-y-auto">
668
- <nav className="grid gap-6 text-lg font-medium pt-12">
669
- {hasDashboard && (
670
- <button
671
- className={
672
- window.location.pathname === "/"
673
- ? "flex items-center gap-4 px-2.5 text-foreground"
674
- : "flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
675
- }
676
- onClick={() => navigateFromSidebar("/")}
677
- >
678
- <ChartBar className="h-5 w-5" />
679
- Dashboard
680
- </button>
681
- )}
682
- {sidebarMenu.map((group) => {
683
- if (group.collections.length === 1) {
684
- const className = "flex items-center gap-4 px-2.5 text-primary"
685
- return (
686
- <button
687
- key={group.collections[0]}
688
- className={
689
- isMatch(group.collections[0])
690
- ? cn(className, "text-foreground")
691
- : cn(className, "text-muted-foreground hover:text-foreground")
692
- }
693
- onClick={() =>
694
- navigateFromSidebar(`/${group.collections[0].toLowerCase()}`)
695
- }
559
+ className="text-center w-32"
560
+ />
561
+ {mfaError && <div className="text-destructive text-xs mt-1">{mfaError}</div>}
562
+ </div>
563
+ </div>
564
+ )}
565
+
566
+ {(!dialogContent?.disableClose || dialogContent?.buttons) && (
567
+ <DialogFooter>
568
+ <div className="flex gap-2 justify-end">
569
+ {dialogContent?.buttons?.map((button) => (
570
+ <Button key={button.label} onClick={button.onClick}>
571
+ {button.label}
572
+ </Button>
573
+ ))}
574
+ {!dialogContent?.disableClose && (
575
+ <Button onClick={() => setDialogContent(null)} variant="outline">
576
+ Close
577
+ </Button>
578
+ )}
579
+ </div>
580
+ </DialogFooter>
581
+ )}
582
+ </DialogContent>
583
+ </Dialog>
584
+
585
+ <header className="sticky top-0 z-50 flex h-16 items-center gap-4 border-b border-[rgb(39,39,42)] bg-black px-4 lg:px-6 print:hidden select-none">
586
+ <nav className="hidden h-full flex-col gap-6 text-lg font-medium lg:flex lg:flex-row lg:items-center lg:text-sm lg:gap-6 dark">
587
+ <button
588
+ className="flex h-full items-center gap-2"
589
+ key="home"
590
+ onClick={() => {
591
+ runViewTransition(() => navigate("/"))
592
+ }}
593
+ >
594
+ <img src={logo || defaultLogo} alt="Logo" className="h-8 mr-2" />
595
+ </button>
596
+ {links}
597
+ </nav>
598
+ <div className="hidden items-center text-sm font-medium gap-4 lg:ml-auto lg:flex lg:gap-4">
599
+ {isLoading && connectionStatus === "online" && <LoadingSpinner size={7} dark />}
600
+ {connectionStatus === "offline" && <Badge variant="destructive">Offline</Badge>}
601
+ {showSearchAll && (
602
+ <div className="ml-auto flex-1 lg:flex-initial text-primary dark">
603
+ <Popover open={searchFocused && Boolean(search)}>
604
+ <PopoverTrigger asChild>
605
+ <search className="relative">
606
+ <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
607
+ <Input
608
+ type="search"
609
+ value={search}
610
+ onChange={(e) => setSearch(e.target.value)}
611
+ placeholder="Search all..."
612
+ className="pl-8 lg:w-[195px] xl:w-[275px]"
613
+ onFocus={() => {
614
+ setTimeout(() => {
615
+ setSearchFocused(true)
616
+ }, 100)
617
+ }}
618
+ onBlur={() => setSearchFocused(false)}
619
+ onKeyDown={(e) => {
620
+ if (e.key === "Escape") {
621
+ setSearchFocused(false)
622
+ } else {
623
+ setSearchFocused(true)
624
+ }
625
+ }}
626
+ />
627
+ </search>
628
+ </PopoverTrigger>
629
+ {search && showSearchAll && <SearchAll query={search} />}
630
+ </Popover>
631
+ </div>
632
+ )}
633
+ <ModeToggle />
634
+ {enableMfa && (mfaActive || mfaEnabled) && !mfaRevoked && (
635
+ <DropdownMenu>
636
+ <DropdownMenuTrigger asChild>
637
+ <Button variant="outline" size="icon">
638
+ <User />
639
+ </Button>
640
+ </DropdownMenuTrigger>
641
+ <DropdownMenuContent className="w-56 dark print:hidden">
642
+ <DropdownMenuGroup>
643
+ <DropdownMenuItem
644
+ onClick={() => {
645
+ revokeMfa()
646
+ }}
696
647
  >
697
- {/* eslint-disable security/detect-object-injection */}
698
- {iconNames[group.collections[0]]
699
- ? createElement(iconNames[group.collections[0]], {
700
- className: "h-5 w-5",
701
- })
702
- : null}
703
- {collectionTitles[group.collections[0]]}
704
- {/* eslint-enable security/detect-object-injection */}
705
- </button>
706
- )
707
- } else {
708
- return group.collections.map((collection) => {
648
+ Revoke MFA
649
+ </DropdownMenuItem>
650
+ </DropdownMenuGroup>
651
+ </DropdownMenuContent>
652
+ </DropdownMenu>
653
+ )}
654
+ <button className="text-muted-foreground hover:text-foreground dark" onClick={signOutUser}>
655
+ Sign Out
656
+ </button>
657
+ </div>
658
+
659
+ <Sheet open={sidebarOpen} onOpenChange={setSidebarOpen}>
660
+ <SheetTrigger asChild>
661
+ <Button size="icon" variant="outline" className="absolute left-4 lg:hidden dark">
662
+ <PanelLeft className="h-5 w-5 text-primary" />
663
+ <span className="sr-only">Toggle Menu</span>
664
+ </Button>
665
+ </SheetTrigger>
666
+ <SheetContent side="left" className="sm:max-w-xs overflow-y-auto">
667
+ <nav className="grid gap-6 text-lg font-medium pt-12">
668
+ {hasDashboard && (
669
+ <button
670
+ className={
671
+ window.location.pathname === "/"
672
+ ? "flex items-center gap-4 px-2.5 text-foreground"
673
+ : "flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
674
+ }
675
+ onClick={() => navigateFromSidebar("/")}
676
+ >
677
+ <ChartBar className="h-5 w-5" />
678
+ Dashboard
679
+ </button>
680
+ )}
681
+ {sidebarMenu.map((group) => {
682
+ if (group.collections.length === 1) {
709
683
  const className = "flex items-center gap-4 px-2.5 text-primary"
710
684
  return (
711
685
  <button
712
- key={collection}
686
+ key={group.collections[0]}
713
687
  className={
714
- isMatch(collection)
688
+ isMatch(group.collections[0])
715
689
  ? cn(className, "text-foreground")
716
690
  : cn(
717
691
  className,
718
692
  "text-muted-foreground hover:text-foreground",
719
693
  )
720
694
  }
721
- onClick={() => navigateFromSidebar(`/${collection.toLowerCase()}`)}
695
+ onClick={() =>
696
+ navigateFromSidebar(`/${group.collections[0].toLowerCase()}`)
697
+ }
722
698
  >
723
699
  {/* eslint-disable security/detect-object-injection */}
724
- {iconNames[collection]
725
- ? createElement(iconNames[collection], { className: "h-5 w-5" })
700
+ {iconNames[group.collections[0]]
701
+ ? createElement(iconNames[group.collections[0]], {
702
+ className: "h-5 w-5",
703
+ })
726
704
  : null}
727
- {collectionTitles[collection]}
705
+ {collectionTitles[group.collections[0]]}
728
706
  {/* eslint-enable security/detect-object-injection */}
729
707
  </button>
730
708
  )
731
- })
732
- }
733
- })}
734
- {enableMfa && ((!mfaActive && !mfaEnabled) || mfaRevoked) && (
709
+ } else {
710
+ return group.collections.map((collection) => {
711
+ const className = "flex items-center gap-4 px-2.5 text-primary"
712
+ return (
713
+ <button
714
+ key={collection}
715
+ className={
716
+ isMatch(collection)
717
+ ? cn(className, "text-foreground")
718
+ : cn(
719
+ className,
720
+ "text-muted-foreground hover:text-foreground",
721
+ )
722
+ }
723
+ onClick={() =>
724
+ navigateFromSidebar(`/${collection.toLowerCase()}`)
725
+ }
726
+ >
727
+ {/* eslint-disable security/detect-object-injection */}
728
+ {iconNames[collection]
729
+ ? createElement(iconNames[collection], {
730
+ className: "h-5 w-5",
731
+ })
732
+ : null}
733
+ {collectionTitles[collection]}
734
+ {/* eslint-enable security/detect-object-injection */}
735
+ </button>
736
+ )
737
+ })
738
+ }
739
+ })}
740
+ {enableMfa && ((!mfaActive && !mfaEnabled) || mfaRevoked) && (
741
+ <button
742
+ key="mfa-enroll-mobile"
743
+ className="flex items-center gap-4 px-2.5 bg-destructive p-4 rounded-md text-white"
744
+ onClick={() => multiFactorEnroll(user, getMultiFactorCode)}
745
+ >
746
+ Enable MFA
747
+ </button>
748
+ )}
749
+ </nav>
750
+ <div className="grid gap-6 text-lg mt-6 font-medium">
735
751
  <button
736
- key="mfa-enroll-mobile"
737
- className="flex items-center gap-4 px-2.5 bg-destructive p-4 rounded-md text-white"
738
- onClick={() => multiFactorEnroll(user, getMultiFactorCode)}
752
+ className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
753
+ onClick={signOutUser}
739
754
  >
740
- Enable MFA
755
+ Sign Out
741
756
  </button>
742
- )}
743
- </nav>
744
- <div className="grid gap-6 text-lg mt-6 font-medium">
745
- <button
746
- className="flex items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
747
- onClick={signOutUser}
748
- >
749
- Sign Out
750
- </button>
751
- <div>
752
- <ModeToggle />
757
+ <div>
758
+ <ModeToggle />
759
+ </div>
753
760
  </div>
754
- </div>
755
- </SheetContent>
756
- </Sheet>
757
- <div className="flex justify-center items-center w-full lg:hidden">
758
- <img src={logo || defaultLogo} alt="Logo" className="h-8" />
759
- </div>
760
- <div className="absolute right-4 flex justify-center items-center gap-4 ml-auto lg:hidden">
761
- {isLoading && connectionStatus === "online" && <LoadingSpinner size={7} dark />}
762
- {connectionStatus === "offline" && <Badge variant="destructive">Offline</Badge>}
763
- </div>
764
- </header>
765
- <main className="flex w-full flex-col">
766
- <Outlet />
767
- </main>
768
- <Toaster />
769
- </>
761
+ </SheetContent>
762
+ </Sheet>
763
+ <div className="flex justify-center items-center w-full lg:hidden">
764
+ <img src={logo || defaultLogo} alt="Logo" className="h-8" />
765
+ </div>
766
+ <div className="absolute right-4 flex justify-center items-center gap-4 ml-auto lg:hidden">
767
+ {isLoading && connectionStatus === "online" && <LoadingSpinner size={7} dark />}
768
+ {connectionStatus === "offline" && <Badge variant="destructive">Offline</Badge>}
769
+ </div>
770
+ </header>
771
+ <main className="flex w-full flex-col">
772
+ <Outlet />
773
+ </main>
774
+ <Toaster />
775
+ </div>
776
+ </div>
770
777
  )
771
778
  )
772
779
  }
package/src/index.css CHANGED
@@ -88,6 +88,6 @@
88
88
  @apply border-border;
89
89
  }
90
90
  body {
91
- @apply bg-background text-foreground;
91
+ @apply text-foreground;
92
92
  }
93
93
  }