create-croissant 0.1.44 → 0.1.46

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 (170) hide show
  1. package/dist/add.js +8 -8
  2. package/dist/index.js +8 -8
  3. package/package.json +10 -10
  4. package/template/.env.example +2 -2
  5. package/template/.github/dependabot.yml +15 -0
  6. package/template/.github/workflows/ci.yml +143 -0
  7. package/template/.github/workflows/dependabot-automerge.yml +23 -0
  8. package/template/.husky/pre-push +0 -0
  9. package/template/README.md +23 -23
  10. package/template/apps/desktop/.eslintcache +1 -0
  11. package/template/apps/desktop/README.md +5 -5
  12. package/template/apps/desktop/package.json +5 -5
  13. package/template/apps/mobile/README.md +3 -3
  14. package/template/apps/mobile/app/(tabs)/_layout.tsx +21 -14
  15. package/template/apps/mobile/app/(tabs)/account.tsx +147 -0
  16. package/template/apps/mobile/app/(tabs)/explore.tsx +335 -104
  17. package/template/apps/mobile/app/(tabs)/index.tsx +94 -96
  18. package/template/apps/mobile/app/_layout.tsx +26 -7
  19. package/template/apps/mobile/app/index.tsx +129 -0
  20. package/template/apps/mobile/app/login.tsx +135 -0
  21. package/template/apps/mobile/app/signup.tsx +144 -0
  22. package/template/apps/mobile/app.json +9 -4
  23. package/template/apps/mobile/components/ui/button.tsx +86 -0
  24. package/template/apps/mobile/components/ui/input.tsx +56 -0
  25. package/template/apps/mobile/lib/auth-client.ts +1 -1
  26. package/template/apps/mobile/lib/orpc.ts +28 -0
  27. package/template/apps/mobile/package.json +14 -1
  28. package/template/apps/mobile/scripts/reset-project.js +0 -0
  29. package/template/apps/mobile/tsconfig.json +4 -1
  30. package/template/apps/platform/.env.example +1 -1
  31. package/template/apps/platform/package.json +10 -6
  32. package/template/apps/platform/portless.json +3 -0
  33. package/template/apps/platform/src/components/login-form.tsx +5 -4
  34. package/template/apps/platform/src/components/signup-form.tsx +12 -16
  35. package/template/apps/platform/src/router.tsx +0 -1
  36. package/template/apps/platform/src/routes/__root.tsx +6 -2
  37. package/template/apps/platform/src/routes/_auth/account.tsx +13 -17
  38. package/template/apps/platform/src/routes/_auth/examples/client-orpc-auth.tsx +2 -6
  39. package/template/apps/platform/src/routes/_public/examples/client-orpc.tsx +16 -29
  40. package/template/apps/platform/src/routes/_public/examples/ssr-orpc.tsx +10 -14
  41. package/template/apps/platform/src/routes/api/auth/$.ts +23 -2
  42. package/template/apps/platform/src/routes/api/rpc.$.ts +18 -0
  43. package/template/package.json +23 -18
  44. package/template/packages/auth/package.json +2 -2
  45. package/template/packages/db/package.json +1 -1
  46. package/template/packages/orpc/package.json +11 -4
  47. package/template/packages/orpc/src/lib/planets.ts +18 -18
  48. package/template/packages/orpc/src/lib/router.ts +3 -3
  49. package/template/packages/orpc/src/react/context.tsx +23 -0
  50. package/template/packages/orpc/src/react/general.ts +29 -0
  51. package/template/packages/orpc/src/react/index.ts +3 -0
  52. package/template/packages/orpc/src/react/planets.ts +90 -0
  53. package/template/packages/ui/package.json +3 -2
  54. package/template/pnpm-workspace.yaml +3 -0
  55. package/template/tsconfig.json +2 -1
  56. package/template/apps/mobile/app/modal.tsx +0 -29
  57. package/template/packages/orpc/node_modules/@types/node/LICENSE +0 -21
  58. package/template/packages/orpc/node_modules/@types/node/README.md +0 -15
  59. package/template/packages/orpc/node_modules/@types/node/assert/strict.d.ts +0 -111
  60. package/template/packages/orpc/node_modules/@types/node/assert.d.ts +0 -1078
  61. package/template/packages/orpc/node_modules/@types/node/async_hooks.d.ts +0 -603
  62. package/template/packages/orpc/node_modules/@types/node/buffer.buffer.d.ts +0 -472
  63. package/template/packages/orpc/node_modules/@types/node/buffer.d.ts +0 -1934
  64. package/template/packages/orpc/node_modules/@types/node/child_process.d.ts +0 -1476
  65. package/template/packages/orpc/node_modules/@types/node/cluster.d.ts +0 -578
  66. package/template/packages/orpc/node_modules/@types/node/compatibility/disposable.d.ts +0 -14
  67. package/template/packages/orpc/node_modules/@types/node/compatibility/index.d.ts +0 -9
  68. package/template/packages/orpc/node_modules/@types/node/compatibility/indexable.d.ts +0 -20
  69. package/template/packages/orpc/node_modules/@types/node/compatibility/iterators.d.ts +0 -20
  70. package/template/packages/orpc/node_modules/@types/node/console.d.ts +0 -452
  71. package/template/packages/orpc/node_modules/@types/node/constants.d.ts +0 -21
  72. package/template/packages/orpc/node_modules/@types/node/crypto.d.ts +0 -4545
  73. package/template/packages/orpc/node_modules/@types/node/dgram.d.ts +0 -600
  74. package/template/packages/orpc/node_modules/@types/node/diagnostics_channel.d.ts +0 -578
  75. package/template/packages/orpc/node_modules/@types/node/dns/promises.d.ts +0 -503
  76. package/template/packages/orpc/node_modules/@types/node/dns.d.ts +0 -923
  77. package/template/packages/orpc/node_modules/@types/node/domain.d.ts +0 -170
  78. package/template/packages/orpc/node_modules/@types/node/events.d.ts +0 -976
  79. package/template/packages/orpc/node_modules/@types/node/fs/promises.d.ts +0 -1295
  80. package/template/packages/orpc/node_modules/@types/node/fs.d.ts +0 -4461
  81. package/template/packages/orpc/node_modules/@types/node/globals.d.ts +0 -172
  82. package/template/packages/orpc/node_modules/@types/node/globals.typedarray.d.ts +0 -38
  83. package/template/packages/orpc/node_modules/@types/node/http.d.ts +0 -2089
  84. package/template/packages/orpc/node_modules/@types/node/http2.d.ts +0 -2644
  85. package/template/packages/orpc/node_modules/@types/node/https.d.ts +0 -579
  86. package/template/packages/orpc/node_modules/@types/node/index.d.ts +0 -97
  87. package/template/packages/orpc/node_modules/@types/node/inspector.d.ts +0 -253
  88. package/template/packages/orpc/node_modules/@types/node/inspector.generated.d.ts +0 -4052
  89. package/template/packages/orpc/node_modules/@types/node/module.d.ts +0 -891
  90. package/template/packages/orpc/node_modules/@types/node/net.d.ts +0 -1076
  91. package/template/packages/orpc/node_modules/@types/node/os.d.ts +0 -506
  92. package/template/packages/orpc/node_modules/@types/node/package.json +0 -145
  93. package/template/packages/orpc/node_modules/@types/node/path.d.ts +0 -200
  94. package/template/packages/orpc/node_modules/@types/node/perf_hooks.d.ts +0 -968
  95. package/template/packages/orpc/node_modules/@types/node/process.d.ts +0 -2084
  96. package/template/packages/orpc/node_modules/@types/node/punycode.d.ts +0 -117
  97. package/template/packages/orpc/node_modules/@types/node/querystring.d.ts +0 -152
  98. package/template/packages/orpc/node_modules/@types/node/readline/promises.d.ts +0 -161
  99. package/template/packages/orpc/node_modules/@types/node/readline.d.ts +0 -594
  100. package/template/packages/orpc/node_modules/@types/node/repl.d.ts +0 -428
  101. package/template/packages/orpc/node_modules/@types/node/sea.d.ts +0 -153
  102. package/template/packages/orpc/node_modules/@types/node/sqlite.d.ts +0 -721
  103. package/template/packages/orpc/node_modules/@types/node/stream/consumers.d.ts +0 -38
  104. package/template/packages/orpc/node_modules/@types/node/stream/promises.d.ts +0 -90
  105. package/template/packages/orpc/node_modules/@types/node/stream/web.d.ts +0 -622
  106. package/template/packages/orpc/node_modules/@types/node/stream.d.ts +0 -1687
  107. package/template/packages/orpc/node_modules/@types/node/string_decoder.d.ts +0 -67
  108. package/template/packages/orpc/node_modules/@types/node/test.d.ts +0 -2163
  109. package/template/packages/orpc/node_modules/@types/node/timers/promises.d.ts +0 -108
  110. package/template/packages/orpc/node_modules/@types/node/timers.d.ts +0 -287
  111. package/template/packages/orpc/node_modules/@types/node/tls.d.ts +0 -1319
  112. package/template/packages/orpc/node_modules/@types/node/trace_events.d.ts +0 -197
  113. package/template/packages/orpc/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +0 -468
  114. package/template/packages/orpc/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +0 -34
  115. package/template/packages/orpc/node_modules/@types/node/ts5.6/index.d.ts +0 -97
  116. package/template/packages/orpc/node_modules/@types/node/tty.d.ts +0 -208
  117. package/template/packages/orpc/node_modules/@types/node/url.d.ts +0 -984
  118. package/template/packages/orpc/node_modules/@types/node/util.d.ts +0 -2606
  119. package/template/packages/orpc/node_modules/@types/node/v8.d.ts +0 -920
  120. package/template/packages/orpc/node_modules/@types/node/vm.d.ts +0 -1000
  121. package/template/packages/orpc/node_modules/@types/node/wasi.d.ts +0 -181
  122. package/template/packages/orpc/node_modules/@types/node/web-globals/abortcontroller.d.ts +0 -34
  123. package/template/packages/orpc/node_modules/@types/node/web-globals/domexception.d.ts +0 -68
  124. package/template/packages/orpc/node_modules/@types/node/web-globals/events.d.ts +0 -97
  125. package/template/packages/orpc/node_modules/@types/node/web-globals/fetch.d.ts +0 -55
  126. package/template/packages/orpc/node_modules/@types/node/web-globals/navigator.d.ts +0 -22
  127. package/template/packages/orpc/node_modules/@types/node/web-globals/storage.d.ts +0 -24
  128. package/template/packages/orpc/node_modules/@types/node/worker_threads.d.ts +0 -784
  129. package/template/packages/orpc/node_modules/@types/node/zlib.d.ts +0 -747
  130. package/template/packages/orpc/node_modules/undici-types/LICENSE +0 -21
  131. package/template/packages/orpc/node_modules/undici-types/README.md +0 -6
  132. package/template/packages/orpc/node_modules/undici-types/agent.d.ts +0 -31
  133. package/template/packages/orpc/node_modules/undici-types/api.d.ts +0 -43
  134. package/template/packages/orpc/node_modules/undici-types/balanced-pool.d.ts +0 -29
  135. package/template/packages/orpc/node_modules/undici-types/cache.d.ts +0 -36
  136. package/template/packages/orpc/node_modules/undici-types/client.d.ts +0 -108
  137. package/template/packages/orpc/node_modules/undici-types/connector.d.ts +0 -34
  138. package/template/packages/orpc/node_modules/undici-types/content-type.d.ts +0 -21
  139. package/template/packages/orpc/node_modules/undici-types/cookies.d.ts +0 -28
  140. package/template/packages/orpc/node_modules/undici-types/diagnostics-channel.d.ts +0 -66
  141. package/template/packages/orpc/node_modules/undici-types/dispatcher.d.ts +0 -256
  142. package/template/packages/orpc/node_modules/undici-types/env-http-proxy-agent.d.ts +0 -21
  143. package/template/packages/orpc/node_modules/undici-types/errors.d.ts +0 -149
  144. package/template/packages/orpc/node_modules/undici-types/eventsource.d.ts +0 -61
  145. package/template/packages/orpc/node_modules/undici-types/fetch.d.ts +0 -209
  146. package/template/packages/orpc/node_modules/undici-types/file.d.ts +0 -39
  147. package/template/packages/orpc/node_modules/undici-types/filereader.d.ts +0 -54
  148. package/template/packages/orpc/node_modules/undici-types/formdata.d.ts +0 -108
  149. package/template/packages/orpc/node_modules/undici-types/global-dispatcher.d.ts +0 -9
  150. package/template/packages/orpc/node_modules/undici-types/global-origin.d.ts +0 -7
  151. package/template/packages/orpc/node_modules/undici-types/handlers.d.ts +0 -15
  152. package/template/packages/orpc/node_modules/undici-types/header.d.ts +0 -4
  153. package/template/packages/orpc/node_modules/undici-types/index.d.ts +0 -71
  154. package/template/packages/orpc/node_modules/undici-types/interceptors.d.ts +0 -17
  155. package/template/packages/orpc/node_modules/undici-types/mock-agent.d.ts +0 -50
  156. package/template/packages/orpc/node_modules/undici-types/mock-client.d.ts +0 -25
  157. package/template/packages/orpc/node_modules/undici-types/mock-errors.d.ts +0 -12
  158. package/template/packages/orpc/node_modules/undici-types/mock-interceptor.d.ts +0 -93
  159. package/template/packages/orpc/node_modules/undici-types/mock-pool.d.ts +0 -25
  160. package/template/packages/orpc/node_modules/undici-types/package.json +0 -55
  161. package/template/packages/orpc/node_modules/undici-types/patch.d.ts +0 -33
  162. package/template/packages/orpc/node_modules/undici-types/pool-stats.d.ts +0 -19
  163. package/template/packages/orpc/node_modules/undici-types/pool.d.ts +0 -39
  164. package/template/packages/orpc/node_modules/undici-types/proxy-agent.d.ts +0 -28
  165. package/template/packages/orpc/node_modules/undici-types/readable.d.ts +0 -65
  166. package/template/packages/orpc/node_modules/undici-types/retry-agent.d.ts +0 -8
  167. package/template/packages/orpc/node_modules/undici-types/retry-handler.d.ts +0 -116
  168. package/template/packages/orpc/node_modules/undici-types/util.d.ts +0 -18
  169. package/template/packages/orpc/node_modules/undici-types/webidl.d.ts +0 -228
  170. package/template/packages/orpc/node_modules/undici-types/websocket.d.ts +0 -150
@@ -0,0 +1,147 @@
1
+ import { useState, useEffect } from "react";
2
+ import { View, Text, StyleSheet, ScrollView, Alert, ActivityIndicator } from "react-native";
3
+ import { useRouter } from "expo-router";
4
+ import { authClient } from "@/lib/auth-client";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Input } from "@/components/ui/input";
7
+
8
+ export default function AccountScreen() {
9
+ const router = useRouter();
10
+ const { data: session, isPending } = authClient.useSession();
11
+ const [updating, setUpdating] = useState(false);
12
+
13
+ // Form states
14
+ const [name, setName] = useState("");
15
+ const [email, setEmail] = useState("");
16
+
17
+ useEffect(() => {
18
+ if (!isPending && !session) {
19
+ router.replace("/login");
20
+ return;
21
+ }
22
+
23
+ if (session) {
24
+ setName(session.user.name);
25
+ setEmail(session.user.email);
26
+ }
27
+ }, [session, isPending, router]);
28
+
29
+ const handleUpdateProfile = async () => {
30
+ if (!name) {
31
+ Alert.alert("Error", "Name is required");
32
+ return;
33
+ }
34
+
35
+ setUpdating(true);
36
+ try {
37
+ const { error } = await authClient.updateUser({
38
+ name,
39
+ });
40
+
41
+ if (error) {
42
+ Alert.alert("Error", error.message || "Failed to update profile");
43
+ } else {
44
+ Alert.alert("Success", "Profile updated successfully");
45
+ }
46
+ } catch (err) {
47
+ Alert.alert("Error", "An unexpected error occurred");
48
+ } finally {
49
+ setUpdating(false);
50
+ }
51
+ };
52
+
53
+ if (isPending) {
54
+ return (
55
+ <View style={styles.center}>
56
+ <ActivityIndicator size="large" color="#000" />
57
+ </View>
58
+ );
59
+ }
60
+
61
+ return (
62
+ <ScrollView style={styles.container} contentContainerStyle={styles.content}>
63
+ <Text style={styles.title}>Account Settings</Text>
64
+
65
+ <View style={styles.section}>
66
+ <Text style={styles.sectionTitle}>Profile Information</Text>
67
+ <View style={styles.form}>
68
+ <Input
69
+ label="Name"
70
+ value={name}
71
+ onChangeText={setName}
72
+ placeholder="Your name"
73
+ />
74
+ <Input
75
+ label="Email"
76
+ value={email}
77
+ editable={false}
78
+ style={styles.disabledInput}
79
+ />
80
+ <Button
81
+ onPress={handleUpdateProfile}
82
+ loading={updating}
83
+ style={styles.button}
84
+ >
85
+ Update Profile
86
+ </Button>
87
+ </View>
88
+ </View>
89
+
90
+ <View style={styles.section}>
91
+ <Text style={styles.sectionTitle}>Security</Text>
92
+ <Text style={styles.infoText}>
93
+ Password management and other security settings are currently available on the web platform.
94
+ </Text>
95
+ </View>
96
+ </ScrollView>
97
+ );
98
+ }
99
+
100
+ const styles = StyleSheet.create({
101
+ container: {
102
+ flex: 1,
103
+ backgroundColor: "#fff",
104
+ },
105
+ content: {
106
+ padding: 24,
107
+ },
108
+ center: {
109
+ flex: 1,
110
+ justifyContent: "center",
111
+ alignItems: "center",
112
+ },
113
+ title: {
114
+ fontSize: 28,
115
+ fontWeight: "bold",
116
+ marginBottom: 24,
117
+ },
118
+ section: {
119
+ marginBottom: 32,
120
+ },
121
+ sectionTitle: {
122
+ fontSize: 18,
123
+ fontWeight: "600",
124
+ marginBottom: 16,
125
+ color: "#333",
126
+ },
127
+ form: {
128
+ gap: 16,
129
+ },
130
+ disabledInput: {
131
+ backgroundColor: "#f5f5f5",
132
+ color: "#888",
133
+ },
134
+ button: {
135
+ marginTop: 8,
136
+ },
137
+ infoText: {
138
+ fontSize: 14,
139
+ color: "#666",
140
+ lineHeight: 20,
141
+ backgroundColor: "#f9f9f9",
142
+ padding: 16,
143
+ borderRadius: 8,
144
+ borderWidth: 1,
145
+ borderColor: "#eee",
146
+ },
147
+ });
@@ -1,114 +1,345 @@
1
- import { Image } from "expo-image";
2
- import { Platform, StyleSheet } from "react-native";
3
-
4
- import { Collapsible } from "@/components/ui/collapsible";
5
- import { ExternalLink } from "@/components/external-link";
6
- import ParallaxScrollView from "@/components/parallax-scroll-view";
7
- import { ThemedText } from "@/components/themed-text";
8
- import { ThemedView } from "@/components/themed-view";
9
- import { IconSymbol } from "@/components/ui/icon-symbol";
10
- import { Fonts } from "@/constants/theme";
11
-
12
- export default function TabTwoScreen() {
13
- return (
14
- <ParallaxScrollView
15
- headerBackgroundColor={{ light: "#D0D0D0", dark: "#353636" }}
16
- headerImage={
17
- <IconSymbol
18
- size={310}
19
- color="#808080"
20
- name="chevron.left.forwardslash.chevron.right"
21
- style={styles.headerImage}
22
- />
1
+ import { useState } from "react";
2
+ import {
3
+ Alert,
4
+ FlatList,
5
+ Modal,
6
+ StyleSheet,
7
+ Text,
8
+ View,
9
+ ActivityIndicator,
10
+ ScrollView,
11
+ } from "react-native";
12
+ import { useQueryClient } from "@tanstack/react-query";
13
+ import { useForm } from "@tanstack/react-form";
14
+ import { z } from "zod";
15
+ import { Button } from "@/components/ui/button";
16
+ import { Input } from "@/components/ui/input";
17
+ import { usePlanets, useCreatePlanet, useUpdatePlanet, useDeletePlanet } from "@workspace/orpc/react";
18
+
19
+ const planetSchema = z.object({
20
+ name: z.string().min(1, "Name is required"),
21
+ description: z.string(),
22
+ distance: z.string().refine((val) => !isNaN(parseFloat(val)), {
23
+ message: "Must be a number",
24
+ }),
25
+ diameter: z.string().refine((val) => !isNaN(parseFloat(val)), {
26
+ message: "Must be a number",
27
+ }),
28
+ });
29
+
30
+ export default function ExploreScreen() {
31
+ const [modalVisible, setModalVisible] = useState(false);
32
+ const [editingId, setEditingId] = useState<number | null>(null);
33
+
34
+ const { data: planets = [], isLoading } = usePlanets();
35
+
36
+ const form = useForm({
37
+ defaultValues: {
38
+ name: "",
39
+ description: "",
40
+ distance: "0",
41
+ diameter: "0",
42
+ },
43
+ validators: {
44
+ onChange: planetSchema,
45
+ },
46
+ onSubmit: async ({ value }) => {
47
+ const payload = {
48
+ name: value.name,
49
+ description: value.description || undefined,
50
+ distanceFromSun: parseFloat(value.distance) || 0,
51
+ diameter: parseFloat(value.diameter) || 0,
52
+ hasRings: false,
53
+ };
54
+ console.log('connard')
55
+
56
+ try {
57
+ if (editingId) {
58
+ await updateMutation.mutateAsync({ id: editingId, ...payload });
59
+ } else {
60
+ await createMutation.mutateAsync(payload);
61
+ }
62
+ closeModal();
63
+ } catch (err) {
64
+ // Error handled in mutation callbacks
23
65
  }
24
- >
25
- <ThemedView style={styles.titleContainer}>
26
- <ThemedText
27
- type="title"
28
- style={{
29
- fontFamily: Fonts.rounded,
30
- }}
31
- >
32
- Explore
33
- </ThemedText>
34
- </ThemedView>
35
- <ThemedText>This app includes example code to help you get started.</ThemedText>
36
- <Collapsible title="File-based routing">
37
- <ThemedText>
38
- This app has two screens:{" "}
39
- <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{" "}
40
- <ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
41
- </ThemedText>
42
- <ThemedText>
43
- The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{" "}
44
- sets up the tab navigator.
45
- </ThemedText>
46
- <ExternalLink href="https://docs.expo.dev/router/introduction">
47
- <ThemedText type="link">Learn more</ThemedText>
48
- </ExternalLink>
49
- </Collapsible>
50
- <Collapsible title="Android, iOS, and web support">
51
- <ThemedText>
52
- You can open this project on Android, iOS, and the web. To open the web version, press{" "}
53
- <ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
54
- </ThemedText>
55
- </Collapsible>
56
- <Collapsible title="Images">
57
- <ThemedText>
58
- For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{" "}
59
- <ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
60
- different screen densities
61
- </ThemedText>
62
- <Image
63
- source={require("@/assets/images/react-logo.png")}
64
- style={{ width: 100, height: 100, alignSelf: "center" }}
65
- />
66
- <ExternalLink href="https://reactnative.dev/docs/images">
67
- <ThemedText type="link">Learn more</ThemedText>
68
- </ExternalLink>
69
- </Collapsible>
70
- <Collapsible title="Light and dark mode components">
71
- <ThemedText>
72
- This template has light and dark mode support. The{" "}
73
- <ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
74
- what the user&apos;s current color scheme is, and so you can adjust UI colors accordingly.
75
- </ThemedText>
76
- <ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
77
- <ThemedText type="link">Learn more</ThemedText>
78
- </ExternalLink>
79
- </Collapsible>
80
- <Collapsible title="Animations">
81
- <ThemedText>
82
- This template includes an example of an animated component. The{" "}
83
- <ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
84
- the powerful{" "}
85
- <ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
86
- react-native-reanimated
87
- </ThemedText>{" "}
88
- library to create a waving hand animation.
89
- </ThemedText>
90
- {Platform.select({
91
- ios: (
92
- <ThemedText>
93
- The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{" "}
94
- component provides a parallax effect for the header image.
95
- </ThemedText>
96
- ),
97
- })}
98
- </Collapsible>
99
- </ParallaxScrollView>
66
+ },
67
+ });
68
+
69
+ const resetForm = () => {
70
+ form.reset();
71
+ setEditingId(null);
72
+ };
73
+
74
+ const createMutation = useCreatePlanet({
75
+ onSuccess: () => {
76
+ Alert.alert("Success", "Planet added successfully");
77
+ },
78
+ onError: (err) => {
79
+ Alert.alert("Error", err.message || "Failed to add planet");
80
+ },
81
+ });
82
+
83
+ const updateMutation = useUpdatePlanet({
84
+ onSuccess: () => {
85
+ Alert.alert("Success", "Planet updated successfully");
86
+ },
87
+ onError: (err) => {
88
+ Alert.alert("Error", err.message || "Failed to update planet");
89
+ },
90
+ });
91
+
92
+ const deleteMutation = useDeletePlanet({
93
+ onSuccess: () => {
94
+ Alert.alert("Success", "Planet deleted successfully");
95
+ },
96
+ onError: (err) => {
97
+ Alert.alert("Error", err.message || "Failed to delete planet");
98
+ },
99
+ });
100
+
101
+ const handleEdit = (planet: any) => {
102
+ setEditingId(planet.id);
103
+ form.setFieldValue("name", planet.name);
104
+ form.setFieldValue("description", planet.description || "");
105
+ form.setFieldValue("distance", planet.distanceFromSun.toString());
106
+ form.setFieldValue("diameter", planet.diameter.toString());
107
+ setModalVisible(true);
108
+ };
109
+
110
+ const handleDelete = (id: number) => {
111
+ deleteMutation.mutateAsync({id})
112
+ };
113
+
114
+ const closeModal = () => {
115
+ setModalVisible(false);
116
+ resetForm();
117
+ };
118
+
119
+ if (isLoading) {
120
+ return (
121
+ <View style={styles.center}>
122
+ <ActivityIndicator size="large" color="#000" />
123
+ </View>
124
+ );
125
+ }
126
+
127
+ return (
128
+ <View style={styles.container}>
129
+ <FlatList
130
+ data={planets}
131
+ keyExtractor={(item) => item.id.toString()}
132
+ contentContainerStyle={styles.listContent}
133
+ renderItem={({ item }) => (
134
+ <View style={styles.planetCard}>
135
+ <View style={styles.planetInfo}>
136
+ <Text style={styles.planetName}>{item.name}</Text>
137
+ <Text style={styles.planetDesc}>{item.description}</Text>
138
+ <Text style={styles.planetDetails}>
139
+ Distance: {item.distanceFromSun} AU • Diameter: {item.diameter} km
140
+ </Text>
141
+ </View>
142
+ <View style={styles.actions}>
143
+ <Button
144
+ variant="outline"
145
+ onPress={() => handleEdit(item)}
146
+ style={styles.actionBtn}
147
+ >
148
+ Edit
149
+ </Button>
150
+ <Button
151
+ variant="destructive"
152
+ onPress={() => handleDelete(item.id)}
153
+ style={styles.actionBtn}
154
+ >
155
+ Delete
156
+ </Button>
157
+ </View>
158
+ </View>
159
+ )}
160
+ ListEmptyComponent={
161
+ <Text style={styles.emptyText}>No planets found. Add one!</Text>
162
+ }
163
+ />
164
+
165
+ <Button
166
+ onPress={() => setModalVisible(true)}
167
+ style={styles.fab}
168
+ >
169
+ Add Planet
170
+ </Button>
171
+
172
+ <Modal
173
+ visible={modalVisible}
174
+ animationType="slide"
175
+ onRequestClose={closeModal}
176
+ >
177
+ <View style={styles.modalContainer}>
178
+ <ScrollView contentContainerStyle={styles.modalContent}>
179
+ <Text style={styles.modalTitle}>
180
+ {editingId ? "Edit Planet" : "Add New Planet"}
181
+ </Text>
182
+
183
+ <form.Field name="name">
184
+ {(field: any) => (
185
+ <Input
186
+ label="Name"
187
+ value={field.state.value}
188
+ onChangeText={field.handleChange}
189
+ placeholder="Earth"
190
+ error={field.state.meta.errors?.[0]?.toString()}
191
+ />
192
+ )}
193
+ </form.Field>
194
+
195
+ <form.Field name="description">
196
+ {(field: any) => (
197
+ <Input
198
+ label="Description"
199
+ value={field.state.value}
200
+ onChangeText={field.handleChange}
201
+ placeholder="The blue planet"
202
+ />
203
+ )}
204
+ </form.Field>
205
+
206
+ <form.Field name="distance">
207
+ {(field: any) => (
208
+ <Input
209
+ label="Distance from Sun (AU)"
210
+ value={field.state.value}
211
+ onChangeText={field.handleChange}
212
+ keyboardType="numeric"
213
+ error={field.state.meta.errors?.[0]?.toString()}
214
+ />
215
+ )}
216
+ </form.Field>
217
+
218
+ <form.Field name="diameter">
219
+ {(field: any) => (
220
+ <Input
221
+ label="Diameter (km)"
222
+ value={field.state.value}
223
+ onChangeText={field.handleChange}
224
+ keyboardType="numeric"
225
+ error={field.state.meta.errors?.[0]?.toString()}
226
+ />
227
+ )}
228
+ </form.Field>
229
+
230
+ <View style={styles.modalActions}>
231
+ <Button
232
+ variant="outline"
233
+ onPress={closeModal}
234
+ style={styles.modalBtn}
235
+ >
236
+ Cancel
237
+ </Button>
238
+ <Button
239
+ onPress={form.handleSubmit}
240
+ loading={createMutation.isPending || updateMutation.isPending}
241
+ style={styles.modalBtn}
242
+ >
243
+ {editingId ? "Update" : "Create"}
244
+ </Button>
245
+ </View>
246
+ </ScrollView>
247
+ </View>
248
+ </Modal>
249
+ </View>
100
250
  );
101
251
  }
102
252
 
103
253
  const styles = StyleSheet.create({
104
- headerImage: {
105
- color: "#808080",
106
- bottom: -90,
107
- left: -35,
108
- position: "absolute",
254
+ container: {
255
+ flex: 1,
256
+ backgroundColor: "#fff",
257
+ },
258
+ center: {
259
+ flex: 1,
260
+ justifyContent: "center",
261
+ alignItems: "center",
262
+ },
263
+ listContent: {
264
+ padding: 16,
265
+ paddingBottom: 100,
266
+ },
267
+ planetCard: {
268
+ padding: 16,
269
+ borderRadius: 12,
270
+ borderWidth: 1,
271
+ borderColor: "#eee",
272
+ marginBottom: 16,
273
+ backgroundColor: "#fff",
274
+ shadowColor: "#000",
275
+ shadowOffset: { width: 0, height: 2 },
276
+ shadowOpacity: 0.05,
277
+ shadowRadius: 4,
278
+ elevation: 2,
279
+ },
280
+ planetInfo: {
281
+ marginBottom: 16,
282
+ },
283
+ planetName: {
284
+ fontSize: 18,
285
+ fontWeight: "bold",
286
+ marginBottom: 4,
287
+ },
288
+ planetDesc: {
289
+ fontSize: 14,
290
+ color: "#666",
291
+ marginBottom: 8,
292
+ },
293
+ planetDetails: {
294
+ fontSize: 12,
295
+ color: "#999",
109
296
  },
110
- titleContainer: {
297
+ actions: {
111
298
  flexDirection: "row",
112
299
  gap: 8,
113
300
  },
301
+ actionBtn: {
302
+ flex: 1,
303
+ height: 36,
304
+ },
305
+ emptyText: {
306
+ textAlign: "center",
307
+ marginTop: 40,
308
+ color: "#999",
309
+ fontStyle: "italic",
310
+ },
311
+ fab: {
312
+ position: "absolute",
313
+ bottom: 24,
314
+ left: 24,
315
+ right: 24,
316
+ height: 56,
317
+ borderRadius: 28,
318
+ shadowColor: "#000",
319
+ shadowOffset: { width: 0, height: 4 },
320
+ shadowOpacity: 0.2,
321
+ shadowRadius: 8,
322
+ elevation: 5,
323
+ },
324
+ modalContainer: {
325
+ flex: 1,
326
+ backgroundColor: "#fff",
327
+ },
328
+ modalContent: {
329
+ padding: 24,
330
+ paddingTop: 60,
331
+ },
332
+ modalTitle: {
333
+ fontSize: 24,
334
+ fontWeight: "bold",
335
+ marginBottom: 32,
336
+ },
337
+ modalActions: {
338
+ flexDirection: "row",
339
+ gap: 12,
340
+ marginTop: 32,
341
+ },
342
+ modalBtn: {
343
+ flex: 1,
344
+ },
114
345
  });