@idealyst/cli 1.0.41 → 1.0.44

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.
Files changed (78) hide show
  1. package/dist/generators/api.js +1 -2
  2. package/dist/generators/api.js.map +1 -1
  3. package/dist/generators/database.js +1 -2
  4. package/dist/generators/database.js.map +1 -1
  5. package/dist/generators/fullstack.js +371 -0
  6. package/dist/generators/fullstack.js.map +1 -0
  7. package/dist/generators/index.js +5 -0
  8. package/dist/generators/index.js.map +1 -1
  9. package/dist/generators/native.js +2 -1
  10. package/dist/generators/native.js.map +1 -1
  11. package/dist/generators/shared.js +1 -2
  12. package/dist/generators/shared.js.map +1 -1
  13. package/dist/generators/utils.js +71 -7
  14. package/dist/generators/utils.js.map +1 -1
  15. package/dist/generators/web.js +1 -2
  16. package/dist/generators/web.js.map +1 -1
  17. package/dist/generators/workspace.js +56 -2
  18. package/dist/generators/workspace.js.map +1 -1
  19. package/dist/index.js +50 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/templates/database/.env.example +1 -8
  22. package/dist/templates/database/README.md +29 -74
  23. package/dist/templates/database/package.json +20 -34
  24. package/dist/templates/database/prisma/seed.ts +11 -11
  25. package/dist/templates/database/schema.prisma +97 -0
  26. package/dist/templates/database/src/index.ts +12 -8
  27. package/dist/templates/database/tsconfig.json +9 -23
  28. package/dist/templates/native/src/App-with-trpc-and-shared.tsx +266 -0
  29. package/dist/templates/shared/package.json +28 -3
  30. package/dist/templates/shared/src/components/index.ts +392 -0
  31. package/dist/templates/shared/src/index.ts +59 -1
  32. package/dist/templates/shared/src/types/index.ts +148 -0
  33. package/dist/templates/shared/src/utils/index.ts +278 -0
  34. package/dist/templates/web/package.json +2 -2
  35. package/dist/templates/web/src/App-with-trpc-and-shared.tsx +304 -0
  36. package/dist/templates/workspace/.devcontainer/Dockerfile +1 -1
  37. package/dist/templates/workspace/.devcontainer/devcontainer.json +7 -2
  38. package/dist/templates/workspace/.devcontainer/docker-compose.yml +14 -0
  39. package/dist/templates/workspace/.devcontainer/figma-mcp.sh +32 -0
  40. package/dist/templates/workspace/.devcontainer/setup.sh +3 -0
  41. package/dist/templates/workspace/setup.sh +22 -197
  42. package/dist/templates/workspace/tsconfig.json +32 -0
  43. package/dist/types/generators/fullstack.d.ts +2 -0
  44. package/dist/types/generators/index.d.ts +1 -0
  45. package/dist/types/generators/utils.d.ts +4 -1
  46. package/dist/types/types.d.ts +3 -1
  47. package/package.json +1 -1
  48. package/templates/database/.env.example +1 -0
  49. package/templates/database/README.md +48 -0
  50. package/templates/database/package.json +21 -2
  51. package/templates/database/prisma/seed.ts +28 -0
  52. package/templates/database/schema.prisma +85 -9
  53. package/templates/database/src/index.ts +7 -7
  54. package/templates/database/src/validators.ts +10 -0
  55. package/templates/native/src/App-with-trpc-and-shared.tsx +266 -0
  56. package/templates/shared/package.json +28 -3
  57. package/templates/shared/src/components/index.ts +392 -0
  58. package/templates/shared/src/index.ts +59 -1
  59. package/templates/shared/src/types/index.ts +148 -0
  60. package/templates/shared/src/utils/index.ts +278 -0
  61. package/templates/web/package.json +1 -1
  62. package/templates/web/src/App-with-trpc-and-shared.tsx +304 -0
  63. package/templates/workspace/.devcontainer/devcontainer.json +7 -2
  64. package/templates/workspace/.devcontainer/docker-compose.yml +14 -0
  65. package/templates/workspace/.devcontainer/figma-mcp.sh +32 -0
  66. package/templates/workspace/.devcontainer/setup.sh +3 -0
  67. package/templates/workspace/setup.sh +30 -0
  68. package/templates/workspace/tsconfig.json +32 -0
  69. package/dist/templates/database/__tests__/database.test.ts +0 -14
  70. package/dist/templates/database/jest.config.js +0 -19
  71. package/dist/templates/database/jest.setup.js +0 -11
  72. package/dist/templates/database/prisma/schema.prisma +0 -21
  73. package/dist/templates/database/src/client.ts +0 -18
  74. package/dist/templates/database/src/schemas.ts +0 -26
  75. package/dist/templates/workspace/scripts/docker/db-backup.sh +0 -230
  76. package/dist/templates/workspace/scripts/docker/deploy.sh +0 -212
  77. package/dist/templates/workspace/scripts/test-runner.js +0 -120
  78. /package/{templates/database/src/validatgors.ts → dist/templates/database/src/validators.ts} +0 -0
@@ -2,9 +2,31 @@
2
2
  "name": "{{packageName}}",
3
3
  "version": "{{version}}",
4
4
  "description": "{{description}}",
5
- "main": "src/index.ts",
6
- "module": "src/index.ts",
7
- "types": "src/index.ts",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./types": {
15
+ "import": "./dist/types/index.js",
16
+ "require": "./dist/types/index.js",
17
+ "types": "./dist/types/index.d.ts"
18
+ },
19
+ "./components": {
20
+ "import": "./dist/components/index.js",
21
+ "require": "./dist/components/index.js",
22
+ "types": "./dist/components/index.d.ts"
23
+ },
24
+ "./utils": {
25
+ "import": "./dist/utils/index.js",
26
+ "require": "./dist/utils/index.js",
27
+ "types": "./dist/utils/index.d.ts"
28
+ }
29
+ },
8
30
  "scripts": {
9
31
  "build": "tsc",
10
32
  "test": "jest",
@@ -12,6 +34,9 @@
12
34
  "test:coverage": "jest --coverage",
13
35
  "type-check": "tsc --noEmit"
14
36
  },
37
+ "dependencies": {
38
+ "zod": "^3.22.4"
39
+ },
15
40
  "peerDependencies": {
16
41
  "@idealyst/components": "^1.0.21",
17
42
  "@idealyst/navigation": "^1.0.21",
@@ -0,0 +1,392 @@
1
+ import React from 'react';
2
+ import { View, Text, Button, Image } from '@idealyst/components';
3
+ import type { User, Post, Comment } from '../types';
4
+
5
+ interface UserCardProps {
6
+ user: User;
7
+ onPress?: () => void;
8
+ showBio?: boolean;
9
+ }
10
+
11
+ export const UserCard: React.FC<UserCardProps> = ({
12
+ user,
13
+ onPress,
14
+ showBio = false
15
+ }) => {
16
+ return (
17
+ <View
18
+ style={{
19
+ padding: 16,
20
+ borderRadius: 8,
21
+ backgroundColor: 'white',
22
+ shadowColor: '#000',
23
+ shadowOffset: { width: 0, height: 2 },
24
+ shadowOpacity: 0.1,
25
+ shadowRadius: 4,
26
+ elevation: 2,
27
+ marginBottom: 8
28
+ }}
29
+ >
30
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
31
+ {user.avatar && (
32
+ <Image
33
+ source={{ uri: user.avatar }}
34
+ style={{
35
+ width: 50,
36
+ height: 50,
37
+ borderRadius: 25,
38
+ marginRight: 12
39
+ }}
40
+ />
41
+ )}
42
+ <View style={{ flex: 1 }}>
43
+ <Text variant="h3" style={{ marginBottom: 4 }}>
44
+ {user.name || 'Anonymous User'}
45
+ </Text>
46
+ <Text variant="caption" style={{ color: 'gray' }}>
47
+ {user.email}
48
+ </Text>
49
+ {user.location && (
50
+ <Text variant="caption" style={{ color: 'gray', marginTop: 2 }}>
51
+ 📍 {user.location}
52
+ </Text>
53
+ )}
54
+ </View>
55
+ {onPress && (
56
+ <Button
57
+ title="View Profile"
58
+ onPress={onPress}
59
+ size="small"
60
+ />
61
+ )}
62
+ </View>
63
+
64
+ {showBio && user.bio && (
65
+ <Text variant="body" style={{ marginTop: 12, fontStyle: 'italic' }}>
66
+ {user.bio}
67
+ </Text>
68
+ )}
69
+
70
+ {user.website && (
71
+ <Text variant="caption" style={{ marginTop: 8, color: 'blue' }}>
72
+ 🌐 {user.website}
73
+ </Text>
74
+ )}
75
+ </View>
76
+ );
77
+ };
78
+
79
+ interface PostCardProps {
80
+ post: Post;
81
+ author?: User;
82
+ onPress?: () => void;
83
+ onLike?: () => void;
84
+ showFullContent?: boolean;
85
+ }
86
+
87
+ export const PostCard: React.FC<PostCardProps> = ({
88
+ post,
89
+ author,
90
+ onPress,
91
+ onLike,
92
+ showFullContent = false
93
+ }) => {
94
+ const content = showFullContent ? post.content : (post.excerpt || post.content.substring(0, 150) + '...');
95
+
96
+ return (
97
+ <View
98
+ style={{
99
+ padding: 16,
100
+ borderRadius: 8,
101
+ backgroundColor: 'white',
102
+ shadowColor: '#000',
103
+ shadowOffset: { width: 0, height: 2 },
104
+ shadowOpacity: 0.1,
105
+ shadowRadius: 4,
106
+ elevation: 2,
107
+ marginBottom: 12
108
+ }}
109
+ >
110
+ {/* Post Header */}
111
+ {author && (
112
+ <View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 12 }}>
113
+ {author.avatar && (
114
+ <Image
115
+ source={{ uri: author.avatar }}
116
+ style={{
117
+ width: 32,
118
+ height: 32,
119
+ borderRadius: 16,
120
+ marginRight: 8
121
+ }}
122
+ />
123
+ )}
124
+ <View>
125
+ <Text variant="body" style={{ fontWeight: 'bold' }}>
126
+ {author.name || 'Anonymous'}
127
+ </Text>
128
+ <Text variant="caption" style={{ color: 'gray' }}>
129
+ {new Date(post.createdAt).toLocaleDateString()}
130
+ </Text>
131
+ </View>
132
+ </View>
133
+ )}
134
+
135
+ {/* Post Content */}
136
+ <Text variant="h2" style={{ marginBottom: 8 }}>
137
+ {post.title}
138
+ </Text>
139
+
140
+ <Text variant="body" style={{ marginBottom: 12 }}>
141
+ {content}
142
+ </Text>
143
+
144
+ {/* Tags */}
145
+ {post.tags.length > 0 && (
146
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', marginBottom: 12 }}>
147
+ {post.tags.map((tag, index) => (
148
+ <View
149
+ key={index}
150
+ style={{
151
+ backgroundColor: '#e3f2fd',
152
+ paddingHorizontal: 8,
153
+ paddingVertical: 4,
154
+ borderRadius: 12,
155
+ marginRight: 6,
156
+ marginBottom: 4
157
+ }}
158
+ >
159
+ <Text variant="caption" style={{ color: '#1976d2' }}>
160
+ #{tag}
161
+ </Text>
162
+ </View>
163
+ ))}
164
+ </View>
165
+ )}
166
+
167
+ {/* Post Stats and Actions */}
168
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
169
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
170
+ <Text variant="caption" style={{ color: 'gray', marginRight: 12 }}>
171
+ 👁️ {post.views} views
172
+ </Text>
173
+ <Text variant="caption" style={{ color: 'gray' }}>
174
+ ❤️ {post.likes} likes
175
+ </Text>
176
+ </View>
177
+
178
+ <View style={{ flexDirection: 'row' }}>
179
+ {onLike && (
180
+ <Button
181
+ title="Like"
182
+ onPress={onLike}
183
+ size="small"
184
+ variant="outline"
185
+ style={{ marginRight: 8 }}
186
+ />
187
+ )}
188
+ {onPress && (
189
+ <Button
190
+ title="Read More"
191
+ onPress={onPress}
192
+ size="small"
193
+ />
194
+ )}
195
+ </View>
196
+ </View>
197
+ </View>
198
+ );
199
+ };
200
+
201
+ interface CommentCardProps {
202
+ comment: Comment;
203
+ author?: User;
204
+ onReply?: () => void;
205
+ level?: number;
206
+ }
207
+
208
+ export const CommentCard: React.FC<CommentCardProps> = ({
209
+ comment,
210
+ author,
211
+ onReply,
212
+ level = 0
213
+ }) => {
214
+ const indentStyle = {
215
+ marginLeft: level * 20,
216
+ maxWidth: level > 2 ? '90%' : '100%'
217
+ };
218
+
219
+ return (
220
+ <View
221
+ style={[
222
+ {
223
+ padding: 12,
224
+ borderRadius: 6,
225
+ backgroundColor: level === 0 ? 'white' : '#f8f9fa',
226
+ borderLeftWidth: level > 0 ? 3 : 0,
227
+ borderLeftColor: '#e3f2fd',
228
+ marginBottom: 8
229
+ },
230
+ indentStyle
231
+ ]}
232
+ >
233
+ {/* Comment Header */}
234
+ {author && (
235
+ <View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 8 }}>
236
+ {author.avatar && (
237
+ <Image
238
+ source={{ uri: author.avatar }}
239
+ style={{
240
+ width: 24,
241
+ height: 24,
242
+ borderRadius: 12,
243
+ marginRight: 6
244
+ }}
245
+ />
246
+ )}
247
+ <Text variant="body" style={{ fontWeight: 'bold', fontSize: 14 }}>
248
+ {author.name || 'Anonymous'}
249
+ </Text>
250
+ <Text variant="caption" style={{ color: 'gray', marginLeft: 8 }}>
251
+ {new Date(comment.createdAt).toLocaleDateString()}
252
+ </Text>
253
+ </View>
254
+ )}
255
+
256
+ {/* Comment Content */}
257
+ <Text variant="body" style={{ marginBottom: 8 }}>
258
+ {comment.content}
259
+ </Text>
260
+
261
+ {/* Comment Actions */}
262
+ {onReply && level < 3 && (
263
+ <Button
264
+ title="Reply"
265
+ onPress={onReply}
266
+ size="small"
267
+ variant="outline"
268
+ />
269
+ )}
270
+ </View>
271
+ );
272
+ };
273
+
274
+ interface LoadingSpinnerProps {
275
+ size?: 'small' | 'medium' | 'large';
276
+ message?: string;
277
+ }
278
+
279
+ export const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
280
+ size = 'medium',
281
+ message
282
+ }) => {
283
+ const sizeMap = {
284
+ small: 20,
285
+ medium: 40,
286
+ large: 60
287
+ };
288
+
289
+ return (
290
+ <View style={{ alignItems: 'center', justifyContent: 'center', padding: 20 }}>
291
+ <View
292
+ style={{
293
+ width: sizeMap[size],
294
+ height: sizeMap[size],
295
+ borderRadius: sizeMap[size] / 2,
296
+ borderWidth: 3,
297
+ borderColor: '#e3f2fd',
298
+ borderTopColor: '#1976d2',
299
+ // Animation would be handled by the platform-specific implementation
300
+ }}
301
+ />
302
+ {message && (
303
+ <Text variant="body" style={{ marginTop: 12, textAlign: 'center' }}>
304
+ {message}
305
+ </Text>
306
+ )}
307
+ </View>
308
+ );
309
+ };
310
+
311
+ interface ErrorMessageProps {
312
+ message: string;
313
+ onRetry?: () => void;
314
+ }
315
+
316
+ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ message, onRetry }) => {
317
+ return (
318
+ <View
319
+ style={{
320
+ padding: 16,
321
+ backgroundColor: '#ffebee',
322
+ borderRadius: 8,
323
+ borderLeftWidth: 4,
324
+ borderLeftColor: '#f44336',
325
+ margin: 16
326
+ }}
327
+ >
328
+ <Text variant="body" style={{ color: '#c62828', marginBottom: onRetry ? 12 : 0 }}>
329
+ ⚠️ {message}
330
+ </Text>
331
+ {onRetry && (
332
+ <Button
333
+ title="Try Again"
334
+ onPress={onRetry}
335
+ size="small"
336
+ style={{ backgroundColor: '#f44336' }}
337
+ />
338
+ )}
339
+ </View>
340
+ );
341
+ };
342
+
343
+ // Feature Card Component - reusable for both web and native
344
+ interface FeatureCardProps {
345
+ icon: string;
346
+ title: string;
347
+ description: string;
348
+ }
349
+
350
+ export const FeatureCard: React.FC<FeatureCardProps> = ({ icon, title, description }) => {
351
+ return (
352
+ <View style={{
353
+ padding: 16,
354
+ backgroundColor: 'white',
355
+ borderRadius: 8,
356
+ shadowColor: '#000',
357
+ shadowOffset: { width: 0, height: 2 },
358
+ shadowOpacity: 0.1,
359
+ shadowRadius: 4,
360
+ elevation: 2,
361
+ marginBottom: 12
362
+ }}>
363
+ <Text style={{ fontSize: 24, marginBottom: 8, textAlign: 'center' }}>{icon}</Text>
364
+ <Text variant="h4" style={{ marginBottom: 8, textAlign: 'center' }}>{title}</Text>
365
+ <Text variant="body" style={{ color: '#666', textAlign: 'center' }}>{description}</Text>
366
+ </View>
367
+ );
368
+ };
369
+
370
+ // Tab Button Component - for navigation tabs in mobile/web apps
371
+ interface TabButtonProps {
372
+ title: string;
373
+ icon: string;
374
+ active: boolean;
375
+ onPress: () => void;
376
+ }
377
+
378
+ export const TabButton: React.FC<TabButtonProps> = ({ title, icon, active, onPress }) => {
379
+ return (
380
+ <Button
381
+ title={`${icon} ${title}`}
382
+ onPress={onPress}
383
+ variant={active ? 'primary' : 'outline'}
384
+ style={{
385
+ flex: 1,
386
+ marginHorizontal: 4,
387
+ backgroundColor: active ? '#007bff' : 'transparent',
388
+ borderColor: active ? '#007bff' : '#ccc'
389
+ }}
390
+ />
391
+ );
392
+ };
@@ -1 +1,59 @@
1
- export const add_stuff = "here";
1
+ // Export all types
2
+ export * from './types';
3
+
4
+ // Export all components
5
+ export * from './components';
6
+
7
+ // Export all utilities
8
+ export * from './utils';
9
+
10
+ // Re-export commonly used items for convenience
11
+ export type {
12
+ User,
13
+ Post,
14
+ Comment,
15
+ UserSettings,
16
+ CreateUser,
17
+ CreatePost,
18
+ CreateComment,
19
+ PostWithAuthor,
20
+ CommentWithAuthor,
21
+ ApiResponse,
22
+ PaginatedResponse,
23
+ Theme,
24
+ LoadingState,
25
+ PaginationParams
26
+ } from './types';
27
+
28
+ export {
29
+ UserCard,
30
+ PostCard,
31
+ CommentCard,
32
+ LoadingSpinner,
33
+ ErrorMessage,
34
+ FeatureCard,
35
+ TabButton
36
+ } from './components';
37
+
38
+ export {
39
+ formatDate,
40
+ formatDateTime,
41
+ formatRelativeTime,
42
+ truncateText,
43
+ capitalizeFirst,
44
+ slugify,
45
+ paginate,
46
+ sortBy,
47
+ pick,
48
+ omit,
49
+ isValidEmail,
50
+ isValidUrl,
51
+ storage,
52
+ debounce,
53
+ getSystemTheme,
54
+ getErrorMessage,
55
+ isWeb,
56
+ isMobile,
57
+ DEMO_USERS,
58
+ DEMO_POSTS
59
+ } from './utils';
@@ -0,0 +1,148 @@
1
+ import { z } from 'zod';
2
+
3
+ // User related schemas and types
4
+ export const UserSchema = z.object({
5
+ id: z.string(),
6
+ email: z.string().email(),
7
+ name: z.string().nullable(),
8
+ avatar: z.string().url().nullable(),
9
+ bio: z.string().nullable(),
10
+ location: z.string().nullable(),
11
+ website: z.string().url().nullable(),
12
+ createdAt: z.date(),
13
+ updatedAt: z.date(),
14
+ });
15
+
16
+ export const CreateUserSchema = z.object({
17
+ email: z.string().email(),
18
+ name: z.string().min(1, 'Name is required'),
19
+ avatar: z.string().url().optional(),
20
+ bio: z.string().optional(),
21
+ location: z.string().optional(),
22
+ website: z.string().url().optional(),
23
+ });
24
+
25
+ export const UpdateUserSchema = CreateUserSchema.partial();
26
+
27
+ // Post related schemas and types
28
+ export const PostSchema = z.object({
29
+ id: z.string(),
30
+ title: z.string(),
31
+ content: z.string(),
32
+ excerpt: z.string().nullable(),
33
+ published: z.boolean(),
34
+ tags: z.array(z.string()),
35
+ authorId: z.string(),
36
+ views: z.number(),
37
+ likes: z.number(),
38
+ createdAt: z.date(),
39
+ updatedAt: z.date(),
40
+ });
41
+
42
+ export const CreatePostSchema = z.object({
43
+ title: z.string().min(1, 'Title is required'),
44
+ content: z.string().min(1, 'Content is required'),
45
+ excerpt: z.string().optional(),
46
+ published: z.boolean().default(false),
47
+ tags: z.array(z.string()).default([]),
48
+ });
49
+
50
+ export const UpdatePostSchema = CreatePostSchema.partial();
51
+
52
+ // Comment related schemas and types
53
+ export const CommentSchema = z.object({
54
+ id: z.string(),
55
+ content: z.string(),
56
+ authorId: z.string(),
57
+ postId: z.string(),
58
+ parentId: z.string().nullable(),
59
+ createdAt: z.date(),
60
+ updatedAt: z.date(),
61
+ });
62
+
63
+ export const CreateCommentSchema = z.object({
64
+ content: z.string().min(1, 'Comment cannot be empty'),
65
+ postId: z.string(),
66
+ parentId: z.string().optional(),
67
+ });
68
+
69
+ // User settings schemas and types
70
+ export const UserSettingsSchema = z.object({
71
+ id: z.string(),
72
+ theme: z.enum(['light', 'dark', 'auto']),
73
+ notifications: z.boolean(),
74
+ emailUpdates: z.boolean(),
75
+ publicProfile: z.boolean(),
76
+ userId: z.string(),
77
+ createdAt: z.date(),
78
+ updatedAt: z.date(),
79
+ });
80
+
81
+ export const UpdateUserSettingsSchema = z.object({
82
+ theme: z.enum(['light', 'dark', 'auto']).optional(),
83
+ notifications: z.boolean().optional(),
84
+ emailUpdates: z.boolean().optional(),
85
+ publicProfile: z.boolean().optional(),
86
+ });
87
+
88
+ // Inferred TypeScript types
89
+ export type User = z.infer<typeof UserSchema>;
90
+ export type CreateUser = z.infer<typeof CreateUserSchema>;
91
+ export type UpdateUser = z.infer<typeof UpdateUserSchema>;
92
+
93
+ export type Post = z.infer<typeof PostSchema>;
94
+ export type CreatePost = z.infer<typeof CreatePostSchema>;
95
+ export type UpdatePost = z.infer<typeof UpdatePostSchema>;
96
+
97
+ export type Comment = z.infer<typeof CommentSchema>;
98
+ export type CreateComment = z.infer<typeof CreateCommentSchema>;
99
+
100
+ export type UserSettings = z.infer<typeof UserSettingsSchema>;
101
+ export type UpdateUserSettings = z.infer<typeof UpdateUserSettingsSchema>;
102
+
103
+ // Extended types with relations for UI components
104
+ export type UserWithPosts = User & {
105
+ posts: Post[];
106
+ settings?: UserSettings;
107
+ };
108
+
109
+ export type PostWithAuthor = Post & {
110
+ author: User;
111
+ comments: CommentWithAuthor[];
112
+ };
113
+
114
+ export type CommentWithAuthor = Comment & {
115
+ author: User;
116
+ children?: CommentWithAuthor[];
117
+ };
118
+
119
+ // API Response types
120
+ export type ApiResponse<T> = {
121
+ data: T;
122
+ success: boolean;
123
+ message?: string;
124
+ };
125
+
126
+ export type PaginatedResponse<T> = ApiResponse<{
127
+ items: T[];
128
+ total: number;
129
+ page: number;
130
+ pageSize: number;
131
+ hasMore: boolean;
132
+ }>;
133
+
134
+ // Common utility types
135
+ export type Theme = 'light' | 'dark' | 'auto';
136
+
137
+ export interface LoadingState {
138
+ isLoading: boolean;
139
+ error?: string;
140
+ }
141
+
142
+ export interface PaginationParams {
143
+ page?: number;
144
+ pageSize?: number;
145
+ sortBy?: string;
146
+ sortOrder?: 'asc' | 'desc';
147
+ search?: string;
148
+ }