Date: Mon, 26 Jan 2026 19:12:32 -0800
Subject: [PATCH 14/23] Add ButtonLink component
---
docs/coding-guide.md | 13 ++-----
.../[center]/events/[slug]/page.tsx | 18 +++++----
src/app/(frontend)/[center]/not-found.tsx | 16 ++++----
.../observations/SingleObservationPage.tsx | 15 ++++---
.../(frontend)/[center]/observations/page.tsx | 9 ++---
src/app/[...segmentsNotFound]/page.tsx | 10 ++---
src/blocks/BlogList/Component.tsx | 12 +++---
src/blocks/EventList/Component.tsx | 12 +++---
src/components/ButtonLink/index.tsx | 30 ++++++++++++++
src/components/EventPreview/index.tsx | 24 +++++++-----
src/components/EventsTable/index.tsx | 39 ++++++++-----------
11 files changed, 110 insertions(+), 88 deletions(-)
create mode 100644 src/components/ButtonLink/index.tsx
diff --git a/docs/coding-guide.md b/docs/coding-guide.md
index 4dad695c..531005d8 100644
--- a/docs/coding-guide.md
+++ b/docs/coding-guide.md
@@ -204,15 +204,10 @@ Interactive actions or buttons as link. A base component using Radix UI's Slot p
// With link
-
-
-// With server action
-
```
diff --git a/src/app/(frontend)/[center]/events/[slug]/page.tsx b/src/app/(frontend)/[center]/events/[slug]/page.tsx
index 99824d44..1ad2af8e 100644
--- a/src/app/(frontend)/[center]/events/[slug]/page.tsx
+++ b/src/app/(frontend)/[center]/events/[slug]/page.tsx
@@ -4,15 +4,14 @@ import configPromise from '@payload-config'
import { draftMode } from 'next/headers'
import { getPayload } from 'payload'
+import { ButtonLink } from '@/components/ButtonLink'
import { EventInfo } from '@/components/EventInfo'
import { Media } from '@/components/Media'
-import { Button } from '@/components/ui/button'
import { formatDateTime } from '@/utilities/formatDateTime'
import { generateMetaForEvent } from '@/utilities/generateMeta'
import { cn } from '@/utilities/ui'
import { ExternalLink } from 'lucide-react'
import { Metadata, ResolvedMetadata } from 'next'
-import Link from 'next/link'
import { redirect } from 'next/navigation'
export const dynamic = 'force-static'
@@ -123,12 +122,15 @@ export default async function Event({ params: paramsPromise }: Args) {
)}
{!isPastEvent && !isRegistrationClosed ? (
-
+
+ Register for Event
+
+
) : (
{isPastEvent ? 'This event has passed' : 'Registration is closed'}
diff --git a/src/app/(frontend)/[center]/not-found.tsx b/src/app/(frontend)/[center]/not-found.tsx
index 3aa674fc..7cf297f2 100644
--- a/src/app/(frontend)/[center]/not-found.tsx
+++ b/src/app/(frontend)/[center]/not-found.tsx
@@ -1,6 +1,4 @@
-import Link from 'next/link'
-
-import { Button } from '@/components/ui/button'
+import { ButtonLink } from '@/components/ButtonLink'
import NotFoundClient from './not-found.client'
export default function NotFound() {
@@ -17,12 +15,12 @@ export default function NotFound() {
-
-
+
+ Back to home
+
+
+ Check the avalanche forecast
+
diff --git a/src/app/(frontend)/[center]/observations/SingleObservationPage.tsx b/src/app/(frontend)/[center]/observations/SingleObservationPage.tsx
index d745c77c..62c203b0 100644
--- a/src/app/(frontend)/[center]/observations/SingleObservationPage.tsx
+++ b/src/app/(frontend)/[center]/observations/SingleObservationPage.tsx
@@ -1,7 +1,6 @@
+import { ButtonLink } from '@/components/ButtonLink'
import { NACWidget } from '@/components/NACWidget'
import ObservationsDisclaimer from '@/components/ObservationsDisclaimer'
-import { Button } from '@/components/ui/button'
-import Link from 'next/link'
export default function SingleObservationPage({
title,
@@ -20,12 +19,12 @@ export default function SingleObservationPage({
{title}
-
-
+
+ Recent Observations
+
+
+ Submit Observation
+
diff --git a/src/app/(frontend)/[center]/observations/page.tsx b/src/app/(frontend)/[center]/observations/page.tsx
index 62a0299e..c3e9c204 100644
--- a/src/app/(frontend)/[center]/observations/page.tsx
+++ b/src/app/(frontend)/[center]/observations/page.tsx
@@ -3,13 +3,12 @@ import type { Metadata, ResolvedMetadata } from 'next/types'
import configPromise from '@payload-config'
import { getPayload } from 'payload'
+import { ButtonLink } from '@/components/ButtonLink'
import { NACWidget } from '@/components/NACWidget'
import { WidgetRouterHandler } from '@/components/NACWidget/WidgetRouterHandler.client'
import ObservationsDisclaimer from '@/components/ObservationsDisclaimer'
-import { Button } from '@/components/ui/button'
import { getAvalancheCenterPlatforms } from '@/services/nac/nac'
import { getNACWidgetsConfig } from '@/utilities/getNACWidgetsConfig'
-import Link from 'next/link'
import { notFound } from 'next/navigation'
import { ObservationLinkHijacker } from './ObservationLinkHijacker.client'
@@ -55,9 +54,9 @@ export default async function Page({ params }: Args) {
Observations
-
+
+ Submit Observation
+
diff --git a/src/app/[...segmentsNotFound]/page.tsx b/src/app/[...segmentsNotFound]/page.tsx
index 1664c465..acc07ed1 100644
--- a/src/app/[...segmentsNotFound]/page.tsx
+++ b/src/app/[...segmentsNotFound]/page.tsx
@@ -1,6 +1,4 @@
-import Link from 'next/link'
-
-import { Button } from '@/components/ui/button'
+import { ButtonLink } from '@/components/ButtonLink'
// Prevent caching 404 responses so new routes can take over when content is created
export const dynamic = 'force-dynamic'
@@ -17,9 +15,9 @@ export default function NotFound() {
-
+
+ Find your local avalanche center
+
diff --git a/src/blocks/BlogList/Component.tsx b/src/blocks/BlogList/Component.tsx
index 172e5d2b..03ce94ac 100644
--- a/src/blocks/BlogList/Component.tsx
+++ b/src/blocks/BlogList/Component.tsx
@@ -1,8 +1,8 @@
'use client'
+import { ButtonLink } from '@/components/ButtonLink'
import { PostPreviewSmallRow } from '@/components/PostPreviewSmallRow'
import RichText from '@/components/RichText'
-import { Button } from '@/components/ui/button'
import type { BlogListBlock as BlogListBlockProps, Post } from '@/payload-types'
import { useTenant } from '@/providers/TenantProvider'
import {
@@ -10,7 +10,6 @@ import {
filterValidRelationships,
} from '@/utilities/relationships'
import { cn } from '@/utilities/ui'
-import Link from 'next/link'
import { useEffect, useState } from 'react'
type BlogListComponentProps = BlogListBlockProps & {
@@ -121,9 +120,12 @@ export const BlogListBlockComponent = (args: BlogListComponentProps) => {
)}
{postOptions === 'dynamic' && (
-
+
+ View all {heading}
+
)}
diff --git a/src/blocks/EventList/Component.tsx b/src/blocks/EventList/Component.tsx
index 81a41c6a..f47c427c 100644
--- a/src/blocks/EventList/Component.tsx
+++ b/src/blocks/EventList/Component.tsx
@@ -1,14 +1,13 @@
'use client'
+import { ButtonLink } from '@/components/ButtonLink'
import { EventPreviewSmallRow } from '@/components/EventPreviewSmallRow'
import RichText from '@/components/RichText'
-import { Button } from '@/components/ui/button'
import type { Event, EventListBlock as EventListBlockProps } from '@/payload-types'
import { useTenant } from '@/providers/TenantProvider'
import { filterValidPublishedRelationships } from '@/utilities/relationships'
import { cn } from '@/utilities/ui'
import { format } from 'date-fns'
-import Link from 'next/link'
import { useEffect, useState } from 'react'
type EventListComponentProps = EventListBlockProps & {
@@ -128,9 +127,12 @@ export const EventListBlockComponent = (args: EventListComponentProps) => {
)}
{eventOptions === 'dynamic' && (
-
+
+ View all {heading}
+
)}
diff --git a/src/components/ButtonLink/index.tsx b/src/components/ButtonLink/index.tsx
new file mode 100644
index 00000000..1f111bf1
--- /dev/null
+++ b/src/components/ButtonLink/index.tsx
@@ -0,0 +1,30 @@
+import Link from 'next/link'
+import * as React from 'react'
+import { Button, type ButtonProps } from '../ui/button'
+
+export interface ButtonLinkProps extends ButtonProps {
+ href: string
+ newTab?: boolean
+}
+
+const ButtonLink = React.forwardRef(
+ ({ href, newTab = false, children, ...props }, ref) => {
+ const externalProps = newTab
+ ? {
+ target: '_blank',
+ rel: 'noopener noreferrer',
+ }
+ : {}
+
+ return (
+
+ )
+ },
+)
+ButtonLink.displayName = 'ButtonLink'
+
+export { ButtonLink }
diff --git a/src/components/EventPreview/index.tsx b/src/components/EventPreview/index.tsx
index 10757315..2620f4fd 100644
--- a/src/components/EventPreview/index.tsx
+++ b/src/components/EventPreview/index.tsx
@@ -5,6 +5,7 @@ import { formatDateTime } from '@/utilities/formatDateTime'
import { cn } from '@/utilities/ui'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
+import { ButtonLink } from '../ButtonLink'
import { EventInfo } from '../EventInfo'
import { ImageMedia } from '../Media/ImageMedia'
import { Badge } from '../ui/badge'
@@ -141,21 +142,24 @@ export const EventPreview = (props: {