Skip to content

Commit 2b5779a

Browse files
Merge pull request #88 from TryCli-Studio/dev
feat(client): revamp landing page and dashboard content & layout
2 parents 45b3ed4 + 5563084 commit 2b5779a

4 files changed

Lines changed: 138 additions & 117 deletions

File tree

client/src/pages/dashboard.rs

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub fn DashboardPage() -> impl IntoView {
2828
Ok(resp) => {
2929
if resp.ok() {
3030
if let Ok(u) = resp.json::<User>().await {
31-
web_sys::console::log_1(&JsValue::from_str(&format!("User authenticated: {}", u.login)));
31+
// user authenticated
3232
set_user.set(Some(u.clone()));
3333

3434
let proj_url = format!("{}/api/my-projects", api_base());
@@ -44,14 +44,14 @@ pub fn DashboardPage() -> impl IntoView {
4444
set_projects.set(projs);
4545
set_error.set(None);
4646
} else {
47-
set_error.set(Some("Failed to parse projects".to_string()));
47+
set_error.set(Some("Failed to parse project list".to_string()));
4848
}
4949
} else {
50-
set_error.set(Some("Failed to fetch projects".to_string()));
50+
set_error.set(Some("Failed to fetch deployments".to_string()));
5151
}
5252
}
5353
Err(_) => {
54-
set_error.set(Some("Network error".to_string()));
54+
set_error.set(Some("Network error connecting to API".to_string()));
5555
}
5656
}
5757
set_loading.set(false);
@@ -62,7 +62,7 @@ pub fn DashboardPage() -> impl IntoView {
6262
}
6363
Err(_) => {
6464
set_loading.set(false);
65-
set_error.set(Some("Failed to authenticate".to_string()));
65+
set_error.set(Some("Authentication check failed".to_string()));
6666
}
6767
}
6868
});
@@ -84,29 +84,22 @@ pub fn DashboardPage() -> impl IntoView {
8484
spawn_local(async move {
8585
let encoded_query = js_sys::encode_uri_component(&query_clone).to_string();
8686
let search_url = format!("{}/api/search-projects?q={}", api_base(), encoded_query);
87-
web_sys::console::log_1(&JsValue::from_str(&format!("Searching for: {}", search_url)));
87+
8888
match Request::get(&search_url)
8989
.credentials(RequestCredentials::Include)
9090
.send()
9191
.await
9292
{
9393
Ok(resp) => {
94-
web_sys::console::log_1(&JsValue::from_str(&format!("Search response status: {}", resp.status())));
9594
if resp.ok() {
9695
if let Ok(results) = resp.json::<Vec<ProjectSummary>>().await {
97-
web_sys::console::log_1(&JsValue::from_str(&format!("Found {} results", results.len())));
9896
set_search_results.set(results);
9997
set_show_suggestions.set(true);
100-
} else {
101-
web_sys::console::log_1(&JsValue::from_str("Failed to parse search results JSON"));
10298
}
103-
} else {
104-
let err_text = resp.text().await.unwrap_or_default();
105-
web_sys::console::log_1(&JsValue::from_str(&format!("Search failed: {}", err_text)));
10699
}
107100
}
108101
Err(e) => {
109-
web_sys::console::log_1(&JsValue::from_str(&format!("Search network error: {:?}", e)));
102+
web_sys::console::log_1(&JsValue::from_str(&format!("Search error: {:?}", e)));
110103
}
111104
}
112105
});
@@ -151,10 +144,10 @@ pub fn DashboardPage() -> impl IntoView {
151144
<span style="color: var(--text-main); font-weight: 500;">{u.login.clone()}</span>
152145
</div>
153146
<a href=format!("{}/auth/logout", api_base())
154-
class="btn-primary btn-logout"
155-
rel="external"
156-
style="text-decoration: none; font-size: 0.9rem;">
157-
"Logout"
147+
class="btn-primary btn-logout"
148+
rel="external"
149+
style="text-decoration: none; font-size: 0.9rem;">
150+
"Logout"
158151
</a>
159152
}.into_view(),
160153
None => view! {
@@ -175,8 +168,8 @@ pub fn DashboardPage() -> impl IntoView {
175168
<div class="dashboard-container">
176169
<div class="dashboard-hero">
177170
<div class="hero-content">
178-
<h1 class="hero-title">"Dream it, Build it."</h1>
179-
<p class="hero-subtitle">"Create and manage your interactive CLI projects"</p>
171+
<h1 class="hero-title">"Workspace Overview"</h1>
172+
<p class="hero-subtitle">"Manage your interactive sandboxes, monitor deployments, and publish new snapshots."</p>
180173
<DashboardSearch
181174
search_input=search_input
182175
set_search_input=set_search_input
@@ -191,9 +184,9 @@ pub fn DashboardPage() -> impl IntoView {
191184

192185
<div class="dashboard-section">
193186
<div class="section-header">
194-
<h2>"Your Projects"</h2>
187+
<h2>"Active Deployments"</h2>
195188
<A href="/new" class="btn-primary">
196-
"+ New Project"
189+
"+ Initialize Environment"
197190
</A>
198191
</div>
199192

@@ -211,8 +204,8 @@ pub fn DashboardPage() -> impl IntoView {
211204
},
212205
(None, false) => view! {
213206
<div style="display: flex; height: calc(100vh - 60px); justify-content: center; align-items: center; flex-direction: column; gap: 20px;">
214-
<h2 style="color: var(--text-main);">"Welcome to TryCli Studio"</h2>
215-
<p style="color: var(--text-muted);">"Please sign in to start creating interactive demos."</p>
207+
<h2 style="color: var(--text-main);">"TryCli Studio Session Expired"</h2>
208+
<p style="color: var(--text-muted);">"Please authenticate via GitHub to access your workspace."</p>
216209
</div>
217210
}.into_view(),
218211
_ => view! {
@@ -241,7 +234,7 @@ fn DashboardSearch(
241234
<div class="input-hero-wrapper" style="position: relative;">
242235
<input type="text"
243236
class="input-hero"
244-
placeholder="Search or create a new project..."
237+
placeholder="Search repositories or initialize a new sandbox..."
245238
value=search_input.get()
246239
on:input=handle_search_input
247240
on:focus=move |_| {
@@ -265,22 +258,22 @@ fn DashboardSearch(
265258
let navigate_fn = nav.clone();
266259

267260
view! {
268-
<div style="position: absolute; top: 100%; left: 0; right: 0; background: var(--bg-main); border: 1px solid var(--border); border-top: none; border-radius: 0 0 8px 8px; max-height: 300px; overflow-y: auto; z-index: 10;">
261+
<div style="position: absolute; top: 100%; left: 0; right: 0; background: var(--bg-panel); border: 1px solid var(--border); border-top: none; border-radius: 0 0 8px 8px; max-height: 300px; overflow-y: auto; z-index: 10; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);">
269262
{if results.is_empty() && !input_val.is_empty() {
270263
let input_val_copy = input_val.clone();
271264
let input_val_display = input_val.clone();
272265
view! {
273266
<div style="padding: 16px; color: var(--text-muted);">
274-
<p style="margin: 0 0 8px 0;">"No project found."</p>
267+
<p style="margin: 0 0 8px 0; font-size: 0.9rem;">"No existing environment found."</p>
275268
<button class="btn-primary"
276269
style="font-size: 0.9rem; padding: 8px 12px; width: 100%; text-align: left;"
277270
on:click=move |_| {
278-
set_show_suggestions.set(false);
279-
set_search_input.set(String::new());
280-
let encoded_name = js_sys::encode_uri_component(&input_val_copy).to_string();
281-
navigate_fn(&format!("/new?name={}", encoded_name), Default::default());
282-
}>
283-
{format!("Create project '{}'?", input_val_display)}
271+
set_show_suggestions.set(false);
272+
set_search_input.set(String::new());
273+
let encoded_name = js_sys::encode_uri_component(&input_val_copy).to_string();
274+
navigate_fn(&format!("/new?name={}", encoded_name), Default::default());
275+
}>
276+
{format!("Initialize repository '{}'?", input_val_display)}
284277
</button>
285278
</div>
286279
}.into_view()
@@ -296,13 +289,13 @@ fn DashboardSearch(
296289
let proj_image = proj.image_tag.clone();
297290
view! {
298291
<a href=format!("/{}/{}", login, proj_slug.clone())
299-
style="display: block; padding: 12px 16px; color: var(--text-main); text-decoration: none; border-bottom: 1px solid var(--bg-dark); cursor: pointer;"
292+
style="display: block; padding: 12px 16px; color: var(--text-main); text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.05); cursor: pointer; transition: background 0.2s;"
300293
on:click=move |_| {
301294
set_show_suggestions.set(false);
302295
set_search_input.set(String::new());
303296
}>
304-
<div style="font-weight: 500;">{proj_slug}</div>
305-
<div style="font-size: 0.85rem; color: var(--text-muted);">{proj_image}</div>
297+
<div style="font-weight: 600; font-size: 0.95rem;">{proj_slug}</div>
298+
<div style="font-size: 0.8rem; color: var(--text-muted); font-family: var(--font-mono); margin-top: 2px;">{proj_image}</div>
306299
</a>
307300
}
308301
}
@@ -326,18 +319,17 @@ fn DashboardProjectList(
326319
set_error: WriteSignal<Option<String>>,
327320
set_loading: WriteSignal<bool>,
328321
projects: ReadSignal<Vec<ProjectSummary>>,
329-
set_projects: WriteSignal<Vec<ProjectSummary>>, // < Receive Setter
322+
set_projects: WriteSignal<Vec<ProjectSummary>>,
330323
user_login: Rc<String>,
331324
) -> impl IntoView {
332325

333326
// Handler for deletion logic
334327
let handle_delete = move |slug: String| {
335328
let prompt_text = format!(
336-
"⚠️ DESTRUCTIVE ACTION\n\nThis will permanently delete the project '{}' and its Docker image.\n\nPlease type the project name to confirm:",
329+
"⚠️ TERMINATE ENVIRONMENT\n\nThis will permanently delete the snapshot '{}'. This action cannot be undone.\n\nPlease type the environment name to confirm:",
337330
slug
338331
);
339332

340-
// FIX: Match against Ok(Some(input)) to handle the Result wrapper
341333
if let Ok(Some(input)) = window().prompt_with_message(&prompt_text) {
342334
if input == slug {
343335
let slug_clone = slug.clone();
@@ -356,9 +348,9 @@ fn DashboardProjectList(
356348
set_projects.update(|list| {
357349
list.retain(|p| p.slug != slug_clone);
358350
});
359-
let _ = window().alert_with_message("Project and Docker image deleted.");
351+
let _ = window().alert_with_message("Environment terminated and image removed.");
360352
} else {
361-
let _ = window().alert_with_message("Failed to delete project. Check server logs.");
353+
let _ = window().alert_with_message("Failed to terminate environment.");
362354
}
363355
},
364356
Err(_) => {
@@ -367,7 +359,7 @@ fn DashboardProjectList(
367359
}
368360
});
369361
} else {
370-
let _ = window().alert_with_message("Project name mismatch. Deletion cancelled.");
362+
let _ = window().alert_with_message("Name mismatch. Termination cancelled.");
371363
}
372364
}
373365
};
@@ -381,7 +373,7 @@ fn DashboardProjectList(
381373
set_error.set(None);
382374
set_loading.set(true);
383375
}>
384-
"Retry"
376+
"Retry Connection"
385377
</button>
386378
</div>
387379
}.into_view(),
@@ -390,9 +382,9 @@ fn DashboardProjectList(
390382
if proj_list.is_empty() {
391383
view! {
392384
<div class="empty-state">
393-
<p class="empty-message">"No projects yet. Create your first one!"</p>
385+
<p class="empty-message">"No active environments. Initialize a new sandbox to start building."</p>
394386
<A href="/new" class="btn-primary">
395-
"Create Project"
387+
"Initialize Environment"
396388
</A>
397389
</div>
398390
}.into_view()
@@ -413,17 +405,17 @@ fn DashboardProjectList(
413405
<h3 class="card-title">{proj.slug.clone()}</h3>
414406
</div>
415407
<div class="card-body">
416-
<p class="card-meta">"Image: "<code>{proj.image_tag.clone()}</code></p>
408+
<p class="card-meta">"Base Image: "<code>{proj.image_tag.clone()}</code></p>
417409
</div>
418410
<div class="card-footer" style="display: flex;">
419411
<A href=format!("/{}/{}", login, proj.slug)
420412
class="btn-card">
421-
"View"
413+
"Launch Studio"
422414
</A>
423415
<button
424416
class="btn-danger"
425417
on:click=move |_| handle_delete(delete_slug.clone())>
426-
"Delete"
418+
"Terminate"
427419
</button>
428420
</div>
429421
</div>

0 commit comments

Comments
 (0)