sandbox-notebookllamalm/src/notebookllama/styles.py
DJP fbefffe002 Add logo to all pages, hide sidebar on login, create Admin Dashboard
Logo Implementation:
- Logo now appears on ALL pages automatically
- Moved logo display to apply_custom_styles() function
- 300px width, positioned at top with margin adjustments
- Uses SBLM.jpg from src/notebookllama/

Login Page Improvements:
- Sidebar completely hidden when not logged in
- Cleaner login experience
- No navigation shown to unauthenticated users
- Title updated to "Sandbox-NotebookLM - Login"

Admin Dashboard:
- Replaced Observability Dashboard with Admin Dashboard
- Shows platform usage statistics:
  * Total users, notebooks, documents, chat messages
  * Estimated costs (documents, chats, podcasts)
  * Recent activity (users, notebooks, documents)
  * Background task monitoring
  * User analytics (most active users)
  * System health (failed/pending tasks)

Admin Access Control:
- Only user ID 1 or users with "admin" in email can access
- Admin link only shows in sidebar for admins
- Protected with access check

Cost Tracking:
- Document processing: ~$0.60 each
- Chat messages: ~$0.01 each
- Podcasts: ~$0.50 each
- Total cost estimate displayed

Analytics Features:
- Top 5 most active users by notebook count
- Recent users/notebooks/documents lists
- Background task status with color coding
- System health indicators
- Real-time statistics

Files:
- Created pages/5_Admin_Dashboard.py
- Removed pages/5_Observability_Dashboard.py (backed up)
- Updated styles.py with show_logo() function
- Updated App.py navigation to show Admin conditionally

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 19:10:51 -04:00

193 lines
4.7 KiB
Python

"""
Global CSS styles for NotebookLlaMa
Apply consistent typography and styling across all pages
"""
def get_custom_css():
"""Return custom CSS for the application"""
return """
<style>
/* Import Montserrat font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap');
/* Apply Montserrat globally */
* {
font-family: 'Montserrat', sans-serif !important;
}
/* Main headers - reduce from 44px to 20px */
h1 {
font-size: 20px !important;
font-weight: 600 !important;
line-height: 1.3 !important;
margin-bottom: 0.5rem !important;
}
/* Subheaders */
h2 {
font-size: 18px !important;
font-weight: 600 !important;
line-height: 1.3 !important;
margin-bottom: 0.5rem !important;
}
h3 {
font-size: 16px !important;
font-weight: 600 !important;
line-height: 1.3 !important;
margin-bottom: 0.5rem !important;
}
/* Body text */
p, div, span {
font-size: 16px !important;
line-height: 1.5 !important;
}
/* Source citations - smaller text */
.stExpander summary {
font-size: 14px !important;
}
.stMarkdown a, .stMarkdown small, .stCaption {
font-size: 14px !important;
}
/* Chat messages */
.stChatMessage {
font-size: 16px !important;
}
.stChatMessage h1, .stChatMessage h2, .stChatMessage h3 {
font-size: 16px !important;
font-weight: 600 !important;
}
.stChatMessage strong {
font-size: 16px !important;
font-weight: 600 !important;
}
/* Buttons */
.stButton button {
font-family: 'Montserrat', sans-serif !important;
font-size: 14px !important;
font-weight: 500 !important;
}
/* Input fields */
input, textarea, select {
font-family: 'Montserrat', sans-serif !important;
font-size: 14px !important;
}
/* Sidebar */
.css-1d391kg, [data-testid="stSidebar"] {
font-size: 14px !important;
}
[data-testid="stSidebar"] h1 {
font-size: 16px !important;
font-weight: 600 !important;
}
[data-testid="stSidebar"] .stMarkdown h1,
[data-testid="stSidebar"] .stMarkdown h2,
[data-testid="stSidebar"] .stMarkdown h3 {
font-size: 14px !important;
font-weight: 600 !important;
}
/* Hide Streamlit's auto-generated page list (keep our custom navigation) */
section[data-testid="stSidebarNav"] {
display: none !important;
}
/* Keep our custom navigation visible */
[data-testid="stSidebar"] .stMarkdown {
display: block !important;
}
/* Sidebar user info - reduce username size */
[data-testid="stSidebar"] .element-container {
font-size: 14px !important;
}
/* Expander headers */
.streamlit-expanderHeader {
font-size: 15px !important;
font-weight: 500 !important;
}
/* Source link badges */
.stMarkdown code {
font-size: 13px !important;
padding: 2px 6px !important;
}
/* Info/Warning/Error boxes */
.stAlert {
font-size: 14px !important;
}
/* Captions */
.caption, small, .stCaption {
font-size: 13px !important;
color: #6c757d !important;
}
/* Reduce spacing for cleaner look */
.block-container {
padding-top: 2rem !important;
padding-bottom: 1rem !important;
}
/* Streamlit title */
[data-testid="stAppViewContainer"] h1:first-of-type {
font-size: 24px !important;
font-weight: 700 !important;
}
/* Hide deploy button in top right */
[data-testid="stToolbar"] {
display: none !important;
}
/* Hide keyboard shortcut hints */
[data-testid="stDecoration"] {
display: none !important;
}
.stDecorationBar {
display: none !important;
}
/* Hide the keyboard shortcut text */
button[kind="header"] span {
display: none !important;
}
</style>
"""
def show_logo():
"""Display the logo at the top of the page"""
import streamlit as st
import base64
from pathlib import Path
logo_path = Path(__file__).parent / "SBLM.jpg"
if logo_path.exists():
with open(logo_path, "rb") as f:
logo_data = base64.b64encode(f.read()).decode()
st.markdown(
f'<div style="text-align: left; margin-bottom: 1.5rem; margin-top: -1rem;"><img src="data:image/jpeg;base64,{logo_data}" width="300"></div>',
unsafe_allow_html=True
)
def apply_custom_styles():
"""Apply custom CSS to the current page"""
import streamlit as st
st.markdown(get_custom_css(), unsafe_allow_html=True)
show_logo()