Add user information and calendar list to sidebar
Backend changes: - Add /api/user/info endpoint to fetch user details and calendar list - Create UserInfo and CalendarInfo models for API responses - Filter out generic calendar collections from sidebar display - Extract readable calendar names with proper title case formatting Frontend changes: - Fetch and display user info (username, server URL) in sidebar header - Show list of user's calendars with hover effects and styling - Add loading states and error handling for user info - Reorganize sidebar layout: header, navigation, calendar list, logout Styling: - Enhanced sidebar with user info section and calendar list - Responsive design hides user info and calendar list on mobile - Improved logout button positioning in sidebar footer - Professional styling with proper spacing and visual hierarchy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,19 @@ impl Default for ReminderAction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UserInfo {
|
||||
pub username: String,
|
||||
pub server_url: String,
|
||||
pub calendars: Vec<CalendarInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CalendarInfo {
|
||||
pub path: String,
|
||||
pub display_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CalendarEvent {
|
||||
pub uid: String,
|
||||
@@ -135,6 +148,48 @@ impl CalendarService {
|
||||
Self { base_url }
|
||||
}
|
||||
|
||||
/// Fetch user info including available calendars
|
||||
pub async fn fetch_user_info(&self, token: &str, password: &str) -> Result<UserInfo, String> {
|
||||
let window = web_sys::window().ok_or("No global window exists")?;
|
||||
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("GET");
|
||||
opts.set_mode(RequestMode::Cors);
|
||||
|
||||
let url = format!("{}/user/info", self.base_url);
|
||||
let request = Request::new_with_str_and_init(&url, &opts)
|
||||
.map_err(|e| format!("Request creation failed: {:?}", e))?;
|
||||
|
||||
request.headers().set("Authorization", &format!("Bearer {}", token))
|
||||
.map_err(|e| format!("Authorization header setting failed: {:?}", e))?;
|
||||
|
||||
request.headers().set("X-CalDAV-Password", password)
|
||||
.map_err(|e| format!("Password header setting failed: {:?}", e))?;
|
||||
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request))
|
||||
.await
|
||||
.map_err(|e| format!("Network request failed: {:?}", e))?;
|
||||
|
||||
let resp: Response = resp_value.dyn_into()
|
||||
.map_err(|e| format!("Response cast failed: {:?}", e))?;
|
||||
|
||||
let text = JsFuture::from(resp.text()
|
||||
.map_err(|e| format!("Text extraction failed: {:?}", e))?)
|
||||
.await
|
||||
.map_err(|e| format!("Text promise failed: {:?}", e))?;
|
||||
|
||||
let text_string = text.as_string()
|
||||
.ok_or("Response text is not a string")?;
|
||||
|
||||
if resp.ok() {
|
||||
let user_info: UserInfo = serde_json::from_str(&text_string)
|
||||
.map_err(|e| format!("JSON parsing failed: {}", e))?;
|
||||
Ok(user_info)
|
||||
} else {
|
||||
Err(format!("Request failed with status {}: {}", resp.status(), text_string))
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch calendar events for a specific month
|
||||
pub async fn fetch_events_for_month(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user