Files
campaign-site/components/Header.tsx
Artur 82955be4cb Campaign Site V2 (#81)
* Apply OpenSats UI enhancements

* Add register modal and some UI fixes

* Add login modal

* Add reset password button

* Use @t3-oss/env-nextjs for env variables

* Email verification without keycloak UI

* Password reset without keycloak UI

* Add "My donations" page with one-time stripe donations list

* Display crypto donations in "My donations" page

* Donation form fixes and improvements

* Display recurring annual fiat donations

* Include keycloak realm export file and remove hardcoded client values

* fix: correctly handle btcpay webhooks and fix donationList query

* feat: add membership modal and implement membership payment using btcpay

* feat: add procedure for membership purchases with stripe and use db as single source of truth for donations

* feat: use webhooks to update stripe donation/membership status

* feat: memberships list page and fixes

* feat: db schema changes and webhook fixes

* feat: re-add "donate" and add "get annual membership" buttons to project page

* feat: open register modal when clicking membership button while logged out

* feat: replace "Get Membership" button with "My Memberships" button when user already has a membership for that project

* feat: multiple funds support

* deps: bump axios

* feat: add different color schemes for each fund and some fixes

* feat: add home page

* feat: add missing titles and responsiveness improvements

* chore: add prod workflow file and compose file

* chore(deploy workflow): set environment name

* chore(Dockerfile): add necessary lines for prisma

* chore: make it skip env validation on build

* fix: prevent donation amounts from being fetched from db during build

* chore(nginx.conf): remove copy-paste junk

* chore(docker compose): correctly set APP_URL env

* feat: replace Sendgrid with SES

* deps: audit fix

* chore(deploy.yml): remove unecessary env

* fix: correctly manage client and server env

* fix(trpc.submitApplication): get recipient emails from server side env

* fix(Dockerfile): define NEXT_PUBLIC_ env on build

* chore(trpc): make it log any errors

* chore(trpc): improve displaying of errors

* chore(trpc): improve displaying of errors

* fix: buggy link buttons

* feat: use single btcpay store

* feat: show form 8283 info in donation form and handle tax deductible donations

* feat: have only one privacy and terms page for the entire site

* feat: support many social links

* feat: add funding required endpoint (wip)

* feat: get rates using btcpay api and small refactor

* deps: audit fix

* fix: correctly handle payment methods on InvoiceSettled event

* fix: make index on Donation.btcPayInvoiceId

* feat(funding-required): improve asset parameter response

* feat(funding-required): add project_status param

* feat(funding-required): add fund param

* feat(funding-required): implement caching

* fix(funding-required): minor fixes

* feat(funding-required): add remaining_amount_<currency> fields and fixes

* chore: include all services in docker-compose.dev.yml and update .env.example

* feat: move terms and privacy links to footer

* fix: address font not always loading bug

* feat: use fund logos as header image

* feat: donation confirmation email

* fix: use correct stripe client for each fund on webhooks

* feat: add account settings page with change password form

* feat: add email change form to settings page

* fix: address wrong btcpay invoice url redirect

* chore: email change request debug

* fix(api): better handle user attributes

* feat: ui improvements

* feat: add btcpay invoice item description

* chore(nginx): api rate limit

* feat: remove typing component from fund landing pages

* feat: implement refresh token rotation using keycloak

* refactor: have gross and net amounts for donations

* feat: invalidate user sessions on password/email change

* fix: make "Create an account" button work on donate/membership modals

* refactor: project props

* fix(utils.md): correctly load md project attributes

* chore(prisma): make composite unique constraint for fundSlug and projectSlug on ProjectAddresses

* chore: mark example project as not funded

* fix(utils.md): serialization error

* chore(funding-required): btcpay invoice payment methods debug

* fix(funding-required): get bitcoin address from correct payment method

* fix(funding-required): correctly handle project_status ANY filter

* fix(btcpay webhook handler): correctly handle payment methods on InvoicePaymentSettled

* chore(docker-compose.yml): expose nginx port 80

* fix(funding-required): correctly concat project url

* feat: ui improvements for smaller screens

* fix(btcpay webhook handler): correctly get payment method amount on InvoiceSettled

* fix(btcpay webhook handler): respond with 200 immediately if there is no metadata

* chore(funding-required): debugging

* chore(funding-required): debugging

* chore(funding-required): debugging

* fix(Dockerfile): define BUILD_MODE as arg instead of env to make it blank at runtime

* fix: correctly pass current and goal values to project card progress

* fix(funding-required): set high monitoring time for static address invoice

* fix: correctly handle refresh token expiration on the ui

* feat: ui improvements

* chore: update README

* chore: update README

* Initial site text

* fix: colors

* chore: mention funds accordingly in texts

* chore: update realm-export.json

* chore: rename docker compose files

* Update emails

* Form updates

* Remove unused pages and page improvements

* feat: allow editing navbar links for each fund

* Cleanup and Firo projects

* chore(deploy.yml): change deploy branch to master

* fix(auth): use fetch instead of axios when fetching refresh token due to edge runtime compatibility

* fix: keep empty project folders

* Fix code scanning alert no. 20: DOM text reinterpreted as HTML

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* chore: sanitize md file paths

* Text and link updates

---------

Co-authored-by: Artur N <arturnunespe@gmail.com>
Co-authored-by: Justin Ehrenhofer <12520755+SamsungGalaxyPlayer@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2024-10-17 10:29:40 -05:00

166 lines
6.0 KiB
TypeScript

import { useEffect, useState } from 'react'
import { signOut, useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
import Link from './CustomLink'
import MobileNav from './MobileNav'
import { fundHeaderNavLinks } from '../data/headerNavLinks'
import MagicLogo from './MagicLogo'
import { Dialog, DialogContent, DialogTrigger } from './ui/dialog'
import { Button } from './ui/button'
import RegisterFormModal from './RegisterFormModal'
import LoginFormModal from './LoginFormModal'
import PasswordResetFormModal from './PasswordResetFormModal'
import { Avatar, AvatarFallback } from './ui/avatar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from './ui/dropdown-menu'
import { useFundSlug } from '../utils/use-fund-slug'
import CustomLink from './CustomLink'
import { funds } from '../utils/funds'
import MoneroLogo from './MoneroLogo'
import FiroLogo from './FiroLogo'
import PrivacyGuidesLogo from './PrivacyGuidesLogo'
const Header = () => {
const [registerIsOpen, setRegisterIsOpen] = useState(false)
const [loginIsOpen, setLoginIsOpen] = useState(false)
const [passwordResetIsOpen, setPasswordResetIsOpen] = useState(false)
const router = useRouter()
const session = useSession()
const fundSlug = useFundSlug()
useEffect(() => {
if (router.query.loginEmail) {
setLoginIsOpen(true)
}
}, [router.query.loginEmail])
const fund = fundSlug ? funds[fundSlug] : null
return (
<header className="flex items-center justify-between py-10">
<div>
<Link
href={fundSlug ? `/${fundSlug}` : '/'}
aria-label="Home"
className="flex items-center mr-3 gap-4"
>
{!fundSlug && <MagicLogo className="w-12 h-12" />}
{fundSlug === 'monero' && <MoneroLogo className="w-12 h-12" />}
{fundSlug === 'firo' && <FiroLogo className="w-12 h-12" />}
{fundSlug === 'privacyguides' && <PrivacyGuidesLogo className="w-12 h-12" />}
{fundSlug === 'general' && <MagicLogo className="w-12 h-12" />}
<span className="text-foreground text-lg font-bold hidden sm:block">
{fund ? fund.title : 'MAGIC Grants'}
</span>
</Link>
</div>
<div className="flex gap-2 items-center text-base leading-5">
{!!fund &&
fundHeaderNavLinks[fund.slug].map((link) => (
<CustomLink
key={link.title}
href={`/${fundSlug}/${link.href}`}
className={
link.isButton
? 'rounded border border-primary bg-transparent px-4 py-2 font-semibold text-primary hover:border-transparent hover:bg-primary hover:text-white'
: 'hidden p-1 font-medium text-gray-900 sm:p-4 md:inline-block'
}
>
{link.title}
</CustomLink>
))}
{!!fund && session.status !== 'authenticated' && (
<>
<Dialog open={loginIsOpen} onOpenChange={setLoginIsOpen}>
<DialogTrigger asChild>
<Button variant="outline" className="w-18 block sm:hidden" size="sm">
Login
</Button>
</DialogTrigger>
<DialogTrigger asChild>
<Button variant="outline" className="w-24 hidden sm:block">
Login
</Button>
</DialogTrigger>
<DialogContent>
<LoginFormModal
close={() => setLoginIsOpen(false)}
openRegisterModal={() => setRegisterIsOpen(true)}
openPasswordResetModal={() => setPasswordResetIsOpen(true)}
/>
</DialogContent>
</Dialog>
<Dialog open={registerIsOpen} onOpenChange={setRegisterIsOpen}>
<DialogTrigger asChild>
<Button className="w-18 block sm:hidden" size="sm">
Register
</Button>
</DialogTrigger>
<DialogTrigger asChild>
<Button className="w-24 hidden sm:block">Register</Button>
</DialogTrigger>
<DialogContent>
<RegisterFormModal
close={() => setRegisterIsOpen(false)}
openLoginModal={() => setLoginIsOpen(true)}
/>
</DialogContent>
</Dialog>
</>
)}
{/* <ThemeSwitch /> */}
{!!fund && session.status === 'authenticated' && (
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar>
<AvatarFallback>
{session.data.user?.email?.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<CustomLink href={`/${fundSlug}/account/my-donations`} className="text-foreground">
<DropdownMenuItem>My Donations</DropdownMenuItem>
</CustomLink>
<CustomLink href={`/${fundSlug}/account/my-memberships`} className="text-foreground">
<DropdownMenuItem>My Memberships</DropdownMenuItem>
</CustomLink>
<CustomLink href={`/${fundSlug}/account/settings`} className="text-foreground">
<DropdownMenuItem>Settings</DropdownMenuItem>
</CustomLink>
<DropdownMenuItem onClick={() => signOut({ callbackUrl: `/${fundSlug}` })}>
Logout
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
{!!fundSlug && <MobileNav />}
</div>
<Dialog open={passwordResetIsOpen} onOpenChange={setPasswordResetIsOpen}>
<DialogContent>
<PasswordResetFormModal close={() => setPasswordResetIsOpen(false)} />
</DialogContent>
</Dialog>
</header>
)
}
export default Header