diff --git a/apps/web/components/layout/header.component.tsx b/apps/web/components/layout/header.component.tsx index bfcca07..d602cc4 100644 --- a/apps/web/components/layout/header.component.tsx +++ b/apps/web/components/layout/header.component.tsx @@ -7,7 +7,7 @@ import Head from "next/head"; import Image from "next/legacy/image"; import Link from "next/link"; import { useRouter } from "next/router"; -import { Fragment, useMemo } from "react"; +import { Fragment, useEffect, useMemo, useState } from "react"; import { DEFAULT_TITLE, SUBTITLE, TAGLINE } from "../../data/marketing.data"; import { ROUTES } from "../../data/routes.data"; import logoImage from "../../public/images/logo.png"; @@ -18,15 +18,54 @@ import { MenuItem } from "../core/menu.component"; import { createToastWrapper } from "../core/toast.component"; export default function HeaderComponent() { - const { loading, user, billingDetails, signOut } = useUserData(); + const { loading, user, billingDetails, signOut, supabase } = useUserData(); const router = useRouter(); const prefersColorScheme = usePrefersColorScheme(); + const [hasPendingInvites, setHasPendingInvites] = useState(false); + + useEffect(() => { + if (!user) return; + supabase + .from("team_invitations") + .select("id") + .eq("status", "pending") + .eq("email", user.email) + .then(({ data }) => { + setHasPendingInvites(data?.length > 0); + }); + + const channel = supabase + .channel("schema-db-changes") + .on( + "postgres_changes", + { + event: "*", + schema: "public", + table: "team_invitations", + filter: `email=eq.${user.email}`, + }, + (payload) => { + const { new: newData, old: oldData } = payload; + if (newData) { + setHasPendingInvites(true); + } else if (oldData) { + setHasPendingInvites(false); + } + } + ) + .subscribe(); + + return () => { + channel.unsubscribe(); + }; + }, [user]); + const navigation = useMemo(() => { if (user) { return [ { name: "Pages", href: ROUTES.PAGES }, - { name: "Teams", href: ROUTES.TEAMS }, + { name: "Teams", href: ROUTES.TEAMS, pulse: hasPendingInvites }, { name: "Zapier", href: ROUTES.ZAPIER }, { name: "Billing", href: ROUTES.BILLING }, { name: "Support", href: ROUTES.SUPPORT, external: true }, @@ -136,6 +175,9 @@ export default function HeaderComponent() { rel={item.external ? "noopener noreferrer" : null} > {item.name} + {item.pulse ? ( + + ) : null} ))} @@ -246,8 +288,9 @@ export default function HeaderComponent() {
{navigation.map((item) => ( - {item.name} - + {item.pulse ? ( + + ) : null} + ))} {!user && ( diff --git a/packages/supabase/migrations/16_teams.sql b/packages/supabase/migrations/16_teams.sql index 6fa815d..8def046 100644 --- a/packages/supabase/migrations/16_teams.sql +++ b/packages/supabase/migrations/16_teams.sql @@ -110,4 +110,6 @@ create table page_audit_logs ( ); alter table page_audit_logs enable row level security; -create policy "Can insert page audit logs." on page_audit_logs for insert with check (actor_id = auth.uid()); \ No newline at end of file +create policy "Can insert page audit logs." on page_audit_logs for insert with check (actor_id = auth.uid()); + +alter publication supabase_realtime add table team_invitations; \ No newline at end of file