Seth commited on
Commit
8134b4e
·
1 Parent(s): 8fde213
frontend/src/components/layout/AppShell.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
2
  import { Link, useLocation } from 'react-router-dom';
3
  import {
4
  Zap,
@@ -8,14 +8,12 @@ import {
8
  Handshake,
9
  ChevronLeft,
10
  ChevronRight,
11
- Settings,
12
  } from 'lucide-react';
13
  import { Button } from '@/components/ui/button';
14
  import { cn } from '@/lib/utils';
15
- import { apiFetch } from '@/lib/api';
16
  import GoogleAuthBar from '@/components/layout/GoogleAuthBar';
17
 
18
- const BASE_NAV_ITEMS = [
19
  { label: 'Generator', href: '/', icon: LayoutDashboard },
20
  { label: 'Contacts', href: '/contacts', icon: Users },
21
  { label: 'Leads', href: '/leads', icon: Inbox },
@@ -31,30 +29,6 @@ function pathMatches(locationPath, href) {
31
 
32
  export default function AppShell({ title, subtitle, rightContent, children }) {
33
  const location = useLocation();
34
- const [showSettingsNav, setShowSettingsNav] = useState(false);
35
-
36
- useEffect(() => {
37
- const syncSettingsNav = () => {
38
- apiFetch('/api/auth/me')
39
- .then((r) => (r.ok ? r.json() : null))
40
- .then((u) => setShowSettingsNav(u != null))
41
- .catch(() => setShowSettingsNav(false));
42
- };
43
- syncSettingsNav();
44
- window.addEventListener('emailout-auth-changed', syncSettingsNav);
45
- return () => window.removeEventListener('emailout-auth-changed', syncSettingsNav);
46
- }, []);
47
-
48
- const navItems = useMemo(
49
- () =>
50
- showSettingsNav
51
- ? [
52
- ...BASE_NAV_ITEMS,
53
- { label: 'Settings', href: '/settings', icon: Settings },
54
- ]
55
- : BASE_NAV_ITEMS,
56
- [showSettingsNav]
57
- );
58
 
59
  const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
60
  try {
@@ -116,7 +90,7 @@ export default function AppShell({ title, subtitle, rightContent, children }) {
116
  </div>
117
  </div>
118
  <nav className="flex items-center gap-2 border-t border-slate-100 bg-white/90 px-4 py-2 md:hidden flex-wrap">
119
- {navItems.map((item) => {
120
  const active = pathMatches(location.pathname, item.href);
121
  return (
122
  <Button
@@ -146,7 +120,7 @@ export default function AppShell({ title, subtitle, rightContent, children }) {
146
  sidebarCollapsed && 'items-center'
147
  )}
148
  >
149
- {navItems.map((item) => {
150
  const Icon = item.icon;
151
  const active = pathMatches(location.pathname, item.href);
152
  return (
 
1
+ import React, { useEffect, useState } from 'react';
2
  import { Link, useLocation } from 'react-router-dom';
3
  import {
4
  Zap,
 
8
  Handshake,
9
  ChevronLeft,
10
  ChevronRight,
 
11
  } from 'lucide-react';
12
  import { Button } from '@/components/ui/button';
13
  import { cn } from '@/lib/utils';
 
14
  import GoogleAuthBar from '@/components/layout/GoogleAuthBar';
15
 
16
+ const NAV_ITEMS = [
17
  { label: 'Generator', href: '/', icon: LayoutDashboard },
18
  { label: 'Contacts', href: '/contacts', icon: Users },
19
  { label: 'Leads', href: '/leads', icon: Inbox },
 
29
 
30
  export default function AppShell({ title, subtitle, rightContent, children }) {
31
  const location = useLocation();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
34
  try {
 
90
  </div>
91
  </div>
92
  <nav className="flex items-center gap-2 border-t border-slate-100 bg-white/90 px-4 py-2 md:hidden flex-wrap">
93
+ {NAV_ITEMS.map((item) => {
94
  const active = pathMatches(location.pathname, item.href);
95
  return (
96
  <Button
 
120
  sidebarCollapsed && 'items-center'
121
  )}
122
  >
123
+ {NAV_ITEMS.map((item) => {
124
  const Icon = item.icon;
125
  const active = pathMatches(location.pathname, item.href);
126
  return (
frontend/src/components/layout/GoogleAuthBar.jsx CHANGED
@@ -1,6 +1,6 @@
1
  import React, { useCallback, useEffect, useState } from 'react';
2
- import { useSearchParams } from 'react-router-dom';
3
- import { LogOut } from 'lucide-react';
4
  import { Button } from '@/components/ui/button';
5
  import { cn } from '@/lib/utils';
6
  import { apiFetch } from '@/lib/api';
@@ -9,6 +9,7 @@ import { apiFetch } from '@/lib/api';
9
  * Sign-in (Google). Workspace details and switching context live in Settings / session. OAuth callback sets session.
10
  */
11
  export default function GoogleAuthBar() {
 
12
  const [searchParams, setSearchParams] = useSearchParams();
13
  const [phase, setPhase] = useState('loading');
14
  const [googleOn, setGoogleOn] = useState(false);
@@ -85,8 +86,26 @@ export default function GoogleAuthBar() {
85
  }
86
 
87
  if (user) {
 
 
88
  return (
89
  <div className="flex max-w-[min(100vw-6rem,28rem)] flex-wrap items-center justify-end gap-2">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  {user.picture ? (
91
  <img
92
  src={user.picture}
 
1
  import React, { useCallback, useEffect, useState } from 'react';
2
+ import { Link, useLocation, useSearchParams } from 'react-router-dom';
3
+ import { LogOut, Settings } from 'lucide-react';
4
  import { Button } from '@/components/ui/button';
5
  import { cn } from '@/lib/utils';
6
  import { apiFetch } from '@/lib/api';
 
9
  * Sign-in (Google). Workspace details and switching context live in Settings / session. OAuth callback sets session.
10
  */
11
  export default function GoogleAuthBar() {
12
+ const location = useLocation();
13
  const [searchParams, setSearchParams] = useSearchParams();
14
  const [phase, setPhase] = useState('loading');
15
  const [googleOn, setGoogleOn] = useState(false);
 
86
  }
87
 
88
  if (user) {
89
+ const settingsActive =
90
+ location.pathname === '/settings' || location.pathname.startsWith('/settings/');
91
  return (
92
  <div className="flex max-w-[min(100vw-6rem,28rem)] flex-wrap items-center justify-end gap-2">
93
+ <Button
94
+ asChild
95
+ variant="ghost"
96
+ size="icon"
97
+ className={cn(
98
+ 'h-9 w-9 shrink-0 text-slate-600 hover:text-violet-700 hover:bg-violet-50',
99
+ settingsActive && 'text-violet-700 bg-violet-50'
100
+ )}
101
+ title="Settings"
102
+ aria-current={settingsActive ? 'page' : undefined}
103
+ >
104
+ <Link to="/settings">
105
+ <Settings className="h-5 w-5" aria-hidden />
106
+ <span className="sr-only">Settings</span>
107
+ </Link>
108
+ </Button>
109
  {user.picture ? (
110
  <img
111
  src={user.picture}