@@ -36,36 +36,29 @@ export function ArticleLayout({
36
36
const { data : session } = useSession ( )
37
37
const [ hasPurchased , setHasPurchased ] = useState ( serverHasPurchased )
38
38
39
+ // Ensure we have string values for required fields with proper defaults
40
+ const safeSlug = metadata ?. slug || '' ;
41
+ const safeTitle = metadata ?. title as string || 'Untitled' ;
42
+ const safeType = typeof metadata ?. type === 'string' ? metadata . type : 'blog' ;
43
+ const safeDescription = metadata ?. description as string || '' ;
44
+
39
45
// Add a debug log to help identify which articles are missing slugs
40
- if ( ! metadata . slug || metadata . slug === '' ) {
46
+ if ( ! safeSlug ) {
41
47
// Instead of just logging an error, log more details to help debug
42
48
logger . warn ( 'ArticleLayout: metadata missing slug' , {
43
- title : metadata . title ,
44
- type : metadata . type ,
45
- date : metadata . date
49
+ title : safeTitle ,
50
+ type : safeType ,
51
+ date : metadata ? .date
46
52
} ) ;
47
53
}
48
-
49
- // Ensure we have string values for required fields
50
- const safeSlug = metadata . slug || '' ;
51
- const safeTitle = metadata . title as string || 'Untitled' ;
52
- const safeType = metadata . type || 'blog' ;
53
54
54
- // Extract the base slug to help with matching
55
- const baseSlug = safeSlug . split ( '/' ) . pop ( ) || safeSlug ;
56
-
57
- // Only log debug info if DEBUG_METADATA environment variable is set
58
- // const isDebugMode = process.env.NODE_ENV === 'development' && process.env.DEBUG_METADATA === 'true';
59
- // const debugLog = (message: string) => {
60
- // if (isDebugMode) {
61
- // console.log(`[ArticleLayout] ${message}`);
62
- // }
63
- // };
55
+ // Extract the base slug to help with matching - handle empty strings gracefully
56
+ const baseSlug = safeSlug ? safeSlug . split ( '/' ) . pop ( ) || safeSlug : '' ;
64
57
65
58
logger . debug ( `Rendering for slug: ${ safeSlug } , baseSlug: ${ baseSlug } ` ) ;
66
59
67
60
// Add more debug information about the image
68
- if ( metadata . image ) {
61
+ if ( metadata ? .image ) {
69
62
logger . debug ( `Image type: ${ typeof metadata . image } ` ) ;
70
63
if ( typeof metadata . image === 'object' && metadata . image !== null ) {
71
64
logger . debug ( `Image keys: ${ Object . keys ( metadata . image ) . join ( ',' ) } ` ) ;
@@ -82,8 +75,8 @@ export function ArticleLayout({
82
75
// Generate the OG URL with proper slug parameter
83
76
const ogUrl = generateOgUrl ( {
84
77
title : safeTitle ,
85
- description : typeof metadata . description === 'string' ? metadata . description : undefined ,
86
- image : metadata . image ,
78
+ description : safeDescription || undefined ,
79
+ image : metadata ? .image ,
87
80
slug : baseSlug as any // Force type to match expected parameter type
88
81
} ) ;
89
82
@@ -100,7 +93,8 @@ export function ArticleLayout({
100
93
}
101
94
102
95
const checkPurchaseStatus = async ( ) => {
103
- if ( ! metadata . commerce ?. isPaid || ! safeSlug ) return ;
96
+ // Make sure both conditions are checked with null/undefined safety
97
+ if ( ! metadata ?. commerce ?. isPaid || ! safeSlug ) return ;
104
98
105
99
try {
106
100
// If user is signed in, use their email
@@ -127,51 +121,55 @@ export function ArticleLayout({
127
121
} ;
128
122
129
123
checkPurchaseStatus ( ) ;
130
- } , [ session , safeSlug , metadata . commerce ?. isPaid , serverHasPurchased ] ) ;
124
+ } , [ session , safeSlug , metadata ? .commerce ?. isPaid , serverHasPurchased ] ) ;
131
125
132
- // Determine if we should show the mini paywall
126
+ // Determine if we should show the mini paywall - add null checks for all properties
133
127
const shouldShowMiniPaywall =
134
- metadata . commerce ?. isPaid &&
135
- ! metadata . hideMiniPaywall &&
128
+ metadata ? .commerce ?. isPaid &&
129
+ ! metadata ? .hideMiniPaywall &&
136
130
! hasPurchased &&
137
- ( metadata . miniPaywallTitle || metadata . commerce . miniPaywallTitle ) &&
138
- ( metadata . miniPaywallDescription || metadata . commerce ?. miniPaywallDescription ) ;
131
+ ( metadata ? .miniPaywallTitle || metadata ? .commerce ? .miniPaywallTitle ) &&
132
+ ( metadata ? .miniPaywallDescription || metadata ? .commerce ?. miniPaywallDescription ) ;
139
133
140
134
// Build the full URL for og:url and twitter:url
141
135
let rootPath = '/blog/' ;
136
+
137
+ // Use a more flexible approach to determine the root path
142
138
if ( safeType === 'video' ) {
143
139
rootPath = '/videos/' ;
144
140
} else if ( safeType === 'course' ) {
145
141
rootPath = '/learn/courses/' ;
142
+ } else if ( typeof safeType === 'string' && safeType . includes ( 'comparison' ) || safeSlug . includes ( 'comparisons/' ) ) {
143
+ rootPath = '/comparisons/' ;
146
144
}
147
145
148
- const fullUrl = `${ process . env . NEXT_PUBLIC_SITE_URL } ${ rootPath } ${ safeSlug } ` ;
146
+ const fullUrl = `${ process . env . NEXT_PUBLIC_SITE_URL || '' } ${ rootPath } ${ safeSlug } ` ;
149
147
150
148
return (
151
149
< >
152
150
< Head >
153
151
< title > { `${ safeTitle } - Zachary Proser` } </ title >
154
- < meta name = "description" content = { metadata . description as string } />
152
+ < meta name = "description" content = { safeDescription } />
155
153
156
154
{ /* Open Graph tags */ }
157
155
< meta property = "og:title" content = { safeTitle } />
158
- < meta property = "og:description" content = { metadata . description as string } />
156
+ < meta property = "og:description" content = { safeDescription } />
159
157
< meta property = "og:url" content = { fullUrl } />
160
158
< meta property = "og:type" content = "article" />
161
159
< meta property = "og:image" content = { ogUrl } />
162
160
163
161
{ /* Twitter card tags */ }
164
162
< meta name = "twitter:card" content = "summary_large_image" />
165
163
< meta name = "twitter:title" content = { safeTitle } />
166
- < meta name = "twitter:description" content = { metadata . description as string } />
164
+ < meta name = "twitter:description" content = { safeDescription } />
167
165
< meta name = "twitter:image" content = { ogUrl } />
168
166
< meta name = "twitter:domain" content = "zackproser.com" />
169
167
</ Head >
170
168
171
169
< Container className = "mt-16 lg:mt-32" >
172
170
< div className = "xl:relative" >
173
171
< div className = "mx-auto max-w-2xl" >
174
- { shouldShowMiniPaywall && (
172
+ { shouldShowMiniPaywall && metadata && (
175
173
< MiniPaywall
176
174
content = { metadata as Content }
177
175
/>
@@ -182,11 +180,11 @@ export function ArticleLayout({
182
180
{ safeTitle }
183
181
</ h1 >
184
182
< time
185
- dateTime = { metadata . date }
183
+ dateTime = { metadata ? .date }
186
184
className = "order-first flex items-center text-base text-gray-500 dark:text-gray-400"
187
185
>
188
186
< span className = "h-4 w-0.5 rounded-full bg-blue-200 dark:bg-blue-700" />
189
- < span className = "ml-3" > { metadata . date } </ span >
187
+ < span className = "ml-3" > { metadata ? .date } </ span >
190
188
</ time >
191
189
</ header >
192
190
0 commit comments