@fecommunity/reactpress-template-hello-world 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,356 @@
1
+ import { GetStaticProps } from 'next';
2
+ import Head from 'next/head';
3
+ import Link from 'next/link';
4
+ import { createApiInstance } from '@fecommunity/reactpress-toolkit';
5
+ import { types, utils } from '@fecommunity/reactpress-toolkit';
6
+ import Header from '../components/Header';
7
+ import Footer from '../components/Footer';
8
+
9
+ // Create a custom API instance with the desired baseURL
10
+ const customApi = createApiInstance({
11
+ baseURL: 'https://api.gaoredu.com/'
12
+ });
13
+
14
+ // Type definitions from the toolkit
15
+ type IArticle = types.IArticle;
16
+ type ICategory = types.ICategory;
17
+ type ITag = types.ITag;
18
+
19
+ interface HomeProps {
20
+ greeting: string;
21
+ articles: IArticle[];
22
+ categories: ICategory[];
23
+ tags: ITag[];
24
+ }
25
+
26
+ export default function Home({ greeting, articles, categories, tags }: HomeProps) {
27
+ // Example usage of utils
28
+ const formatDate = (dateString: string) => {
29
+ try {
30
+ const date = new Date(dateString);
31
+ return utils.formatDate(date, 'YYYY-MM-DD');
32
+ } catch (error) {
33
+ return 'Unknown date';
34
+ }
35
+ };
36
+
37
+ return (
38
+ <div className="container">
39
+ <Head>
40
+ <title>Hello World Template</title>
41
+ <meta name="description" content="A minimal hello-world template for ReactPress" />
42
+ <link rel="icon" href="/favicon.ico" />
43
+ </Head>
44
+
45
+ <Header currentPage="home" />
46
+
47
+ <main className="main">
48
+ <div className="content-wrapper">
49
+ <div className="hero">
50
+ <h1 className="hero-title">{greeting}</h1>
51
+ <p className="hero-description">
52
+ Welcome to your new ReactPress site! This is a minimal template to get you started quickly.
53
+ </p>
54
+ <div className="cta-buttons">
55
+ <Link href="/about">
56
+ <a className="cta-button primary">Learn More</a>
57
+ </Link>
58
+ <Link href="/toolkit-demo">
59
+ <a className="cta-button secondary">Toolkit Demo</a>
60
+ </Link>
61
+ </div>
62
+ </div>
63
+
64
+ {/* Articles Section */}
65
+ <div className="content-section">
66
+ <h2 className="section-title">Latest Articles</h2>
67
+ <div className="articles-grid">
68
+ {articles.slice(0, 3).map((article) => (
69
+ <div key={article.id} className="article-card">
70
+ <h3 className="article-title">{article.title}</h3>
71
+ {article.summary && (
72
+ <p className="article-summary">{article.summary}</p>
73
+ )}
74
+ <div className="article-meta">
75
+ <span className="publish-date">{formatDate(article.publishAt)}</span>
76
+ </div>
77
+ </div>
78
+ ))}
79
+ </div>
80
+ </div>
81
+
82
+ {/* Categories Section */}
83
+ <div className="content-section">
84
+ <h2 className="section-title">Categories</h2>
85
+ <div className="categories-list">
86
+ {categories.slice(0, 5).map((category) => (
87
+ <span key={category.value} className="category-tag">
88
+ {category.label} ({(category as any).articleCount || 0})
89
+ </span>
90
+ ))}
91
+ </div>
92
+ </div>
93
+
94
+ {/* Tags Section */}
95
+ <div className="content-section">
96
+ <h2 className="section-title">Popular Tags</h2>
97
+ <div className="tags-list">
98
+ {tags.slice(0, 10).map((tag) => (
99
+ <span key={tag.value} className="tag-item">
100
+ {tag.label}
101
+ </span>
102
+ ))}
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </main>
107
+
108
+ <Footer />
109
+
110
+ <style jsx>{`
111
+ .container {
112
+ min-height: 100vh;
113
+ display: flex;
114
+ flex-direction: column;
115
+ background-color: #f8f9fa;
116
+ }
117
+
118
+ .main {
119
+ flex: 1;
120
+ padding: 3rem 0;
121
+ }
122
+
123
+ .content-wrapper {
124
+ max-width: 1200px;
125
+ margin: 0 auto;
126
+ padding: 0 2rem;
127
+ }
128
+
129
+ .hero {
130
+ text-align: center;
131
+ padding: 4rem 0;
132
+ margin-bottom: 3rem;
133
+ background: #fff;
134
+ border-radius: 16px;
135
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
136
+ }
137
+
138
+ .hero-title {
139
+ font-size: 3rem;
140
+ font-weight: 800;
141
+ color: #111827;
142
+ margin: 0 0 1.5rem 0;
143
+ letter-spacing: -0.025em;
144
+ }
145
+
146
+ .hero-description {
147
+ color: #6b7280;
148
+ font-size: 1.25rem;
149
+ max-width: 600px;
150
+ margin: 0 auto 2.5rem;
151
+ line-height: 1.7;
152
+ }
153
+
154
+ .cta-buttons {
155
+ display: flex;
156
+ justify-content: center;
157
+ gap: 1rem;
158
+ }
159
+
160
+ .cta-button {
161
+ display: inline-block;
162
+ color: #fff;
163
+ text-decoration: none;
164
+ font-weight: 600;
165
+ padding: 0.875rem 1.75rem;
166
+ border-radius: 12px;
167
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
168
+ position: relative;
169
+ overflow: hidden;
170
+ }
171
+
172
+ .cta-button:hover {
173
+ transform: translateY(-3px);
174
+ box-shadow: 0 6px 15px rgba(59, 130, 246, 0.3);
175
+ }
176
+
177
+ .cta-button.primary {
178
+ background: linear-gradient(135deg, #3b82f6, #8b5cf6);
179
+ border: none;
180
+ }
181
+
182
+ .cta-button.secondary {
183
+ background: transparent;
184
+ color: #3b82f6;
185
+ border: 2px solid #3b82f6;
186
+ }
187
+
188
+ .content-section {
189
+ margin-bottom: 3rem;
190
+ }
191
+
192
+ .section-title {
193
+ font-size: 1.75rem;
194
+ font-weight: 700;
195
+ color: #111827;
196
+ margin: 0 0 1.5rem 0;
197
+ padding-bottom: 0.75rem;
198
+ border-bottom: 2px solid #e5e7eb;
199
+ }
200
+
201
+ .articles-grid {
202
+ display: grid;
203
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
204
+ gap: 1.5rem;
205
+ }
206
+
207
+ .article-card {
208
+ background: #fff;
209
+ border-radius: 12px;
210
+ padding: 1.5rem;
211
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
212
+ transition: all 0.3s ease;
213
+ }
214
+
215
+ .article-card:hover {
216
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
217
+ transform: translateY(-2px);
218
+ }
219
+
220
+ .article-title {
221
+ font-size: 1.25rem;
222
+ font-weight: 600;
223
+ color: #111827;
224
+ margin: 0 0 0.75rem 0;
225
+ }
226
+
227
+ .article-summary {
228
+ color: #6b7280;
229
+ line-height: 1.6;
230
+ margin: 0 0 1rem 0;
231
+ }
232
+
233
+ .article-meta {
234
+ display: flex;
235
+ justify-content: space-between;
236
+ align-items: center;
237
+ font-size: 0.9rem;
238
+ color: #9ca3af;
239
+ }
240
+
241
+ .publish-date {
242
+ font-style: italic;
243
+ }
244
+
245
+ .categories-list {
246
+ display: flex;
247
+ flex-wrap: wrap;
248
+ gap: 0.75rem;
249
+ }
250
+
251
+ .category-tag {
252
+ background: #eff6ff;
253
+ color: #3b82f6;
254
+ padding: 0.5rem 1rem;
255
+ border-radius: 9999px;
256
+ font-size: 0.9rem;
257
+ font-weight: 500;
258
+ }
259
+
260
+ .tags-list {
261
+ display: flex;
262
+ flex-wrap: wrap;
263
+ gap: 0.5rem;
264
+ }
265
+
266
+ .tag-item {
267
+ background: #f3f4f6;
268
+ color: #4b5563;
269
+ padding: 0.4rem 0.8rem;
270
+ border-radius: 0.375rem;
271
+ font-size: 0.875rem;
272
+ }
273
+
274
+ /* Responsive Design */
275
+ @media (max-width: 768px) {
276
+ .hero-title {
277
+ font-size: 2.2rem;
278
+ }
279
+
280
+ .hero-description {
281
+ font-size: 1.1rem;
282
+ }
283
+
284
+ .cta-buttons {
285
+ flex-direction: column;
286
+ align-items: center;
287
+ }
288
+
289
+ .articles-grid {
290
+ grid-template-columns: 1fr;
291
+ }
292
+ }
293
+ `}</style>
294
+
295
+ <style jsx global>{`
296
+ html,
297
+ body {
298
+ padding: 0;
299
+ margin: 0;
300
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
301
+ background-color: #f8f9fa;
302
+ }
303
+
304
+ * {
305
+ box-sizing: border-box;
306
+ }
307
+ `}</style>
308
+ </div>
309
+ );
310
+ }
311
+
312
+ export const getStaticProps: GetStaticProps<HomeProps> = async () => {
313
+ try {
314
+ // Fetch data using the ReactPress toolkit
315
+ // Cast to any to access the actual response data
316
+ const [articlesResponse, categoriesResponse, tagsResponse] = await Promise.all([
317
+ customApi.article.findAll() as any,
318
+ customApi.category.findAll() as any,
319
+ customApi.tag.findAll() as any,
320
+ ]);
321
+
322
+ // Extract the actual data from the responses
323
+ // The API response is wrapped in { statusCode, msg, success, data }
324
+ // For paginated responses, data is [items, total]
325
+ const articles = articlesResponse?.data?.data?.[0] || [];
326
+ const categories = categoriesResponse?.data?.data || [];
327
+ const tags = tagsResponse?.data?.data || [];
328
+
329
+ return {
330
+ props: {
331
+ greeting: "Hello, World!",
332
+ articles,
333
+ categories,
334
+ tags,
335
+ },
336
+ revalidate: 60, // Revalidate at most once per minute
337
+ };
338
+ } catch (error) {
339
+ console.error('Failed to fetch data:', error);
340
+
341
+ // Example of using utils.ApiError
342
+ if (utils.ApiError.isInstance(error)) {
343
+ console.error(`API Error ${error.code}: ${error.message}`);
344
+ }
345
+
346
+ return {
347
+ props: {
348
+ greeting: "Hello, World!",
349
+ articles: [],
350
+ categories: [],
351
+ tags: [],
352
+ },
353
+ revalidate: 60,
354
+ };
355
+ }
356
+ };