Skip to content

Commit 1aa0f0e

Browse files
authored
Merge pull request #392 from PolicyEngine/feat/report-and-v1-banner
Add report button and v1 banner
2 parents 6a17fe3 + f5efa78 commit 1aa0f0e

File tree

20 files changed

+737
-355
lines changed

20 files changed

+737
-355
lines changed

app/src/components/Layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { clearFlow } from '@/reducers/flowReducer';
88
import { RootState } from '@/store';
99
import { cacheMonitor } from '@/utils/cacheMonitor';
1010
import HeaderNavigation from './shared/HomeHeader';
11+
import LegacyBanner from './shared/LegacyBanner';
1112
import Sidebar from './Sidebar';
1213

1314
export default function Layout() {
@@ -67,6 +68,7 @@ export default function Layout() {
6768
>
6869
<AppShell.Header p={0}>
6970
<HeaderNavigation />
71+
<LegacyBanner />
7072
</AppShell.Header>
7173

7274
<AppShell.Navbar>

app/src/components/Sidebar.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@ export default function Sidebar({ isOpen = true }: SidebarProps) {
5252
external: true,
5353
},
5454
{
55-
label: 'Visit blog',
55+
label: 'View research',
5656
icon: IconBook,
57-
path: 'https://blog.example.com',
57+
path: `/${countryId}/research`,
5858
external: true,
59-
disabled: true,
6059
},
6160
];
6261

app/src/components/StaticLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Outlet } from 'react-router-dom';
22
import HeaderNavigation from '@/components/shared/HomeHeader';
3+
import LegacyBanner from '@/components/shared/LegacyBanner';
34

45
export default function StaticLayout() {
56
return (
67
<>
78
<HeaderNavigation />
9+
<LegacyBanner />
810
<Outlet />
911
</>
1012
);

app/src/components/home-header/DesktopNavigation.tsx

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,16 @@
1-
import { IconChevronDown } from '@tabler/icons-react';
2-
import { Anchor, Group, Menu, Text, UnstyledButton } from '@mantine/core';
3-
import { colors, spacing, typography } from '@/designTokens';
4-
import { useCurrentCountry } from '@/hooks/useCurrentCountry';
5-
6-
interface NavLink {
7-
label: string;
8-
path?: string;
9-
}
1+
import { Group } from '@mantine/core';
2+
import { spacing } from '@/designTokens';
3+
import NavItem, { NavItemSetup } from './NavItem';
104

115
interface DesktopNavigationProps {
12-
navLinks: NavLink[];
13-
aboutLinks: NavLink[];
14-
onNavClick: (path?: string) => void;
6+
navItems: NavItemSetup[];
157
}
168

17-
export default function DesktopNavigation({
18-
navLinks,
19-
aboutLinks,
20-
onNavClick,
21-
}: DesktopNavigationProps) {
22-
const countryId = useCurrentCountry();
23-
9+
export default function DesktopNavigation({ navItems }: DesktopNavigationProps) {
2410
return (
2511
<Group gap={spacing['3xl']} visibleFrom="lg" align="center">
26-
<Anchor
27-
c={colors.text.inverse}
28-
variant="subtle"
29-
td="none"
30-
fw={typography.fontWeight.medium}
31-
size="18px"
32-
style={{ fontFamily: typography.fontFamily.primary }}
33-
onClick={() => onNavClick(`https://policyengine.org/${countryId}`)}
34-
>
35-
Home
36-
</Anchor>
37-
38-
<Menu shadow="md" width={200} zIndex={1001} position="bottom" offset={10}>
39-
<Menu.Target>
40-
<UnstyledButton>
41-
<Group gap={4} align="center">
42-
<Text
43-
c={colors.text.inverse}
44-
fw={typography.fontWeight.medium}
45-
size="18px"
46-
style={{ fontFamily: typography.fontFamily.primary }}
47-
>
48-
About
49-
</Text>
50-
<IconChevronDown size={18} color={colors.text.inverse} />
51-
</Group>
52-
</UnstyledButton>
53-
</Menu.Target>
54-
<Menu.Dropdown>
55-
{aboutLinks.map((link) => (
56-
<Menu.Item key={link.label} onClick={() => onNavClick(link.path)}>
57-
{link.label}
58-
</Menu.Item>
59-
))}
60-
</Menu.Dropdown>
61-
</Menu>
62-
63-
{navLinks.map((link) => (
64-
<Anchor
65-
key={link.label}
66-
c={colors.text.inverse}
67-
variant="subtle"
68-
td="none"
69-
fw={typography.fontWeight.medium}
70-
size="18px"
71-
style={{ fontFamily: typography.fontFamily.primary }}
72-
onClick={() => onNavClick(link.path)}
73-
>
74-
{link.label}
75-
</Anchor>
12+
{navItems.map((item) => (
13+
<NavItem key={item.label} setup={item} />
7614
))}
7715
</Group>
7816
);

app/src/components/home-header/HeaderContent.tsx

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,16 @@ import DesktopNavigation from './DesktopNavigation';
33
import HeaderActionButtons from './HeaderActionButtons';
44
import HeaderLogo from './HeaderLogo';
55
import MobileMenu from './MobileMenu';
6-
7-
interface NavLink {
8-
label: string;
9-
path?: string;
10-
}
6+
import { NavItemSetup } from './NavItem';
117

128
interface HeaderContentProps {
139
opened: boolean;
1410
onOpen: () => void;
1511
onClose: () => void;
16-
navLinks: NavLink[];
17-
aboutLinks: NavLink[];
18-
onNavClick: (path?: string) => void;
12+
navItems: NavItemSetup[];
1913
}
2014

21-
export default function HeaderContent({
22-
opened,
23-
onOpen,
24-
onClose,
25-
navLinks,
26-
aboutLinks,
27-
onNavClick,
28-
}: HeaderContentProps) {
15+
export default function HeaderContent({ opened, onOpen, onClose, navItems }: HeaderContentProps) {
2916
return (
3017
<Container
3118
h="100%"
@@ -41,21 +28,14 @@ export default function HeaderContent({
4128
<Group justify="space-between" h="100%">
4229
<Group>
4330
<HeaderLogo />
44-
<DesktopNavigation navLinks={navLinks} aboutLinks={aboutLinks} onNavClick={onNavClick} />
31+
<DesktopNavigation navItems={navItems} />
4532
</Group>
4633

4734
<Group visibleFrom="lg">
4835
<HeaderActionButtons />
4936
</Group>
5037

51-
<MobileMenu
52-
opened={opened}
53-
onOpen={onOpen}
54-
onClose={onClose}
55-
navLinks={navLinks}
56-
aboutLinks={aboutLinks}
57-
onNavClick={onNavClick}
58-
/>
38+
<MobileMenu opened={opened} onOpen={onOpen} onClose={onClose} navItems={navItems} />
5939
</Group>
6040
</Container>
6141
);

app/src/components/home-header/MobileMenu.tsx

Lines changed: 37 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
1-
import { Anchor, Box, Burger, Divider, Drawer, Group, Stack, Text } from '@mantine/core';
1+
import { Anchor, Box, Burger, Drawer, Group, Stack, Text } from '@mantine/core';
22
import { colors, spacing, typography } from '@/designTokens';
33
import CountrySelector from './CountrySelector';
4-
5-
interface NavLink {
6-
label: string;
7-
path?: string;
8-
}
4+
import { NavItemSetup } from './NavItem';
95

106
interface MobileMenuProps {
117
opened: boolean;
128
onOpen: () => void;
139
onClose: () => void;
14-
navLinks: NavLink[];
15-
aboutLinks: NavLink[];
16-
onNavClick: (path?: string) => void;
10+
navItems: NavItemSetup[];
1711
}
1812

19-
export default function MobileMenu({
20-
opened,
21-
onOpen,
22-
onClose,
23-
navLinks,
24-
aboutLinks,
25-
onNavClick,
26-
}: MobileMenuProps) {
13+
export default function MobileMenu({ opened, onOpen, onClose, navItems }: MobileMenuProps) {
2714
return (
2815
<>
2916
{/* Mobile Burger Menu with Country Selector */}
@@ -45,56 +32,53 @@ export default function MobileMenu({
4532
closeButtonProps={{ style: { color: colors.text.inverse }, size: 'md' }}
4633
>
4734
<Stack gap={spacing.lg} p={spacing.lg}>
48-
{/* About Section */}
49-
<Box>
50-
<Text
51-
c={colors.text.inverse}
52-
fw={typography.fontWeight.medium}
53-
size="sm"
54-
mb={spacing.xs}
55-
style={{ fontFamily: typography.fontFamily.primary }}
56-
>
57-
About
58-
</Text>
59-
<Stack gap={spacing.xs} pl={spacing.md}>
60-
{aboutLinks.map((link) => (
61-
<Anchor
62-
key={link.label}
35+
{navItems.map((item) =>
36+
item.hasDropdown && item.dropdownItems ? (
37+
// Render dropdown as a section
38+
<Box key={item.label}>
39+
<Text
6340
c={colors.text.inverse}
64-
variant="subtle"
65-
td="none"
66-
fw={typography.fontWeight.normal}
41+
fw={typography.fontWeight.medium}
6742
size="sm"
68-
onClick={() => onNavClick(link.path)}
43+
mb={spacing.xs}
6944
style={{ fontFamily: typography.fontFamily.primary }}
7045
>
71-
{link.label}
72-
</Anchor>
73-
))}
74-
</Stack>
75-
</Box>
76-
77-
<Divider color={colors.border.dark} />
78-
79-
{/* Navigation Links Section */}
80-
<Box>
81-
{navLinks.map((link) => (
46+
{item.label}
47+
</Text>
48+
<Stack gap={spacing.xs} pl={spacing.md}>
49+
{item.dropdownItems.map((dropdownItem) => (
50+
<Anchor
51+
key={dropdownItem.label}
52+
c={colors.text.inverse}
53+
variant="subtle"
54+
td="none"
55+
fw={typography.fontWeight.normal}
56+
size="sm"
57+
onClick={dropdownItem.onClick}
58+
style={{ fontFamily: typography.fontFamily.primary }}
59+
>
60+
{dropdownItem.label}
61+
</Anchor>
62+
))}
63+
</Stack>
64+
</Box>
65+
) : (
66+
// Render regular link
8267
<Anchor
83-
key={link.label}
68+
key={item.label}
8469
c={colors.text.inverse}
8570
variant="subtle"
8671
td="none"
8772
fw={typography.fontWeight.medium}
8873
size="sm"
89-
onClick={() => onNavClick(link.path)}
74+
onClick={item.onClick}
9075
style={{ fontFamily: typography.fontFamily.primary }}
9176
display="block"
92-
mb={spacing.xs}
9377
>
94-
{link.label}
78+
{item.label}
9579
</Anchor>
96-
))}
97-
</Box>
80+
)
81+
)}
9882
</Stack>
9983
</Drawer>
10084
</>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { IconChevronDown } from '@tabler/icons-react';
2+
import { Anchor, Group, Menu, Text, UnstyledButton } from '@mantine/core';
3+
import { colors, typography } from '@/designTokens';
4+
5+
export interface DropdownItem {
6+
label: string;
7+
onClick: () => void;
8+
}
9+
10+
export interface NavItemSetup {
11+
label: string;
12+
onClick: () => void;
13+
hasDropdown: boolean;
14+
dropdownItems?: DropdownItem[];
15+
}
16+
17+
interface NavItemProps {
18+
setup: NavItemSetup;
19+
}
20+
21+
/**
22+
* Reusable navigation item component
23+
* Can be either a simple link or a dropdown menu
24+
*/
25+
export default function NavItem({ setup }: NavItemProps) {
26+
const { label, onClick, hasDropdown, dropdownItems } = setup;
27+
28+
if (hasDropdown && dropdownItems) {
29+
return (
30+
<Menu shadow="md" width={200} zIndex={1001} position="bottom" offset={10}>
31+
<Menu.Target>
32+
<UnstyledButton onClick={onClick}>
33+
<Group gap={4} align="center">
34+
<Text
35+
c={colors.text.inverse}
36+
fw={typography.fontWeight.medium}
37+
size="18px"
38+
style={{ fontFamily: typography.fontFamily.primary }}
39+
>
40+
{label}
41+
</Text>
42+
<IconChevronDown size={18} color={colors.text.inverse} />
43+
</Group>
44+
</UnstyledButton>
45+
</Menu.Target>
46+
<Menu.Dropdown>
47+
{dropdownItems.map((item) => (
48+
<Menu.Item key={item.label} onClick={item.onClick}>
49+
{item.label}
50+
</Menu.Item>
51+
))}
52+
</Menu.Dropdown>
53+
</Menu>
54+
);
55+
}
56+
57+
return (
58+
<Anchor
59+
c={colors.text.inverse}
60+
variant="subtle"
61+
td="none"
62+
fw={typography.fontWeight.medium}
63+
size="18px"
64+
style={{ fontFamily: typography.fontFamily.primary }}
65+
onClick={onClick}
66+
>
67+
{label}
68+
</Anchor>
69+
);
70+
}

0 commit comments

Comments
 (0)