jac-client 0.2.8__py3-none-any.whl → 0.2.11__py3-none-any.whl
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.
- jac_client/examples/all-in-one/button.jac +4 -3
- jac_client/examples/all-in-one/components/CategoryFilter.jac +36 -24
- jac_client/examples/all-in-one/components/Header.jac +12 -8
- jac_client/examples/all-in-one/components/ProfitOverview.jac +49 -35
- jac_client/examples/all-in-one/components/Summary.jac +59 -36
- jac_client/examples/all-in-one/components/TransactionForm.jac +142 -112
- jac_client/examples/all-in-one/components/TransactionItem.jac +37 -30
- jac_client/examples/all-in-one/components/TransactionList.jac +33 -26
- jac_client/examples/all-in-one/components/button.jac +4 -3
- jac_client/examples/all-in-one/components/navigation.jac +111 -117
- jac_client/examples/all-in-one/constants/categories.jac +23 -24
- jac_client/examples/all-in-one/constants/clients.jac +7 -8
- jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
- jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
- jac_client/examples/all-in-one/main.jac +542 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +26 -12
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +43 -12
- jac_client/examples/all-in-one/pages/LandingPage.jac +113 -90
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +114 -119
- jac_client/examples/all-in-one/pages/nestedDemo.jac +44 -51
- jac_client/examples/all-in-one/pages/notFound.jac +15 -21
- jac_client/examples/all-in-one/pages/signupPage.jac +113 -119
- jac_client/examples/all-in-one/utils/formatters.jac +5 -8
- jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
- jac_client/examples/asset-serving/image-asset/main.jac +56 -0
- jac_client/examples/asset-serving/import-alias/main.jac +109 -0
- jac_client/examples/basic/main.jac +23 -0
- jac_client/examples/basic-auth/main.jac +363 -0
- jac_client/examples/basic-auth-with-router/main.jac +451 -0
- jac_client/examples/basic-full-stack/main.jac +362 -0
- jac_client/examples/css-styling/js-styling/main.jac +63 -0
- jac_client/examples/css-styling/material-ui/main.jac +122 -0
- jac_client/examples/css-styling/pure-css/main.jac +55 -0
- jac_client/examples/css-styling/sass-example/main.jac +55 -0
- jac_client/examples/css-styling/styled-components/main.jac +62 -0
- jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
- jac_client/examples/full-stack-with-auth/main.jac +696 -0
- jac_client/examples/little-x/main.jac +681 -0
- jac_client/examples/little-x/src/submit-button.jac +15 -14
- jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
- jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
- jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
- jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
- jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
- jac_client/examples/ts-support/main.jac +35 -0
- jac_client/examples/with-router/main.jac +286 -0
- jac_client/plugin/cli.jac +491 -411
- jac_client/plugin/client.jac +25 -0
- jac_client/plugin/client_runtime.cl.jac +10 -4
- jac_client/plugin/impl/client.impl.jac +96 -55
- jac_client/plugin/impl/client_runtime.impl.jac +155 -1
- jac_client/plugin/plugin_config.jac +211 -29
- jac_client/plugin/src/__init__.jac +0 -2
- jac_client/plugin/src/compiler.jac +0 -1
- jac_client/plugin/src/config_loader.jac +1 -0
- jac_client/plugin/src/desktop_config.jac +31 -0
- jac_client/plugin/src/impl/compiler.impl.jac +49 -17
- jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
- jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
- jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
- jac_client/plugin/src/impl/vite_bundler.impl.jac +191 -64
- jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
- jac_client/plugin/src/targets/desktop_target.jac +37 -0
- jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
- jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
- jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
- jac_client/plugin/src/targets/register.jac +21 -0
- jac_client/plugin/src/targets/registry.jac +87 -0
- jac_client/plugin/src/targets/web_target.jac +35 -0
- jac_client/plugin/src/vite_bundler.jac +6 -0
- jac_client/plugin/utils/__init__.jac +3 -0
- jac_client/plugin/utils/bun_installer.jac +16 -0
- jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
- jac_client/templates/client.jacpack +72 -0
- jac_client/templates/fullstack.jacpack +61 -0
- jac_client/tests/conftest.py +103 -47
- jac_client/tests/fixtures/spawn_test/app.jac +49 -52
- jac_client/tests/fixtures/with-ts/app.jac +27 -27
- jac_client/tests/test_cli.py +182 -71
- jac_client/tests/test_e2e.py +232 -0
- jac_client/tests/test_helpers.py +58 -0
- jac_client/tests/test_it.py +91 -135
- jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/METADATA +6 -6
- jac_client-0.2.11.dist-info/RECORD +113 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
- jac_client/examples/all-in-one/app.jac +0 -573
- jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +0 -70
- jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +0 -552
- jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
- jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
- jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
- jac_client/examples/basic/src/app.jac +0 -21
- jac_client/examples/basic-auth/src/app.jac +0 -371
- jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
- jac_client/examples/basic-full-stack/src/app.jac +0 -359
- jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
- jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
- jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
- jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
- jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
- jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
- jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
- jac_client/examples/little-x/src/app.jac +0 -719
- jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
- jac_client/examples/ts-support/src/app.jac +0 -35
- jac_client/examples/with-router/src/app.jac +0 -323
- jac_client/plugin/src/babel_processor.jac +0 -18
- jac_client/plugin/src/impl/babel_processor.impl.jac +0 -89
- jac_client-0.2.8.dist-info/RECORD +0 -97
- {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
|
@@ -1,719 +0,0 @@
|
|
|
1
|
-
import datetime;
|
|
2
|
-
cl import from lodash { * as _ }
|
|
3
|
-
cl import from antd { Button, Input, Card, Typography, Space }
|
|
4
|
-
cl import from pluralize { default as pluralize }
|
|
5
|
-
cl import from 'react-animated-components' { Rotate }
|
|
6
|
-
|
|
7
|
-
node Profile {
|
|
8
|
-
has username: str = "";
|
|
9
|
-
|
|
10
|
-
can update with update_profile entry {
|
|
11
|
-
self.username = visitor.new_username;
|
|
12
|
-
report self ;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
can get with get_profile entry {
|
|
16
|
-
follwers = [
|
|
17
|
-
{"id": jid(i), "username": i.username} for i in [self-->(`?Profile)]
|
|
18
|
-
];
|
|
19
|
-
report {"user": self, "followers": follwers} ;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
can follow with follow_request entry {
|
|
23
|
-
current_profile = [root-->(`?Profile)];
|
|
24
|
-
current_profile[0] +>: Follow() :+> self;
|
|
25
|
-
report self ;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
can un_follow with un_follow_request entry {
|
|
29
|
-
current_profile = [root-->(`?Profile)];
|
|
30
|
-
follow_edge = [edge current_profile[0]->:Follow:->self];
|
|
31
|
-
del follow_edge[0] ;
|
|
32
|
-
report self ;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
obj TweetInfo {
|
|
37
|
-
has username: str,
|
|
38
|
-
id: str,
|
|
39
|
-
content: str,
|
|
40
|
-
embedding: list,
|
|
41
|
-
likes: list,
|
|
42
|
-
comments: list;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
node Tweet {
|
|
46
|
-
has content: str,
|
|
47
|
-
embedding: list,
|
|
48
|
-
created_at: str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S");
|
|
49
|
-
|
|
50
|
-
can update with update_tweet exit {
|
|
51
|
-
self.content = visitor.updated_content;
|
|
52
|
-
report self ;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
can delete with remove_tweet exit {
|
|
56
|
-
del self ;
|
|
57
|
-
disengage;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
can like_tweet with like_tweet entry {
|
|
61
|
-
current_profile = [root-->(`?Profile)];
|
|
62
|
-
self +>: Like() :+> current_profile[0];
|
|
63
|
-
report self ;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
can remove_like with remove_like entry {
|
|
67
|
-
current_profile = [root-->(`?Profile)];
|
|
68
|
-
like_edge = [edge self->:Like:->current_profile[0]];
|
|
69
|
-
del like_edge[0] ;
|
|
70
|
-
report self ;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
can comment with comment_tweet entry {
|
|
74
|
-
current_profile = [root-->(`?Profile)];
|
|
75
|
-
comment_node = current_profile[0] +>: Post() :+> Comment(
|
|
76
|
-
content=visitor.content
|
|
77
|
-
);
|
|
78
|
-
grant(comment_node[0], level=ConnectPerm);
|
|
79
|
-
self ++> comment_node[0];
|
|
80
|
-
report comment_node[0] ;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
def get_info -> TweetInfo {
|
|
84
|
-
return TweetInfo(
|
|
85
|
-
username=[self<-:Post:<-][0].username,
|
|
86
|
-
id=jid(self),
|
|
87
|
-
content=self.content,
|
|
88
|
-
embedding=self.embedding,
|
|
89
|
-
likes=[i.username for i in [self->:Like:->]],
|
|
90
|
-
comments=[
|
|
91
|
-
{
|
|
92
|
-
"username": [i<--(`?Profile)][0].username,
|
|
93
|
-
"id": jid(i),
|
|
94
|
-
"content": i.content
|
|
95
|
-
} for i in [self-->(`?Comment)]
|
|
96
|
-
]
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
can get with load_feed entry {
|
|
101
|
-
tweet_info = self.get_info();
|
|
102
|
-
visitor.results.append({"Tweet_Info": tweet_info});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
node Comment {
|
|
107
|
-
has content: str;
|
|
108
|
-
|
|
109
|
-
can update with update_comment entry {
|
|
110
|
-
self.content = visitor.updated_content;
|
|
111
|
-
report self ;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
can delete with remove_comment entry {
|
|
115
|
-
del self ;
|
|
116
|
-
disengage;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
edge Follow {}
|
|
121
|
-
|
|
122
|
-
edge Like {}
|
|
123
|
-
|
|
124
|
-
edge Post {}
|
|
125
|
-
|
|
126
|
-
walker visit_profile {
|
|
127
|
-
can visit_profile with `root entry {
|
|
128
|
-
visit [-->(`?Profile)] else {
|
|
129
|
-
new_profile = here ++> Profile();
|
|
130
|
-
grant(new_profile[0], level=ConnectPerm);
|
|
131
|
-
visit new_profile;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
walker update_profile(visit_profile) {
|
|
137
|
-
has new_username: str;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
walker get_profile(visit_profile) {}
|
|
141
|
-
|
|
142
|
-
walker load_user_profiles {
|
|
143
|
-
can load_profiles with `root entry {
|
|
144
|
-
self.profiles: list = [];
|
|
145
|
-
for each_root in allroots() {
|
|
146
|
-
profile = [each_root-->(`?Profile)][0];
|
|
147
|
-
self.profiles.append({"name": profile.username, "id": jid(profile)});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
can report_profiles with exit {
|
|
152
|
-
report self.profiles ;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
walker follow_request {}
|
|
157
|
-
|
|
158
|
-
walker un_follow_request {}
|
|
159
|
-
|
|
160
|
-
walker create_tweet(visit_profile) {
|
|
161
|
-
has content: str;
|
|
162
|
-
|
|
163
|
-
can tweet with Profile entry {
|
|
164
|
-
tweet_node = here +>: Post() :+> Tweet(content=self.content, embedding=[]);
|
|
165
|
-
grant(tweet_node[0], level=ConnectPerm);
|
|
166
|
-
report tweet_node ;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
walker update_tweet {
|
|
171
|
-
has updated_content: str;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
walker remove_tweet {}
|
|
175
|
-
|
|
176
|
-
walker like_tweet {}
|
|
177
|
-
|
|
178
|
-
walker remove_like {}
|
|
179
|
-
|
|
180
|
-
walker comment_tweet {
|
|
181
|
-
has content: str;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
walker update_comment {
|
|
185
|
-
has updated_content: str;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
walker remove_comment {}
|
|
189
|
-
|
|
190
|
-
walker load_feed(visit_profile) {
|
|
191
|
-
has search_query: str = "",
|
|
192
|
-
results: list = [];
|
|
193
|
-
|
|
194
|
-
can load with Profile entry {
|
|
195
|
-
visit [-->(`?Tweet)];
|
|
196
|
-
for user_node in [->:Follow:->(`?Profile)] {
|
|
197
|
-
visit [user_node-->(`?Tweet)];
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
can report_feed with exit {
|
|
202
|
-
report self.results ;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
# Client-side UI Components (marked with 'cl' for browser execution)
|
|
207
|
-
# ===================================================================
|
|
208
|
-
|
|
209
|
-
# Manual routing functions removed - now using reactive createRouter
|
|
210
|
-
|
|
211
|
-
# Shared data model for client/server
|
|
212
|
-
cl obj ClientTweet {
|
|
213
|
-
has username: str = "",
|
|
214
|
-
id: str = "",
|
|
215
|
-
content: str = "",
|
|
216
|
-
likes: list = [],
|
|
217
|
-
comments: list = [];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
cl obj ClientProfile {
|
|
221
|
-
has username: str = "",
|
|
222
|
-
id: str = "";
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
# UI Components - Render a single tweet card
|
|
226
|
-
cl def TweetCard(tweet: ClientTweet) -> any {
|
|
227
|
-
return <div
|
|
228
|
-
class="tweet-card"
|
|
229
|
-
style={{
|
|
230
|
-
"border": "1px solid #e1e8ed",
|
|
231
|
-
"padding": "15px",
|
|
232
|
-
"margin": "10px 0",
|
|
233
|
-
"borderRadius": "8px"
|
|
234
|
-
}}
|
|
235
|
-
>
|
|
236
|
-
<div
|
|
237
|
-
class="tweet-header"
|
|
238
|
-
style={{"fontWeight": "bold", "marginBottom": "80px"}}
|
|
239
|
-
>
|
|
240
|
-
@{tweet.username}
|
|
241
|
-
</div>
|
|
242
|
-
<div
|
|
243
|
-
class="tweet-content"
|
|
244
|
-
style={{"marginBottom": "12px"}}
|
|
245
|
-
>
|
|
246
|
-
{tweet.content}
|
|
247
|
-
</div>
|
|
248
|
-
<div
|
|
249
|
-
class="tweet-actions"
|
|
250
|
-
style={{"display": "flex", "gap": "15px"}}
|
|
251
|
-
>
|
|
252
|
-
<button
|
|
253
|
-
onclick={like_tweet_action(tweet.id)}
|
|
254
|
-
style={{"padding": "5px 10px", "cursor": "pointer"}}
|
|
255
|
-
>
|
|
256
|
-
Like ({tweet.likes.length})
|
|
257
|
-
</button>
|
|
258
|
-
<button
|
|
259
|
-
style={{"padding": "5px 10px"}}
|
|
260
|
-
>
|
|
261
|
-
Comment ({tweet.comments.length})
|
|
262
|
-
</button>
|
|
263
|
-
</div>
|
|
264
|
-
</div>;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
# Handle liking a tweet - calls server walker directly
|
|
268
|
-
# The compiler automatically transforms this to __jacCallFunction at compile time!
|
|
269
|
-
cl async def like_tweet_action(
|
|
270
|
-
tweet_id: str
|
|
271
|
-
) -> any {
|
|
272
|
-
try {
|
|
273
|
-
result = await like_tweet(tweet_id);
|
|
274
|
-
print("Tweet liked:", result);
|
|
275
|
-
# Re-render feed after like
|
|
276
|
-
window.location.reload();
|
|
277
|
-
} except Exception as e {
|
|
278
|
-
print("Error liking tweet:", e);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
# Render the main feed view
|
|
283
|
-
cl def FeedView(tweets: list) -> any {
|
|
284
|
-
return <div
|
|
285
|
-
class="feed-container"
|
|
286
|
-
style={{"maxWidth": "600px", "margin": "0 auto", "fontFamily": "sans-serif"}}
|
|
287
|
-
>
|
|
288
|
-
<div
|
|
289
|
-
class="feed-header"
|
|
290
|
-
style={{"padding": "20px", "borderBottom": "1px solid #e1e8ed"}}
|
|
291
|
-
>
|
|
292
|
-
<h1
|
|
293
|
-
style={{"margin": "0"}}
|
|
294
|
-
>
|
|
295
|
-
LittleX Feed
|
|
296
|
-
</h1>
|
|
297
|
-
</div>
|
|
298
|
-
<div class="feed-content">
|
|
299
|
-
{[TweetCard(tweet) for tweet in tweets]}
|
|
300
|
-
</div>
|
|
301
|
-
</div>;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
# Render login form
|
|
305
|
-
cl def LoginForm -> any {
|
|
306
|
-
suggestions = ['good luck', 'have fun', 'enjoy the ride'];
|
|
307
|
-
randomSuggestion = _.sample(suggestions);
|
|
308
|
-
result = "Good luck with your journey!";
|
|
309
|
-
return <Card
|
|
310
|
-
title="Login to LittleX"
|
|
311
|
-
style={{"maxWidth": "400px", "margin": "50px auto"}}
|
|
312
|
-
>
|
|
313
|
-
<Card.Meta
|
|
314
|
-
title={randomSuggestion}
|
|
315
|
-
description={result}
|
|
316
|
-
/>
|
|
317
|
-
<form
|
|
318
|
-
onSubmit={handle_login}
|
|
319
|
-
>
|
|
320
|
-
<div
|
|
321
|
-
style={{"marginBottom": "15px"}}
|
|
322
|
-
>
|
|
323
|
-
<label
|
|
324
|
-
style={{"display": "block", "marginBottom": "5px"}}
|
|
325
|
-
>
|
|
326
|
-
Username:
|
|
327
|
-
</label>
|
|
328
|
-
<input
|
|
329
|
-
type="text"
|
|
330
|
-
id="username"
|
|
331
|
-
style={{
|
|
332
|
-
"width": "100%",
|
|
333
|
-
"padding": "8px",
|
|
334
|
-
"boxSizing": "border-box"
|
|
335
|
-
}}
|
|
336
|
-
/>
|
|
337
|
-
</div>
|
|
338
|
-
<div
|
|
339
|
-
style={{"marginBottom": "15px"}}
|
|
340
|
-
>
|
|
341
|
-
<label
|
|
342
|
-
style={{"display": "block", "marginBottom": "5px"}}
|
|
343
|
-
>
|
|
344
|
-
Password:
|
|
345
|
-
</label>
|
|
346
|
-
<input
|
|
347
|
-
type="password"
|
|
348
|
-
id="password"
|
|
349
|
-
style={{
|
|
350
|
-
"width": "100%",
|
|
351
|
-
"padding": "8px",
|
|
352
|
-
"boxSizing": "border-box"
|
|
353
|
-
}}
|
|
354
|
-
/>
|
|
355
|
-
</div>
|
|
356
|
-
<Button
|
|
357
|
-
htmlType="submit"
|
|
358
|
-
style={{
|
|
359
|
-
"width": "100%",
|
|
360
|
-
"padding": "10px",
|
|
361
|
-
"backgroundColor": "#1da1f2",
|
|
362
|
-
"color": "white",
|
|
363
|
-
"border": "none",
|
|
364
|
-
"borderRadius": "4px",
|
|
365
|
-
"cursor": "pointer"
|
|
366
|
-
}}
|
|
367
|
-
>
|
|
368
|
-
Login
|
|
369
|
-
</Button>
|
|
370
|
-
<Button
|
|
371
|
-
color="default"
|
|
372
|
-
variant="dashed"
|
|
373
|
-
>
|
|
374
|
-
Dashed
|
|
375
|
-
</Button>
|
|
376
|
-
<Button
|
|
377
|
-
color="default"
|
|
378
|
-
variant="filled"
|
|
379
|
-
>
|
|
380
|
-
Filled
|
|
381
|
-
</Button>
|
|
382
|
-
<Button
|
|
383
|
-
color="default"
|
|
384
|
-
variant="text"
|
|
385
|
-
>
|
|
386
|
-
Text
|
|
387
|
-
</Button>
|
|
388
|
-
<Button
|
|
389
|
-
color="default"
|
|
390
|
-
variant="link"
|
|
391
|
-
>
|
|
392
|
-
Link
|
|
393
|
-
</Button>
|
|
394
|
-
</form>
|
|
395
|
-
<div
|
|
396
|
-
style={{"marginTop": "15px", "textAlign": "center"}}
|
|
397
|
-
>
|
|
398
|
-
<Link href="/signup">
|
|
399
|
-
Don't have an account? Sign up
|
|
400
|
-
</Link>
|
|
401
|
-
</div>
|
|
402
|
-
</Card>;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
# Handle login form submission
|
|
406
|
-
cl async def handle_login(event: any) -> None {
|
|
407
|
-
event.preventDefault();
|
|
408
|
-
username = document.getElementById("username").value;
|
|
409
|
-
password = document.getElementById("password").value;
|
|
410
|
-
success = await jacLogin(username, password);
|
|
411
|
-
if success {
|
|
412
|
-
navigate("/home");
|
|
413
|
-
} else {
|
|
414
|
-
alert("Login failed. Please try again.");
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
# Render signup form
|
|
419
|
-
cl def SignupForm -> any {
|
|
420
|
-
return <div
|
|
421
|
-
class="signup-container"
|
|
422
|
-
style={{
|
|
423
|
-
"maxWidth": "400px",
|
|
424
|
-
"margin": "50px auto",
|
|
425
|
-
"padding": "20px",
|
|
426
|
-
"border": "1px solid #e1e8ed",
|
|
427
|
-
"borderRadius": "8px",
|
|
428
|
-
"fontFamily": "sans-serif"
|
|
429
|
-
}}
|
|
430
|
-
>
|
|
431
|
-
<Typography.Title
|
|
432
|
-
level={2}
|
|
433
|
-
style={{"marginTop": "0"}}
|
|
434
|
-
>
|
|
435
|
-
Sign Up for LittleX
|
|
436
|
-
</Typography.Title>
|
|
437
|
-
<form
|
|
438
|
-
onSubmit={handle_signup}
|
|
439
|
-
>
|
|
440
|
-
<div
|
|
441
|
-
style={{"marginBottom": "15px"}}
|
|
442
|
-
>
|
|
443
|
-
<label
|
|
444
|
-
style={{"display": "block", "marginBottom": "5px"}}
|
|
445
|
-
>
|
|
446
|
-
Username:
|
|
447
|
-
</label>
|
|
448
|
-
<input
|
|
449
|
-
type="text"
|
|
450
|
-
id="signup-username"
|
|
451
|
-
required
|
|
452
|
-
style={{
|
|
453
|
-
"width": "100%",
|
|
454
|
-
"padding": "8px",
|
|
455
|
-
"boxSizing": "border-box"
|
|
456
|
-
}}
|
|
457
|
-
/>
|
|
458
|
-
</div>
|
|
459
|
-
<div
|
|
460
|
-
style={{"marginBottom": "15px"}}
|
|
461
|
-
>
|
|
462
|
-
<label
|
|
463
|
-
style={{"display": "block", "marginBottom": "5px"}}
|
|
464
|
-
>
|
|
465
|
-
Password:
|
|
466
|
-
</label>
|
|
467
|
-
<input
|
|
468
|
-
type="password"
|
|
469
|
-
id="signup-password"
|
|
470
|
-
required
|
|
471
|
-
style={{
|
|
472
|
-
"width": "100%",
|
|
473
|
-
"padding": "8px",
|
|
474
|
-
"boxSizing": "border-box"
|
|
475
|
-
}}
|
|
476
|
-
/>
|
|
477
|
-
</div>
|
|
478
|
-
<div
|
|
479
|
-
style={{"marginBottom": "15px"}}
|
|
480
|
-
>
|
|
481
|
-
<label
|
|
482
|
-
style={{"display": "block", "marginBottom": "5px"}}
|
|
483
|
-
>
|
|
484
|
-
Confirm Password:
|
|
485
|
-
</label>
|
|
486
|
-
<input
|
|
487
|
-
type="password"
|
|
488
|
-
id="signup-password-confirm"
|
|
489
|
-
required
|
|
490
|
-
style={{
|
|
491
|
-
"width": "100%",
|
|
492
|
-
"padding": "8px",
|
|
493
|
-
"boxSizing": "border-box"
|
|
494
|
-
}}
|
|
495
|
-
/>
|
|
496
|
-
</div>
|
|
497
|
-
<button
|
|
498
|
-
type="submit"
|
|
499
|
-
style={{
|
|
500
|
-
"width": "100%",
|
|
501
|
-
"padding": "10px",
|
|
502
|
-
"backgroundColor": "#1da1f2",
|
|
503
|
-
"color": "white",
|
|
504
|
-
"border": "none",
|
|
505
|
-
"borderRadius": "4px",
|
|
506
|
-
"cursor": "pointer"
|
|
507
|
-
}}
|
|
508
|
-
>
|
|
509
|
-
Sign Up
|
|
510
|
-
</button>
|
|
511
|
-
</form>
|
|
512
|
-
<div
|
|
513
|
-
style={{"marginTop": "15px", "textAlign": "center"}}
|
|
514
|
-
>
|
|
515
|
-
<Link href="/login">
|
|
516
|
-
Already have an account? Login
|
|
517
|
-
</Link>
|
|
518
|
-
</div>
|
|
519
|
-
</div>;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
# Navigation helper functions removed - using Link component and navigate() directly
|
|
523
|
-
|
|
524
|
-
# Handle signup form submission
|
|
525
|
-
cl async def handle_signup(event: any) -> None {
|
|
526
|
-
event.preventDefault();
|
|
527
|
-
username = document.getElementById("signup-username").value;
|
|
528
|
-
password = document.getElementById("signup-password").value;
|
|
529
|
-
password_confirm = document.getElementById("signup-password-confirm").value;
|
|
530
|
-
# Client-side validation
|
|
531
|
-
if password != password_confirm {
|
|
532
|
-
alert("Passwords do not match!");
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
if username.length < 3 {
|
|
536
|
-
alert("Username must be at least 3 characters long.");
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
if password.length < 6 {
|
|
540
|
-
alert("Password must be at least 6 characters long.");
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
# Use runtime auth function - no need to know about /user/register endpoint!
|
|
544
|
-
result = await jacSignup(username, password);
|
|
545
|
-
if result["success"] if "success" in result else False {
|
|
546
|
-
alert("Account created successfully! Welcome to LittleX!");
|
|
547
|
-
navigate("/home");
|
|
548
|
-
} else {
|
|
549
|
-
alert(result["error"] if "error" in result else "Signup failed");
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
# Handle logout
|
|
554
|
-
cl def logout_action -> None {
|
|
555
|
-
jacLogout();
|
|
556
|
-
navigate("/login");
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
# Main App component with declarative router
|
|
560
|
-
cl def:pub App -> any {
|
|
561
|
-
# Create routes array manually (workaround for JS compiler bug with named args)
|
|
562
|
-
login_route = {
|
|
563
|
-
"path": "/login",
|
|
564
|
-
"component": lambda -> any{ return LoginForm(); } ,
|
|
565
|
-
"guard": None
|
|
566
|
-
};
|
|
567
|
-
signup_route = {
|
|
568
|
-
"path": "/signup",
|
|
569
|
-
"component": lambda -> any{ return SignupForm(); } ,
|
|
570
|
-
"guard": None
|
|
571
|
-
};
|
|
572
|
-
home_route = {
|
|
573
|
-
"path": "/home",
|
|
574
|
-
"component": lambda -> any{ return HomeView(); } ,
|
|
575
|
-
"guard": jacIsLoggedIn
|
|
576
|
-
};
|
|
577
|
-
profile_route = {
|
|
578
|
-
"path": "/profile",
|
|
579
|
-
"component": lambda -> any{ return ProfileView(); } ,
|
|
580
|
-
"guard": jacIsLoggedIn
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
routes = [login_route, signup_route, home_route, profile_route];
|
|
584
|
-
router = initRouter(routes, "/login");
|
|
585
|
-
|
|
586
|
-
# Get current path for navbar
|
|
587
|
-
currentPath = router.path();
|
|
588
|
-
|
|
589
|
-
return <div class="app-container">
|
|
590
|
-
{build_nav_bar(currentPath)}
|
|
591
|
-
<Rotate>
|
|
592
|
-
<span>
|
|
593
|
-
😂
|
|
594
|
-
</span>
|
|
595
|
-
</Rotate>
|
|
596
|
-
{router.render()}
|
|
597
|
-
</div>;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
# Helper to build navigation bar
|
|
601
|
-
cl def build_nav_bar(route: str) -> any {
|
|
602
|
-
if not jacIsLoggedIn() or route == "/login" or route == "/signup" {
|
|
603
|
-
return None;
|
|
604
|
-
}
|
|
605
|
-
return <nav
|
|
606
|
-
style={{
|
|
607
|
-
"backgroundColor": "#1da1f2",
|
|
608
|
-
"padding": "15px",
|
|
609
|
-
"marginBottom": "20px"
|
|
610
|
-
}}
|
|
611
|
-
>
|
|
612
|
-
<div
|
|
613
|
-
style={{
|
|
614
|
-
"maxWidth": "600px",
|
|
615
|
-
"margin": "0 auto",
|
|
616
|
-
"display": "flex",
|
|
617
|
-
"gap": "20px",
|
|
618
|
-
"alignItems": "center"
|
|
619
|
-
}}
|
|
620
|
-
>
|
|
621
|
-
<Link href="/home">
|
|
622
|
-
<span
|
|
623
|
-
style={{
|
|
624
|
-
"color": "white",
|
|
625
|
-
"textDecoration": "none",
|
|
626
|
-
"fontWeight": "bold"
|
|
627
|
-
}}
|
|
628
|
-
>
|
|
629
|
-
Home
|
|
630
|
-
</span>
|
|
631
|
-
</Link>
|
|
632
|
-
<Link href="/profile">
|
|
633
|
-
<span
|
|
634
|
-
style={{
|
|
635
|
-
"color": "white",
|
|
636
|
-
"textDecoration": "none",
|
|
637
|
-
"fontWeight": "bold"
|
|
638
|
-
}}
|
|
639
|
-
>
|
|
640
|
-
Profile
|
|
641
|
-
</span>
|
|
642
|
-
</Link>
|
|
643
|
-
<button
|
|
644
|
-
onClick={logout_action}
|
|
645
|
-
style={{
|
|
646
|
-
"marginLeft": "auto",
|
|
647
|
-
"padding": "5px 15px",
|
|
648
|
-
"backgroundColor": "white",
|
|
649
|
-
"color": "#1da1f2",
|
|
650
|
-
"border": "none",
|
|
651
|
-
"borderRadius": "4px",
|
|
652
|
-
"cursor": "pointer",
|
|
653
|
-
"fontWeight": "bold"
|
|
654
|
-
}}
|
|
655
|
-
>
|
|
656
|
-
Logout
|
|
657
|
-
</button>
|
|
658
|
-
</div>
|
|
659
|
-
</nav>;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
# Home view - simplified for testing reactive routing
|
|
663
|
-
cl def HomeView -> any {
|
|
664
|
-
if not jacIsLoggedIn() {
|
|
665
|
-
navigate("/login");
|
|
666
|
-
return <div></div>;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return <div
|
|
670
|
-
style={{"textAlign": "center", "padding": "50px", "fontFamily": "sans-serif"}}
|
|
671
|
-
>
|
|
672
|
-
<h1>
|
|
673
|
-
Home Feed
|
|
674
|
-
</h1>
|
|
675
|
-
<p>
|
|
676
|
-
Welcome to LittleX! This is the home page.
|
|
677
|
-
</p>
|
|
678
|
-
<p>
|
|
679
|
-
The reactive router is working!
|
|
680
|
-
</p>
|
|
681
|
-
</div>;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
# Profile view
|
|
685
|
-
cl def ProfileView -> any {
|
|
686
|
-
if not jacIsLoggedIn() {
|
|
687
|
-
navigate("/login");
|
|
688
|
-
return <div></div>;
|
|
689
|
-
}
|
|
690
|
-
return <div
|
|
691
|
-
class="profile-container"
|
|
692
|
-
style={{
|
|
693
|
-
"maxWidth": "600px",
|
|
694
|
-
"margin": "20px auto",
|
|
695
|
-
"padding": "20px",
|
|
696
|
-
"fontFamily": "sans-serif"
|
|
697
|
-
}}
|
|
698
|
-
>
|
|
699
|
-
<h1>
|
|
700
|
-
Profile
|
|
701
|
-
</h1>
|
|
702
|
-
<div
|
|
703
|
-
style={{
|
|
704
|
-
"padding": "15px",
|
|
705
|
-
"border": "1px solid #e1e8ed",
|
|
706
|
-
"borderRadius": "8px"
|
|
707
|
-
}}
|
|
708
|
-
>
|
|
709
|
-
<p>
|
|
710
|
-
Profile information will be displayed here.
|
|
711
|
-
</p>
|
|
712
|
-
</div>
|
|
713
|
-
</div>;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
# Main SPA entry point - simplified with reactive routing
|
|
717
|
-
cl def:pub jac_app -> any {
|
|
718
|
-
return App();
|
|
719
|
-
}
|