| import gradio as gr |
| import pandas as pd |
| from datetime import datetime, date |
| from supabase import create_client, Client |
| import plotly.express as px |
| import plotly.graph_objects as go |
|
|
| |
| CUSTOM_CSS = """ |
| /* Main theme and layout */ |
| .gradio-container { |
| max-width: 1400px !important; |
| margin: auto !important; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
| padding: 20px !important; |
| border-radius: 20px !important; |
| box-shadow: 0 20px 40px rgba(0,0,0,0.1) !important; |
| } |
| |
| /* Header styling */ |
| .main-header { |
| text-align: center; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| font-size: 3em !important; |
| font-weight: bold !important; |
| margin-bottom: 20px !important; |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.3); |
| } |
| |
| /* Card styling */ |
| .card { |
| background: rgba(255, 255, 255, 0.95) !important; |
| border-radius: 15px !important; |
| padding: 20px !important; |
| margin: 10px !important; |
| box-shadow: 0 8px 32px rgba(0,0,0,0.1) !important; |
| border: 1px solid rgba(255, 255, 255, 0.2) !important; |
| backdrop-filter: blur(10px) !important; |
| } |
| |
| /* Button styling */ |
| .primary-btn { |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
| border: none !important; |
| border-radius: 25px !important; |
| padding: 12px 24px !important; |
| color: white !important; |
| font-weight: bold !important; |
| transition: all 0.3s ease !important; |
| box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important; |
| } |
| |
| .primary-btn:hover { |
| transform: translateY(-2px) !important; |
| box-shadow: 0 6px 20px rgba(0,0,0,0.3) !important; |
| } |
| |
| /* Status indicators */ |
| .status-success { |
| background: linear-gradient(135deg, #4CAF50, #45a049) !important; |
| color: white !important; |
| border-radius: 10px !important; |
| padding: 10px !important; |
| text-align: center !important; |
| } |
| |
| .status-error { |
| background: linear-gradient(135deg, #f44336, #d32f2f) !important; |
| color: white !important; |
| border-radius: 10px !important; |
| padding: 10px !important; |
| text-align: center !important; |
| } |
| |
| /* Tab styling */ |
| .tab-nav { |
| background: rgba(255, 255, 255, 0.1) !important; |
| border-radius: 15px !important; |
| padding: 5px !important; |
| } |
| |
| /* Table styling */ |
| .dataframe { |
| border-radius: 10px !important; |
| overflow: hidden !important; |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1) !important; |
| } |
| |
| /* Form styling */ |
| .form-container { |
| background: rgba(255, 255, 255, 0.05) !important; |
| border-radius: 15px !important; |
| padding: 20px !important; |
| margin: 15px 0 !important; |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; |
| } |
| |
| /* Stats cards */ |
| .stats-card { |
| background: linear-gradient(135deg, rgba(255,255,255,0.2), rgba(255,255,255,0.1)) !important; |
| border-radius: 15px !important; |
| padding: 20px !important; |
| text-align: center !important; |
| margin: 10px !important; |
| backdrop-filter: blur(10px) !important; |
| border: 1px solid rgba(255,255,255,0.2) !important; |
| } |
| |
| .stats-number { |
| font-size: 2.5em !important; |
| font-weight: bold !important; |
| color: #667eea !important; |
| margin-bottom: 5px !important; |
| } |
| |
| .stats-label { |
| color: #666 !important; |
| font-size: 1.1em !important; |
| text-transform: uppercase !important; |
| letter-spacing: 1px !important; |
| } |
| """ |
|
|
| class ModernLibraryManagement: |
| def __init__(self): |
| """Initialize Supabase connection for library management""" |
| self.url = "https://bnjblzcqaumctpehgoid.supabase.co" |
| self.key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJuamJsemNxYXVtY3RwZWhnb2lkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgyOTA0OTQsImV4cCI6MjA3Mzg2NjQ5NH0.L4wPzuBj9dlSKpYxO1eDX-57KcP0mbNfN8stmTB-STM" |
| |
| try: |
| self.supabase: Client = create_client(self.url, self.key) |
| self.connection_status = "π’ Connected to Library Database" |
| except Exception as e: |
| self.supabase = None |
| self.connection_status = f"π΄ Connection failed: {str(e)}" |
| |
| def get_dashboard_stats(self): |
| """Get dashboard statistics""" |
| try: |
| |
| members_count = len(self.supabase.table("member").select("id").execute().data) |
| books_count = len(self.supabase.table("book").select("id").execute().data) |
| active_borrows = len(self.supabase.table("borrow").select("id").is_("return_date", "null").execute().data) |
| |
| return members_count, books_count, active_borrows |
| except Exception as e: |
| return 0, 0, 0 |
| |
| def get_borrowing_chart(self): |
| """Create a borrowing trends chart""" |
| try: |
| |
| borrows = self.supabase.table("borrow").select("*").execute().data |
| |
| if not borrows: |
| |
| fig = go.Figure() |
| fig.add_annotation(text="No borrowing data available", |
| xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False) |
| fig.update_layout(title="Borrowing Activity", height=300) |
| return fig |
| |
| |
| df = pd.DataFrame(borrows) |
| df['borrow_date'] = pd.to_datetime(df['borrow_date']) |
| df['month'] = df['borrow_date'].dt.strftime('%Y-%m') |
| |
| |
| monthly_borrows = df.groupby('month').size().reset_index(name='count') |
| |
| |
| fig = px.bar(monthly_borrows, x='month', y='count', |
| title="π Monthly Borrowing Activity", |
| color='count', |
| color_continuous_scale="Viridis") |
| |
| fig.update_layout( |
| height=300, |
| showlegend=False, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| font=dict(color='#333') |
| ) |
| |
| return fig |
| |
| except Exception as e: |
| fig = go.Figure() |
| fig.add_annotation(text=f"Error: {str(e)}", |
| xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False) |
| fig.update_layout(title="Borrowing Activity", height=300) |
| return fig |
| |
| def get_popular_books_chart(self): |
| """Create a popular books chart""" |
| try: |
| |
| borrows = self.supabase.table("borrow").select("book_id").execute().data |
| books = self.supabase.table("book").select("*").execute().data |
| |
| if not borrows or not books: |
| fig = go.Figure() |
| fig.add_annotation(text="No data available", |
| xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False) |
| fig.update_layout(title="Popular Books", height=300) |
| return fig |
| |
| |
| book_counts = {} |
| for borrow in borrows: |
| book_id = borrow['book_id'] |
| book_counts[book_id] = book_counts.get(book_id, 0) + 1 |
| |
| |
| book_dict = {book['id']: book['title'] for book in books} |
| |
| |
| titles = [] |
| counts = [] |
| for book_id, count in book_counts.items(): |
| title = book_dict.get(book_id, f"Book {book_id}") |
| titles.append(title[:30] + "..." if len(title) > 30 else title) |
| counts.append(count) |
| |
| |
| sorted_data = sorted(zip(titles, counts), key=lambda x: x[1], reverse=True)[:10] |
| if sorted_data: |
| titles, counts = zip(*sorted_data) |
| |
| |
| fig = go.Figure(data=go.Bar( |
| y=list(titles), |
| x=list(counts), |
| orientation='h', |
| marker_color='rgba(102, 126, 234, 0.8)' |
| )) |
| |
| fig.update_layout( |
| title="π Most Popular Books", |
| height=300, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| font=dict(color='#333'), |
| margin=dict(l=0, r=0, t=40, b=0) |
| ) |
| |
| return fig |
| |
| except Exception as e: |
| fig = go.Figure() |
| fig.add_annotation(text=f"Error: {str(e)}", |
| xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False) |
| fig.update_layout(title="Popular Books", height=300) |
| return fig |
| |
| def get_all_members(self): |
| """Get all library members with enhanced formatting""" |
| try: |
| result = self.supabase.table("member").select("*").order("name").execute() |
| df = pd.DataFrame(result.data) |
| if not df.empty: |
| |
| df.index = range(1, len(df) + 1) |
| df.columns = ['π ID', 'π€ Name', 'π§ Email'] |
| return df |
| except Exception as e: |
| return pd.DataFrame([{"β Error": str(e)}]) |
| |
| def get_all_books(self): |
| """Get all books with enhanced formatting""" |
| try: |
| result = self.supabase.table("book").select("*").order("title").execute() |
| df = pd.DataFrame(result.data) |
| if not df.empty: |
| df.index = range(1, len(df) + 1) |
| df.columns = ['π ID', 'π Title', 'βοΈ Author', 'π
Year'] |
| return df |
| except Exception as e: |
| return pd.DataFrame([{"β Error": str(e)}]) |
| |
| def get_current_borrows(self): |
| """Get current borrows with enhanced formatting""" |
| try: |
| |
| borrows = self.supabase.table("borrow").select("*").is_("return_date", "null").execute() |
| |
| if not borrows.data: |
| return pd.DataFrame([{"π Status": "No active borrows"}]) |
| |
| |
| members = self.supabase.table("member").select("*").execute() |
| books = self.supabase.table("book").select("*").execute() |
| |
| |
| member_dict = {m['id']: m for m in members.data} |
| book_dict = {b['id']: b for b in books.data} |
| |
| |
| result_data = [] |
| for borrow in borrows.data: |
| member = member_dict.get(borrow['member_id'], {}) |
| book = book_dict.get(borrow['book_id'], {}) |
| |
| |
| borrow_date = datetime.strptime(borrow['borrow_date'], '%Y-%m-%d').date() |
| days_borrowed = (date.today() - borrow_date).days |
| |
| result_data.append({ |
| 'π Borrow ID': borrow['id'], |
| 'π€ Member': member.get('name', 'Unknown'), |
| 'π§ Email': member.get('email', 'Unknown'), |
| 'π Book': book.get('title', 'Unknown'), |
| 'βοΈ Author': book.get('author', 'Unknown'), |
| 'π
Borrowed Date': borrow['borrow_date'], |
| 'β±οΈ Days': days_borrowed |
| }) |
| |
| df = pd.DataFrame(result_data) |
| df.index = range(1, len(df) + 1) |
| return df |
| |
| except Exception as e: |
| return pd.DataFrame([{"β Error": str(e)}]) |
| |
| def add_member(self, name: str, email: str): |
| """Add a new member with enhanced feedback""" |
| try: |
| if not name or not email: |
| return "β Name and email are required" |
| |
| data = {"name": name, "email": email} |
| result = self.supabase.table("member").insert(data).execute() |
| |
| if result.data: |
| return f"β
Successfully added member: {name}" |
| else: |
| return "β Failed to add member" |
| |
| except Exception as e: |
| if "duplicate key" in str(e): |
| return "β Email already exists" |
| return f"β Error: {str(e)}" |
| |
| def add_book(self, title: str, author: str, year: int): |
| """Add a new book with enhanced feedback""" |
| try: |
| if not title or not author: |
| return "β Title and author are required" |
| |
| if year < 0 or year > datetime.now().year + 1: |
| return "β Invalid year" |
| |
| data = {"title": title, "author": author, "year": year} |
| result = self.supabase.table("book").insert(data).execute() |
| |
| if result.data: |
| return f"β
Successfully added book: {title}" |
| else: |
| return "β Failed to add book" |
| |
| except Exception as e: |
| return f"β Error: {str(e)}" |
| |
| def borrow_book(self, member_id: int, book_id: int): |
| """Create a new borrow record""" |
| try: |
| if not member_id or not book_id: |
| return "β Please select both member and book" |
| |
| |
| existing = self.supabase.table("borrow").select("*").eq("book_id", book_id).is_("return_date", "null").execute() |
| |
| if existing.data: |
| return "β Book is already borrowed and not returned" |
| |
| data = { |
| "member_id": member_id, |
| "book_id": book_id, |
| "borrow_date": date.today().isoformat() |
| } |
| |
| result = self.supabase.table("borrow").insert(data).execute() |
| |
| if result.data: |
| return f"β
Book borrowed successfully! π" |
| else: |
| return "β Failed to create borrow record" |
| |
| except Exception as e: |
| return f"β Error: {str(e)}" |
| |
| def return_book(self, borrow_id: int): |
| """Return a borrowed book""" |
| try: |
| if not borrow_id: |
| return "β Please select a borrow record" |
| |
| data = {"return_date": date.today().isoformat()} |
| result = self.supabase.table("borrow").update(data).eq("id", borrow_id).execute() |
| |
| if result.data: |
| return f"β
Book returned successfully! πβ‘οΈ" |
| else: |
| return "β Failed to return book" |
| |
| except Exception as e: |
| return f"β Error: {str(e)}" |
| |
| def get_member_choices(self): |
| """Get member choices for dropdown""" |
| try: |
| result = self.supabase.table("member").select("id, name").order("name").execute() |
| return [(f"π€ {m['name']} (ID: {m['id']})", m['id']) for m in result.data] |
| except: |
| return [("No members found", 0)] |
| |
| def get_book_choices(self): |
| """Get book choices for dropdown""" |
| try: |
| result = self.supabase.table("book").select("id, title, author").order("title").execute() |
| return [(f"π {b['title']} by {b['author']} (ID: {b['id']})", b['id']) for b in result.data] |
| except: |
| return [("No books found", 0)] |
| |
| def get_borrow_choices(self): |
| """Get active borrow choices for dropdown""" |
| try: |
| borrows = self.supabase.table("borrow").select("*").is_("return_date", "null").execute() |
| |
| if not borrows.data: |
| return [("No active borrows", 0)] |
| |
| members = self.supabase.table("member").select("*").execute() |
| books = self.supabase.table("book").select("*").execute() |
| |
| member_dict = {m['id']: m for m in members.data} |
| book_dict = {b['id']: b for b in books.data} |
| |
| choices = [] |
| for borrow in borrows.data: |
| member = member_dict.get(borrow['member_id'], {}) |
| book = book_dict.get(borrow['book_id'], {}) |
| |
| label = f"π {member.get('name', 'Unknown')} - {book.get('title', 'Unknown')} (ID: {borrow['id']})" |
| choices.append((label, borrow['id'])) |
| |
| return choices |
| except: |
| return [("Error loading borrows", 0)] |
|
|
| |
| library = ModernLibraryManagement() |
|
|
| |
| def refresh_dashboard(): |
| """Refresh dashboard with stats and charts""" |
| members_count, books_count, active_borrows = library.get_dashboard_stats() |
| borrowing_chart = library.get_borrowing_chart() |
| popular_books_chart = library.get_popular_books_chart() |
| |
| return ( |
| gr.HTML(f'<div class="stats-card"><div class="stats-number">{members_count}</div><div class="stats-label">π₯ Members</div></div>'), |
| gr.HTML(f'<div class="stats-card"><div class="stats-number">{books_count}</div><div class="stats-label">π Books</div></div>'), |
| gr.HTML(f'<div class="stats-card"><div class="stats-number">{active_borrows}</div><div class="stats-label">π Active Loans</div></div>'), |
| borrowing_chart, |
| popular_books_chart |
| ) |
|
|
| def refresh_all_data(): |
| """Refresh all data displays""" |
| return ( |
| library.get_all_members(), |
| library.get_all_books(), |
| library.get_current_borrows() |
| ) |
|
|
| def add_new_member(name, email): |
| status = library.add_member(name, email) |
| members_df = library.get_all_members() |
| return status, members_df, "", "" |
|
|
| def add_new_book(title, author, year): |
| status = library.add_book(title, author, year) |
| books_df = library.get_all_books() |
| return status, books_df, "", "", 2024 |
|
|
| def create_borrow(member_choice, book_choice): |
| member_id = member_choice if isinstance(member_choice, int) else 0 |
| book_id = book_choice if isinstance(book_choice, int) else 0 |
| status = library.borrow_book(member_id, book_id) |
| borrows_df = library.get_current_borrows() |
| return status, borrows_df |
|
|
| def process_return(borrow_choice): |
| borrow_id = borrow_choice if isinstance(borrow_choice, int) else 0 |
| status = library.return_book(borrow_id) |
| borrows_df = library.get_current_borrows() |
| return status, borrows_df |
|
|
| |
| with gr.Blocks(title="ποΈ Modern Library Management System", css=CUSTOM_CSS, theme=gr.themes.Soft()) as app: |
| |
| |
| gr.HTML('<h1 class="main-header">ποΈ Modern Library Management System</h1>') |
| gr.HTML(f'<div style="text-align: center; color: #666; margin-bottom: 30px; font-size: 1.2em;">π‘ Status: {library.connection_status}</div>') |
| |
| with gr.Tabs(): |
| |
| with gr.Tab("π Dashboard"): |
| gr.HTML('<div class="card">') |
| gr.Markdown("### π Library Overview") |
| |
| with gr.Row(): |
| members_stat = gr.HTML('<div class="stats-card"><div class="stats-number">0</div><div class="stats-label">π₯ Members</div></div>') |
| books_stat = gr.HTML('<div class="stats-card"><div class="stats-number">0</div><div class="stats-label">π Books</div></div>') |
| borrows_stat = gr.HTML('<div class="stats-card"><div class="stats-number">0</div><div class="stats-label">π Active Loans</div></div>') |
| |
| refresh_dashboard_btn = gr.Button("π Refresh Dashboard", variant="primary", size="lg") |
| |
| with gr.Row(): |
| with gr.Column(): |
| borrowing_chart = gr.Plot(label="π Borrowing Trends") |
| with gr.Column(): |
| popular_books_chart = gr.Plot(label="π Popular Books") |
| |
| gr.HTML('</div>') |
| |
| |
| app.load(refresh_dashboard, outputs=[members_stat, books_stat, borrows_stat, borrowing_chart, popular_books_chart]) |
| refresh_dashboard_btn.click(refresh_dashboard, outputs=[members_stat, books_stat, borrows_stat, borrowing_chart, popular_books_chart]) |
| |
| |
| with gr.Tab("π₯ Members Management"): |
| gr.HTML('<div class="card">') |
| gr.Markdown("### π₯ Library Members") |
| |
| refresh_members_btn = gr.Button("π Refresh Members", variant="secondary") |
| members_table = gr.Dataframe(label="π All Members", interactive=False) |
| |
| gr.HTML('<div class="form-container">') |
| gr.Markdown("#### β Add New Member") |
| |
| with gr.Row(): |
| member_name = gr.Textbox(label="π€ Name", placeholder="Enter member name", scale=2) |
| member_email = gr.Textbox(label="π§ Email", placeholder="Enter email address", scale=2) |
| add_member_btn = gr.Button("β Add Member", variant="primary", scale=1) |
| |
| member_status = gr.Textbox(label="π Status", interactive=False) |
| gr.HTML('</div>') |
| gr.HTML('</div>') |
| |
| |
| app.load(library.get_all_members, outputs=members_table) |
| refresh_members_btn.click(library.get_all_members, outputs=members_table) |
| add_member_btn.click( |
| add_new_member, |
| inputs=[member_name, member_email], |
| outputs=[member_status, members_table, member_name, member_email] |
| ) |
| |
| |
| with gr.Tab("π Books Catalog"): |
| gr.HTML('<div class="card">') |
| gr.Markdown("### π Library Books") |
| |
| refresh_books_btn = gr.Button("π Refresh Books", variant="secondary") |
| books_table = gr.Dataframe(label="π All Books", interactive=False) |
| |
| gr.HTML('<div class="form-container">') |
| gr.Markdown("#### β Add New Book") |
| |
| with gr.Row(): |
| book_title = gr.Textbox(label="π Title", placeholder="Enter book title", scale=2) |
| book_author = gr.Textbox(label="βοΈ Author", placeholder="Enter author name", scale=2) |
| |
| with gr.Row(): |
| book_year = gr.Number(label="π
Year", value=2024, minimum=1900, maximum=2030, scale=2) |
| add_book_btn = gr.Button("β Add Book", variant="primary", scale=1) |
| |
| book_status = gr.Textbox(label="π Status", interactive=False) |
| gr.HTML('</div>') |
| gr.HTML('</div>') |
| |
| |
| app.load(library.get_all_books, outputs=books_table) |
| refresh_books_btn.click(library.get_all_books, outputs=books_table) |
| add_book_btn.click( |
| add_new_book, |
| inputs=[book_title, book_author, book_year], |
| outputs=[book_status, books_table, book_title, book_author, book_year] |
| ) |
| |
| |
| with gr.Tab("π Borrowing Management"): |
| gr.HTML('<div class="card">') |
| gr.Markdown("### π Current Borrowings") |
| |
| refresh_borrows_btn = gr.Button("π Refresh Borrowings", variant="secondary") |
| borrows_table = gr.Dataframe(label="π Active Borrowings", interactive=False) |
| |
| gr.HTML('<div class="form-container">') |
| gr.Markdown("#### π Borrow a Book") |
| |
| with gr.Row(): |
| member_dropdown = gr.Dropdown( |
| choices=library.get_member_choices(), |
| label="π€ Select Member", |
| scale=2 |
| ) |
| book_dropdown = gr.Dropdown( |
| choices=library.get_book_choices(), |
| label="π Select Book", |
| scale=2 |
| ) |
| refresh_dropdowns_btn = gr.Button("π", size="sm", scale=1) |
| |
| borrow_btn = gr.Button("π Borrow Book", variant="primary", size="lg") |
| borrow_status = gr.Textbox(label="π Borrow Status", interactive=False) |
| gr.HTML('</div>') |
| |
| gr.HTML('<div class="form-container">') |
| gr.Markdown("#### β©οΈ Return a Book") |
| |
| with gr.Row(): |
| borrow_dropdown = gr.Dropdown( |
| choices=library.get_borrow_choices(), |
| label="π Select Book to Return", |
| scale=2 |
| ) |
| refresh_return_btn = gr.Button("π", size="sm", scale=1) |
| |
| return_btn = gr.Button("β©οΈ Return Book", variant="secondary", size="lg") |
| return_status = gr.Textbox(label="π Return Status", interactive=False) |
| gr.HTML('</div>') |
| gr.HTML('</div>') |
| |
| |
| app.load(library.get_current_borrows, outputs=borrows_table) |
| refresh_borrows_btn.click(library.get_current_borrows, outputs=borrows_table) |
| |
| refresh_dropdowns_btn.click( |
| lambda: [gr.Dropdown(choices=library.get_member_choices()), |
| gr.Dropdown(choices=library.get_book_choices())], |
| outputs=[member_dropdown, book_dropdown] |
| ) |
| |
| refresh_return_btn.click( |
| lambda: gr.Dropdown(choices=library.get_borrow_choices()), |
| outputs=borrow_dropdown |
| ) |
| |
| borrow_btn.click( |
| create_borrow, |
| inputs=[member_dropdown, book_dropdown], |
| outputs=[borrow_status, borrows_table] |
| ) |
| |
| return_btn.click( |
| process_return, |
| inputs=[borrow_dropdown], |
| outputs=[return_status, borrows_table] |
| ) |
| |
| |
| with gr.Tab("π Analytics & Reports"): |
| gr.HTML('<div class="card">') |
| gr.Markdown("### π Library Analytics") |
| |
| with gr.Row(): |
| analytics_refresh_btn = gr.Button("π Refresh Analytics", variant="primary") |
| |
| with gr.Row(): |
| analytics_borrowing_chart = gr.Plot(label="π Detailed Borrowing Trends") |
| analytics_popular_chart = gr.Plot(label="π Book Popularity Rankings") |
| |
| gr.Markdown("### π Quick Stats") |
| with gr.Row(): |
| all_members_df = gr.Dataframe(label="π₯ All Members", interactive=False) |
| all_books_df = gr.Dataframe(label="π All Books", interactive=False) |
| |
| gr.HTML('</div>') |
| |
| analytics_refresh_btn.click( |
| lambda: [library.get_borrowing_chart(), library.get_popular_books_chart(), |
| library.get_all_members(), library.get_all_books()], |
| outputs=[analytics_borrowing_chart, analytics_popular_chart, all_members_df, all_books_df] |
| ) |
| |
| |
| gr.HTML(''' |
| <div style="text-align: center; margin-top: 40px; padding: 20px; |
| background: rgba(255,255,255,0.1); border-radius: 15px; |
| color: #666;"> |
| <p><strong>ποΈ Modern Library Management System</strong></p> |
| <p>Built with β€οΈ using Gradio & Supabase | Real-time Database Integration</p> |
| </div> |
| ''') |
|
|
| |
| if __name__ == "__main__": |
| app.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| show_error=True |
| ) |
|
|