@@ -24,19 +24,61 @@ struct PeopleListScreen: View {
24
24
25
25
var body : some View {
26
26
NavigationStack ( path: $path) {
27
- VStack {
27
+ ZStack {
28
+ // Background color to match details screen
29
+ Color ( . systemGroupedBackground)
30
+ . edgesIgnoringSafeArea ( . all)
31
+
28
32
Observing ( viewModel. uiState) { playerListUIState in
29
33
switch onEnum ( of: playerListUIState) {
30
34
case . loading:
31
- ProgressView ( )
32
- . progressViewStyle ( CircularProgressViewStyle ( ) )
35
+ VStack ( spacing: 16 ) {
36
+ ProgressView ( )
37
+ . progressViewStyle ( CircularProgressViewStyle ( ) )
38
+ Text ( " Loading astronauts... " )
39
+ . font ( . headline)
40
+ . foregroundColor ( . secondary)
41
+ }
33
42
case . error( let error) :
34
- Text ( " Error: \( error) " )
43
+ VStack ( spacing: 16 ) {
44
+ Image ( systemName: " exclamationmark.triangle " )
45
+ . font ( . largeTitle)
46
+ . foregroundColor ( . orange)
47
+ Text ( " Error: \( error) " )
48
+ . font ( . headline)
49
+ . foregroundColor ( . primary)
50
+ . multilineTextAlignment ( . center)
51
+
52
+ Button ( action: {
53
+ // Refresh action
54
+ viewModel = PersonListViewModel ( )
55
+ } ) {
56
+ Label ( " Try Again " , systemImage: " arrow.clockwise " )
57
+ . padding ( )
58
+ . background ( Color . blue)
59
+ . foregroundColor ( . white)
60
+ . cornerRadius ( 8 )
61
+ }
62
+ }
63
+ . padding ( )
35
64
case . success( let success) :
36
- List ( success. result, id: \. name) { person in
37
- NavigationLink ( value: person) {
38
- PersonView ( person: person)
65
+ ScrollView {
66
+ LazyVStack ( spacing: 12 ) {
67
+ ForEach ( success. result, id: \. name) { person in
68
+ NavigationLink ( value: person) {
69
+ PersonView ( person: person)
70
+ . padding ( . horizontal)
71
+ . background (
72
+ RoundedRectangle ( cornerRadius: 12 )
73
+ . fill ( Color ( . systemBackground) )
74
+ . shadow ( color: Color . black. opacity ( 0.05 ) , radius: 5 , x: 0 , y: 2 )
75
+ )
76
+ . padding ( . horizontal)
77
+ }
78
+ . buttonStyle ( PlainButtonStyle ( ) )
79
+ }
39
80
}
81
+ . padding ( . vertical)
40
82
}
41
83
}
42
84
}
@@ -45,7 +87,17 @@ struct PeopleListScreen: View {
45
87
PersonDetailsScreen ( person: person)
46
88
}
47
89
. navigationBarTitle ( Text ( " People In Space " ) )
48
- . navigationBarTitleDisplayMode ( . inline)
90
+ . navigationBarTitleDisplayMode ( . large)
91
+ . toolbar {
92
+ ToolbarItem ( placement: . navigationBarTrailing) {
93
+ Button ( action: {
94
+ // Refresh action
95
+ viewModel = PersonListViewModel ( )
96
+ } ) {
97
+ Image ( systemName: " arrow.clockwise " )
98
+ }
99
+ }
100
+ }
49
101
}
50
102
}
51
103
}
@@ -54,21 +106,52 @@ struct PersonView: View {
54
106
let person : Assignment
55
107
56
108
var body : some View {
57
- HStack {
109
+ HStack ( spacing: 16 ) {
110
+ // Person image with improved styling
58
111
AsyncImage ( url: URL ( string: person. personImageUrl ?? " " ) ) { image in
59
- image. resizable ( )
60
- . aspectRatio ( contentMode: . fit)
112
+ image. resizable ( )
113
+ . aspectRatio ( contentMode: . fill)
114
+ . clipShape ( Circle ( ) )
115
+ . overlay ( Circle ( ) . stroke ( Color . gray. opacity ( 0.2 ) , lineWidth: 1 ) )
61
116
} placeholder: {
62
- ProgressView ( )
117
+ Circle ( )
118
+ . fill ( Color . gray. opacity ( 0.2 ) )
119
+ . overlay (
120
+ ProgressView ( )
121
+ . progressViewStyle ( CircularProgressViewStyle ( tint: . gray) )
122
+ )
63
123
}
64
- . frame ( width: 64 , height: 64 )
65
-
124
+ . frame ( width: 70 , height: 70 )
125
+ . shadow ( color : . gray . opacity ( 0.3 ) , radius : 3 , x : 0 , y : 1 )
66
126
67
- VStack ( alignment: . leading) {
68
- Text ( person. name) . font ( . headline)
69
- Text ( person. craft) . font ( . subheadline)
127
+ // Person information with improved typography and layout
128
+ VStack ( alignment: . leading, spacing: 6 ) {
129
+ Text ( person. name)
130
+ . font ( . headline)
131
+ . foregroundColor ( . primary)
132
+
133
+ Text ( person. craft)
134
+ . font ( . subheadline)
135
+ . foregroundColor ( . secondary)
136
+
137
+ // Add a subtle bio preview if available
138
+ if let bio = person. personBio, !bio. isEmpty {
139
+ Text ( bio. prefix ( 50 ) + ( bio. count > 50 ? " ... " : " " ) )
140
+ . font ( . caption)
141
+ . foregroundColor ( . secondary)
142
+ . lineLimit ( 1 )
143
+ }
70
144
}
145
+
146
+ Spacer ( )
147
+
148
+ // Add a chevron to indicate navigation
149
+ Image ( systemName: " chevron.right " )
150
+ . font ( . caption)
151
+ . foregroundColor ( . gray)
71
152
}
153
+ . padding ( . vertical, 8 )
154
+ . contentShape ( Rectangle ( ) )
72
155
}
73
156
}
74
157
@@ -78,22 +161,131 @@ struct PersonDetailsScreen: View {
78
161
79
162
var body : some View {
80
163
ScrollView {
81
- VStack ( alignment: . center, spacing: 32 ) {
82
- Text ( person. name) . font ( . title)
164
+ VStack ( alignment: . center, spacing: 0 ) {
165
+ // Header with astronaut name and craft
166
+ VStack ( spacing: 8 ) {
167
+ Text ( person. name)
168
+ . font ( . largeTitle)
169
+ . fontWeight ( . bold)
170
+ . foregroundColor ( . primary)
171
+ . multilineTextAlignment ( . center)
172
+
173
+ Text ( " Currently on \( person. craft) " )
174
+ . font ( . headline)
175
+ . foregroundColor ( . secondary)
176
+ . padding ( . bottom, 16 )
177
+ }
178
+ . padding ( . top, 24 )
179
+ . padding ( . horizontal)
83
180
84
- AsyncImage ( url: URL ( string: person. personImageUrl ?? " " ) ) { image in
85
- image. resizable ( )
86
- . aspectRatio ( contentMode: . fit)
87
- } placeholder: {
88
- ProgressView ( )
181
+ // Astronaut image with enhanced styling
182
+ ZStack {
183
+ RoundedRectangle ( cornerRadius: 16 )
184
+ . fill ( Color . gray. opacity ( 0.1 ) )
185
+ . frame ( height: 300 )
186
+
187
+ AsyncImage ( url: URL ( string: person. personImageUrl ?? " " ) ) { image in
188
+ image. resizable ( )
189
+ . aspectRatio ( contentMode: . fit)
190
+ . frame ( height: 300 )
191
+ . clipShape ( RoundedRectangle ( cornerRadius: 16 ) )
192
+ } placeholder: {
193
+ VStack {
194
+ ProgressView ( )
195
+ . progressViewStyle ( CircularProgressViewStyle ( ) )
196
+ Text ( " Loading image... " )
197
+ . font ( . caption)
198
+ . foregroundColor ( . secondary)
199
+ . padding ( . top, 8 )
200
+ }
201
+ }
202
+ . frame ( height: 300 )
89
203
}
90
- . frame ( width: 240 , height: 240 )
91
-
92
- Text ( person. personBio ?? " " ) . font ( . body)
93
- Spacer ( )
204
+ . padding ( . horizontal)
205
+ . padding ( . bottom, 24 )
206
+
207
+ // Bio section with card styling
208
+ if let bio = person. personBio, !bio. isEmpty {
209
+ VStack ( alignment: . leading, spacing: 16 ) {
210
+ Text ( " Biography " )
211
+ . font ( . title2)
212
+ . fontWeight ( . bold)
213
+ . foregroundColor ( . primary)
214
+
215
+ Text ( bio)
216
+ . font ( . body)
217
+ . foregroundColor ( . primary)
218
+ . fixedSize ( horizontal: false , vertical: true )
219
+ . lineSpacing ( 6 )
220
+ }
221
+ . padding ( 24 )
222
+ . background (
223
+ RoundedRectangle ( cornerRadius: 16 )
224
+ . fill ( Color ( . systemBackground) )
225
+ . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 10 , x: 0 , y: 5 )
226
+ )
227
+ . padding ( . horizontal)
228
+ } else {
229
+ VStack ( alignment: . center, spacing: 8 ) {
230
+ Image ( systemName: " info.circle " )
231
+ . font ( . largeTitle)
232
+ . foregroundColor ( . secondary)
233
+ Text ( " No biography available " )
234
+ . font ( . headline)
235
+ . foregroundColor ( . secondary)
236
+ }
237
+ . frame ( maxWidth: . infinity)
238
+ . padding ( 24 )
239
+ . background (
240
+ RoundedRectangle ( cornerRadius: 16 )
241
+ . fill ( Color . gray. opacity ( 0.1 ) )
242
+ )
243
+ . padding ( . horizontal)
244
+ }
245
+
246
+ // Additional information section
247
+ VStack ( alignment: . leading, spacing: 16 ) {
248
+ Text ( " Additional Information " )
249
+ . font ( . title2)
250
+ . fontWeight ( . bold)
251
+ . foregroundColor ( . primary)
252
+
253
+ HStack {
254
+ VStack ( alignment: . leading, spacing: 8 ) {
255
+ Label ( " Spacecraft " , systemImage: " airplane.circle.fill " )
256
+ . font ( . headline)
257
+ Text ( person. craft)
258
+ . font ( . body)
259
+ . foregroundColor ( . secondary)
260
+ }
261
+ . frame ( maxWidth: . infinity, alignment: . leading)
262
+
263
+ Divider ( )
264
+ . frame ( height: 40 )
265
+
266
+ VStack ( alignment: . leading, spacing: 8 ) {
267
+ Label ( " Mission " , systemImage: " star.circle.fill " )
268
+ . font ( . headline)
269
+ Text ( " Active " )
270
+ . font ( . body)
271
+ . foregroundColor ( . green)
272
+ }
273
+ . frame ( maxWidth: . infinity, alignment: . leading)
274
+ }
275
+ }
276
+ . padding ( 24 )
277
+ . background (
278
+ RoundedRectangle ( cornerRadius: 16 )
279
+ . fill ( Color ( . systemBackground) )
280
+ . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 10 , x: 0 , y: 5 )
281
+ )
282
+ . padding ( . horizontal)
283
+ . padding ( . top, 16 )
284
+ . padding ( . bottom, 32 )
94
285
}
95
- . padding ( )
96
286
}
287
+ . background ( Color ( . systemGroupedBackground) . edgesIgnoringSafeArea ( . all) )
288
+ . navigationBarTitleDisplayMode ( . inline)
97
289
}
98
290
}
99
291
0 commit comments