diff --git a/public/assets/city_council.png b/public/assets/city_council.png new file mode 100644 index 000000000..059de81ce Binary files /dev/null and b/public/assets/city_council.png differ diff --git a/public/assets/coop.png b/public/assets/coop.png new file mode 100644 index 000000000..f83a0666b Binary files /dev/null and b/public/assets/coop.png differ diff --git a/public/assets/org.png b/public/assets/org.png new file mode 100644 index 000000000..cd7e5c063 Binary files /dev/null and b/public/assets/org.png differ diff --git a/public/assets/political_party.png b/public/assets/political_party.png new file mode 100644 index 000000000..fb0fc409b Binary files /dev/null and b/public/assets/political_party.png differ diff --git a/public/assets/sport_club.png b/public/assets/sport_club.png new file mode 100644 index 000000000..576c54f41 Binary files /dev/null and b/public/assets/sport_club.png differ diff --git a/public/assets/web3.png b/public/assets/web3.png new file mode 100644 index 000000000..adf705d96 Binary files /dev/null and b/public/assets/web3.png differ diff --git a/src/components/Dashboard/Menu/Item.tsx b/src/components/Dashboard/Menu/Item.tsx index bde1e277c..d4311857c 100644 --- a/src/components/Dashboard/Menu/Item.tsx +++ b/src/components/Dashboard/Menu/Item.tsx @@ -9,6 +9,8 @@ export const DashboardMenuItem = ({ isOpen = false, isActive = false, onToggle, + onMouseEnter, + onMouseLeave, hasChildren = false, }: { label: string @@ -17,6 +19,8 @@ export const DashboardMenuItem = ({ isOpen?: boolean isActive?: boolean onToggle?: () => void + onMouseEnter?: () => void + onMouseLeave?: () => void hasChildren?: boolean }) => ( diff --git a/src/components/Layout/UseCases.tsx b/src/components/Layout/UseCases.tsx new file mode 100644 index 000000000..074baf6bb --- /dev/null +++ b/src/components/Layout/UseCases.tsx @@ -0,0 +1,86 @@ +import { Box } from '@chakra-ui/react' +import { useTranslation } from 'react-i18next' +import { Routes } from '~src/router/routes' +import cityCouncilImg from '/assets/city_council.png' +import coopsImg from '/assets/coop.png' +import organizationsImg from '/assets/org.png' +import politicalPartyImg from '/assets/political_party.png' +import sportClubsImg from '/assets/sport_club.png' +import web3Img from '/assets/web3.png' + +type UseCasesItem = { + category: string + label: string + description: string + url: string + route: string +} + +const UseCases = () => { + return Use Cases +} + +export const useCases = (): UseCasesItem[] => { + const { t } = useTranslation() + + return [ + { + category: t('use_cases.insitutions_tab', { defaultValue: 'Institutions and Public Sector' }), + label: t('use_cases.insitutions_label', { defaultValue: 'City Councils' }), + description: t('use_cases.insitutions_description', { + defaultValue: 'How do you create compelling presentations that wow your colleagues and impress your managers?', + }), + url: cityCouncilImg, + route: Routes.useCases.cityCouncils, + }, + { + category: t('use_cases.cooperatives_tab', { defaultValue: 'Professional Colleges' }), + label: t('use_cases.cooperatives_label', { defaultValue: 'Organizations' }), + description: t('use_cases.cooperatives_description', { + defaultValue: + "Linear helps streamline software projects, sprints, tasks, and bug tracking. Here's how to get started.", + }), + url: organizationsImg, + route: Routes.useCases.organizations, + }, + { + category: t('use_cases.organizations_tab', { defaultValue: 'Software Engineering' }), + label: t('use_cases.organizations_label', { defaultValue: 'Political Parties' }), + description: t('use_cases.organizations_description', { + defaultValue: + 'The rise of RESTful APIs has been met by a rise in tools for creating, testing, and managing them.', + }), + url: politicalPartyImg, + route: Routes.useCases.politicalParties, + }, + { + category: t('use_cases.sports_and_recreations_tab', { defaultValue: 'Product' }), + label: t('use_cases.sports_and_recreations_label', { defaultValue: 'Co-ops' }), + description: t('use_cases.sports_and_recreations_description', { + defaultValue: 'Mental models are simple expressions of complex processes or relationships.', + }), + url: coopsImg, + route: Routes.useCases.coOps, + }, + { + category: t('use_cases.community_groups_tab', { defaultValue: 'Design' }), + label: t('use_cases.community_groups_label', { defaultValue: 'Web3' }), + description: t('use_cases.community_groups_description', { + defaultValue: 'Introduction to Wireframing and its Principles. Learn from the best in the industry.', + }), + url: web3Img, + route: Routes.useCases.web3, + }, + { + category: t('use_cases.community_groups_tab', { defaultValue: 'Software Engineering' }), + label: t('use_cases.community_groups_label', { defaultValue: 'Sport Clubs' }), + description: t('use_cases.community_groups_description', { + defaultValue: 'Javascript frameworks make development easy with extensive features and functionalities.', + }), + url: sportClubsImg, + route: Routes.useCases.sportClubs, + }, + ] +} + +export default UseCases diff --git a/src/components/Navbar/Menu.tsx b/src/components/Navbar/Menu.tsx deleted file mode 100644 index 79f504672..000000000 --- a/src/components/Navbar/Menu.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { ChevronDownIcon, ChevronUpIcon, CopyIcon } from '@chakra-ui/icons' -import { - Box, - Button, - Flex, - HStack, - Icon, - IconButton, - Link, - MenuItem, - MenuList, - Text, - useClipboard, -} from '@chakra-ui/react' -import { Balance, HR } from '@vocdoni/chakra-components' -import { useClient } from '@vocdoni/react-providers' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { FaWallet } from 'react-icons/fa' -import { HiShoppingCart } from 'react-icons/hi' -import { MdOutlineLogout } from 'react-icons/md' -import { Link as ReactRouterLink } from 'react-router-dom' -import { useDisconnect } from 'wagmi' -import { addressTextOverflow } from '~constants' -import { Routes } from '~src/router/routes' -import { LanguagesList } from './LanguagesList' - -const MenuDropdown = () => { - const { t } = useTranslation() - const { disconnect } = useDisconnect() - const { account, clear } = useClient() - const { onCopy } = useClipboard(account?.address as string) - - const [isOpenMenuLanguages, setIsOpenMenuLanguages] = useState(false) - - return ( - - {account && ( - <> - { - onCopy() - }} - > - {t('menu.wallet')} - - - - - {addressTextOverflow((account?.address as string) || '', 10)} - } - aria-label={t('menu.copy_aria_label')} - onClick={() => { - onCopy() - }} - /> - - - - - - - - - - - - - {t('menu.organization')} - - - )} - setIsOpenMenuLanguages((prev) => !prev)} - display='flex' - flexDirection='column' - px={0} - pb={0} - > - - {t('menu.languages')} - {isOpenMenuLanguages ? : } - - - {isOpenMenuLanguages && } - - {t('menu.documentation')} - -
- { - disconnect() - clear() - }} - fontWeight='bold' - > - - {t('menu.logout')} - - - {t('menu.terms')} - - - {t('menu.privacy')} - -
- ) -} - -export default MenuDropdown diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 6ecb38a22..246427b42 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,58 +1,166 @@ -import { Button, ButtonProps, Flex, List, ListItem } from '@chakra-ui/react' +import { HamburgerIcon } from '@chakra-ui/icons' +import { + Box, + Button, + ButtonProps, + Collapse, + Drawer, + DrawerContent, + DrawerOverlay, + Flex, + IconButton, + List, + ListItem, + useBreakpointValue, + useDisclosure, +} from '@chakra-ui/react' +import { useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { generatePath, Link as ReactRouterLink } from 'react-router-dom' import { useAuth } from '~components/Auth/useAuth' +import { DashboardMenuItem } from '~components/Dashboard/Menu/Item' import { ColorModeSwitcher } from '~components/Layout/ColorModeSwitcher' import Logo from '~components/Layout/Logo' +import { useCases } from '~components/Layout/UseCases' import { Routes } from '~src/router/routes' -import { LanguagesMenu } from './LanguagesList' - -const Navbar = () => ( - - + +type MenuItem = { + label: string + route?: string + children?: MenuItem[] +} + +const Navbar = () => { + const { isOpen, onOpen, onClose } = useDisclosure() + + return ( + - - - - - - - - - - - + + + + + } onClick={onOpen} display={{ xl: 'none' }} aria-label='Open menu' /> - -) + ) +} + +const NavbarMenu = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) => { + return ( + <> + + + + + + + + + + + + + + + ) +} + +const NavbarMenuContent = () => { + const { t } = useTranslation() + const ref = useRef() + const useCasesItems = useCases() + const [openSection, setOpenSection] = useState(null) + const isLargerThanXL = useBreakpointValue({ base: false, xl: true }) + + const handleToggle = (label: string) => { + setOpenSection((prev) => (prev === label ? null : label)) + } + + const menuItems: MenuItem[] = [ + { + label: t('navbar.use_cases', { defaultValue: 'Use Cases' }), + route: Routes.useCases.root, + children: useCasesItems, + }, + { + label: t('navbar.features', { defaultValue: 'Features' }), + route: Routes.features, + }, + { + label: t('navbar.pricing', { defaultValue: 'Pricing' }), + route: Routes.pricing, + }, + { + label: t('navbar.technolgy', { defaultValue: 'Technology' }), + route: Routes.technology, + }, + { + label: t('navbar.demos', { defaultValue: 'Demos' }), + route: Routes.demos, + }, + { + label: t('navbar.contact_us', { defaultValue: 'Contact Us' }), + route: Routes.contactUs, + }, + ] + + return ( + + {menuItems.map((item, index) => ( + + {item.children ? ( + <> + handleToggle(item.label)} + onMouseLeave={() => setOpenSection(null)} + hasChildren + /> + + handleToggle(item.label)} + onMouseLeave={() => setOpenSection(null)} + zIndex={20} + position={{ base: 'relative', xl: 'absolute' }} + gap={6} + px={6} + bgColor={'navbar.bg_light'} + _dark={{ bgColor: 'navbar.bg_dark' }} + > + {item.children.map((child, idx) => ( + + ))} + + + + ) : ( + + )} + + ))} + + ) +} -const DashboardButton = (props?: ButtonProps) => { +const NavbarMenuButtons = (props?: ButtonProps) => { const { t } = useTranslation() const { isAuthenticated } = useAuth() return ( - + <> + + + ) } diff --git a/src/elements/Layout.tsx b/src/elements/Layout.tsx index b0dcfa73a..3fcebfdeb 100644 --- a/src/elements/Layout.tsx +++ b/src/elements/Layout.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, HStack } from '@chakra-ui/react' +import { Box, Flex } from '@chakra-ui/react' import { Outlet, ScrollRestoration, useLocation } from 'react-router-dom' import Footer from '~components/Layout/Footer' import Navbar from '~components/Navbar' @@ -17,9 +17,16 @@ const Layout = () => { bgColor: 'process_view.bg_dark', }} > - + - + diff --git a/src/router/Router.tsx b/src/router/Router.tsx index 984be6c20..9559ca26f 100644 --- a/src/router/Router.tsx +++ b/src/router/Router.tsx @@ -3,14 +3,16 @@ import { useAuthRoutes } from './routes/auth' import { useDashboardRoutes } from './routes/dashboard' import { useProcessCreateRoutes } from './routes/process-create' import { useRootRoutes } from './routes/root' +import { useUseCasesRoutes } from './routes/use-cases' export const RoutesProvider = () => { const root = useRootRoutes() const auth = useAuthRoutes() const processCreate = useProcessCreateRoutes() const dashboard = useDashboardRoutes() + const useCases = useUseCasesRoutes() - const router = createBrowserRouter([root, auth, processCreate, dashboard]) + const router = createBrowserRouter([root, auth, processCreate, dashboard, useCases]) return } diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts index 8e5a8d77b..31bcb401f 100644 --- a/src/router/routes/index.ts +++ b/src/router/routes/index.ts @@ -8,6 +8,7 @@ export const Routes = { passwordReset: '/account/password/reset', }, calculator: '/calculator', + contactUs: '/contact-us', dashboard: { base: '/admin', organization: '/admin/organization', @@ -16,8 +17,11 @@ export const Routes = { profile: '/admin/profile', team: '/admin/team', }, + demos: '/demos', faucet: '/faucet', + features: '/features', organization: '/organization/:address', + pricing: '/pricing', privacy: '/privacy', processes: { create: '/admin/processes/create', @@ -27,6 +31,16 @@ export const Routes = { checkout: '/stripe/checkout/:amount?', return: '/stripe/return/:sessionId', }, + useCases: { + root: '/use-cases', + cityCouncils: '/city-councils', + organizations: '/organizations', + politicalParties: '/political-parties', + coOps: '/coops', + web3: '/web3', + sportClubs: '/sport-clubs', + }, + technology: '/technology', terms: '/terms', } diff --git a/src/router/routes/use-cases.tsx b/src/router/routes/use-cases.tsx new file mode 100644 index 000000000..faf17d106 --- /dev/null +++ b/src/router/routes/use-cases.tsx @@ -0,0 +1,29 @@ +import { lazy } from 'react' +// These aren't lazy loaded since they are main layouts and related components +import Layout from '~elements/Layout' +import { Routes } from '.' +import { SuspenseLoader } from '../SuspenseLoader' + +const UseCases = lazy(() => import('~components/Layout/UseCases')) + +const UseCasesElements = [ + { + children: [ + { + path: Routes.useCases.root, + element: ( + + + + ), + }, + ], + }, +] + +export const useUseCasesRoutes = () => { + return { + element: , + children: UseCasesElements, + } +} diff --git a/src/theme/colors.ts b/src/theme/colors.ts index 8f4fe4131..bcacdf786 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -298,6 +298,11 @@ export const colors = { border: colorsBase.primary, }, + navbar: { + bg_light: colorsBase.white.pure, + bg_dark: colorsBase.blue.dark, + }, + text_area: { bg_light: colorsBase.white.pure, bg_dark: colorsBase.blue.grayish,