"""Roster view page - search and view artist rosters.""" import streamlit as st from snowflake.snowpark.session import Session from common import queries from common.db import get_session from common.services import delete_roster_entry, get_all_roster_vendors, get_roster_view from common.types import RosterFilters, VendorWithBrandRow from common.utils import format_datetime, format_roster_type_display, init_session_state # Custom CSS to reduce spacing st.markdown( """ """, unsafe_allow_html=True, ) st.title("📋 Artist Roster - View") st.write("Search and view artists across all roster tables") # Get session session = get_session() # Show schema indicator st.info(f"📊 Using schema: {queries.SCHEMA_NAME}") # Initialize session state init_session_state("roster_vendor_id", None) init_session_state("roster_vendor_name", "") init_session_state("roster_artist_uuid", None) init_session_state("roster_artist_name", "") init_session_state("roster_subaccount_id", None) init_session_state("roster_current_page", 0) init_session_state("roster_results", None) init_session_state("pending_delete", None) init_session_state("delete_success", None) init_session_state("delete_error", None) st.divider() # Search filters st.subheader("Search Filters") col1, col2 = st.columns(2) with col1: st.write("**Vendor Search**") # Cache vendor list @st.cache_data(ttl=3600, show_spinner="Loading vendors...") def load_vendors(_session: Session) -> list[VendorWithBrandRow]: return get_all_roster_vendors(_session) vendors = load_vendors(session) vendor_options = [(None, "All Vendors")] + [ (v.vendor_id, v.get_display_text()) for v in vendors ] selected_vendor_id = st.selectbox( "Select Vendor", options=[v[0] for v in vendor_options], format_func=lambda x: next( (v[1] for v in vendor_options if v[0] == x), "All Vendors" ), help="Filter by vendor (includes brand information)", key="vendor_dropdown", ) with col2: st.write("**Artist Search**") artist_uuid_input = st.text_input( "Artist UUID (exact match)", key="artist_uuid_input", help="Enter exact artist UUID", ) artist_name_input = st.text_input( "Artist Name (partial match)", key="artist_name_input", help="Enter partial artist name", ) subaccount_id_input = st.text_input( "Subaccount ID (optional)", key="subaccount_id_input", help="Enter subaccount ID to filter", ) col1, col2, col3 = st.columns([1, 1, 4]) with col1: if st.button("🔍 Search", type="primary"): # Reset pagination when new search st.session_state.roster_current_page = 0 # Parse inputs - use dropdown selection vendor_id = selected_vendor_id vendor_name = None # Not used with dropdown artist_uuid = artist_uuid_input.strip() if artist_uuid_input.strip() else None artist_name = artist_name_input.strip() if artist_name_input.strip() else None subaccount_id = ( int(subaccount_id_input) if subaccount_id_input.strip() else None ) # Store filters st.session_state.roster_vendor_id = vendor_id st.session_state.roster_vendor_name = vendor_name st.session_state.roster_artist_uuid = artist_uuid st.session_state.roster_artist_name = artist_name st.session_state.roster_subaccount_id = subaccount_id st.rerun() with col2: if st.button("🔄 Clear"): # Clear all filters st.session_state.roster_vendor_id = None st.session_state.roster_vendor_name = "" st.session_state.roster_artist_uuid = None st.session_state.roster_artist_name = "" st.session_state.roster_subaccount_id = None st.session_state.roster_current_page = 0 st.session_state.roster_results = None st.rerun() st.divider() # Display results if any( [ st.session_state.roster_vendor_id, st.session_state.roster_vendor_name, st.session_state.roster_artist_uuid, st.session_state.roster_artist_name, st.session_state.roster_subaccount_id, ] ): with st.spinner("Loading roster data..."): # Create pagination page_size = 100 offset = st.session_state.roster_current_page * page_size # Create filters filters = RosterFilters( vendor_id=st.session_state.roster_vendor_id, vendor_name=st.session_state.roster_vendor_name, artist_uuid=st.session_state.roster_artist_uuid, artist_name=st.session_state.roster_artist_name, subaccount_id=st.session_state.roster_subaccount_id, limit=page_size, offset=offset, ) # Execute query try: df = get_roster_view(session, filters) # Convert numeric IDs to strings to prevent comma formatting if not df.empty: # Check for column names (Snowflake returns uppercase) vendor_col = "VENDOR_ID" if "VENDOR_ID" in df.columns else "vendor_id" subaccount_col = ( "SUBACCOUNT_ID" if "SUBACCOUNT_ID" in df.columns else "subaccount_id" ) df[vendor_col] = df[vendor_col].astype(str) df[subaccount_col] = df[subaccount_col].astype(str) st.session_state.roster_results = df if df.empty: st.warning("No results found. Try adjusting your search criteria.") else: st.success(f"Found {len(df)} result(s)") # Pagination controls total_pages = ( st.session_state.roster_current_page + 1 if len(df) == page_size else st.session_state.roster_current_page + 1 ) col1, col2, col3 = st.columns([1, 3, 1]) with col1: if st.button( "← Previous", disabled=st.session_state.roster_current_page == 0 ): st.session_state.roster_current_page -= 1 st.rerun() with col2: st.write( f"Page {st.session_state.roster_current_page + 1} " f"(Showing up to {page_size} rows per page)" ) with col3: if st.button("Next →", disabled=len(df) < page_size): st.session_state.roster_current_page += 1 st.rerun() # Display results table with delete buttons # Header row header_cols = st.columns([1, 1.5, 1, 2, 2, 1, 1, 1.5, 1.2, 1.5, 1]) headers = [ "Vendor ID", "Vendor Name", "Brand", "Artist UUID", "Artist Name", "Rep Type", "Status", "Countries", "Is Artist Team", "Created At", "Actions", ] for col, header in zip(header_cols, headers): col.markdown(f"**{header}**") st.divider() # Data rows for idx, row in df.iterrows(): cols = st.columns([1, 1.5, 1, 2, 2, 1, 1, 1.5, 1.2, 1.5, 1]) # Extract values (handle uppercase column names from Snowflake) vendor_id = row.get("VENDOR_ID", row.get("vendor_id")) vendor_name = row.get("VENDOR_NAME", row.get("vendor_name", "")) brand_name = row.get("BRAND_NAME", row.get("brand_name", "")) artist_uuid = row.get("ARTIST_UUID", row.get("artist_uuid", "")) artist_name = row.get("ARTIST_NAME", row.get("artist_name", "")) subaccount_id = row.get("SUBACCOUNT_ID", row.get("subaccount_id")) rep_type = row.get("REP_TYPE", row.get("rep_type", "")) main_status = row.get("MAIN_STATUS", row.get("main_status", "")) country_codes = row.get( "COUNTRY_CODES", row.get("country_codes", "") ) is_artist_team = row.get( "IS_ARTIST_TEAM", row.get("is_artist_team", False) ) created_at = row.get("CREATED_AT", row.get("created_at", None)) # Display data cols[0].write(str(vendor_id) if vendor_id else "") cols[1].write(vendor_name or "") cols[2].write(brand_name or "") cols[3].write(artist_uuid) cols[4].write(artist_name or "") cols[5].write(format_roster_type_display(rep_type) if rep_type else "-") cols[6].write(main_status or "-") cols[7].write(country_codes or "-") # Is Artist Team with tooltip with cols[8]: if is_artist_team: st.markdown( "✓", help="This field is only applicable for Rep Owner roster type", ) else: st.write("") cols[9].write(format_datetime(created_at)) # Actions column - delete buttons with cols[10]: # Create unique key for this row row_key = f"{vendor_id}_{artist_uuid}_{subaccount_id}_{idx}" if rep_type == "LOCAL" and country_codes: # Parse countries countries_list = [ c.strip() for c in str(country_codes).split(",") ] if len(countries_list) > 1: # Multiple countries - show two buttons col_a, col_b = st.columns(2) with col_a: if st.button( "🗑️", key=f"delete_all_{row_key}", help="Delete all countries", use_container_width=True, ): st.session_state.pending_delete = { "vendor_id": int(vendor_id) if vendor_id else 0, "artist_uuid": artist_uuid, "subaccount_id": int(subaccount_id) if subaccount_id else 0, "roster_type": rep_type, "delete_all_countries": True, "countries": country_codes, "artist_name": artist_name, "vendor_name": vendor_name, } st.rerun() # Country selector in popover with col_b: with st.popover( "▼", use_container_width=True, help="Select specific country", ): st.write("**Delete specific country:**") for country in countries_list: if st.button( f"Delete {country}", key=f"delete_{country}_{row_key}", use_container_width=True, ): st.session_state.pending_delete = { "vendor_id": int(vendor_id) if vendor_id else 0, "artist_uuid": artist_uuid, "subaccount_id": int(subaccount_id) if subaccount_id else 0, "roster_type": rep_type, "delete_all_countries": False, "country_code": country, "artist_name": artist_name, "vendor_name": vendor_name, } st.rerun() else: # Single country - simple delete if st.button( "🗑️", key=f"delete_{row_key}", help="Delete this roster entry", use_container_width=True, ): st.session_state.pending_delete = { "vendor_id": int(vendor_id) if vendor_id else 0, "artist_uuid": artist_uuid, "subaccount_id": int(subaccount_id) if subaccount_id else 0, "roster_type": rep_type, "delete_all_countries": False, "country_code": countries_list[0], "artist_name": artist_name, "vendor_name": vendor_name, } st.rerun() else: # MAIN_REP or ARTIST_ROSTER - simple delete if st.button( "🗑️", key=f"delete_{row_key}", help="Delete this roster entry", use_container_width=True, ): st.session_state.pending_delete = { "vendor_id": int(vendor_id) if vendor_id else 0, "artist_uuid": artist_uuid, "subaccount_id": int(subaccount_id) if subaccount_id else 0, "roster_type": rep_type, "delete_all_countries": False, "artist_name": artist_name, "vendor_name": vendor_name, } st.rerun() # Compact divider st.markdown("---") except Exception as e: st.error(f"Error loading roster data: {str(e)}") st.exception(e) else: st.info( "👆 Enter search criteria above and click **Search** to view roster entries" ) # Confirmation dialog for deletion if st.session_state.pending_delete: delete_data = st.session_state.pending_delete @st.dialog("⚠️ Confirm Deletion") def show_delete_confirmation() -> None: st.warning("**Are you sure you want to delete this roster entry?**") # Show details st.write("**Details:**") st.write(f"- Artist: {delete_data.get('artist_name', 'N/A')}") st.write(f"- Vendor: {delete_data.get('vendor_name', 'N/A')}") st.write(f"- Vendor ID: {delete_data['vendor_id']}") st.write(f"- Subaccount ID: {delete_data['subaccount_id']}") roster_type_display = format_roster_type_display(delete_data['roster_type']) st.write(f"- Roster Type: {roster_type_display}") if delete_data.get("delete_all_countries"): st.write(f"- **Deleting ALL countries:** {delete_data.get('countries')}") elif delete_data.get("country_code"): st.write(f"- Country: {delete_data['country_code']}") st.divider() col1, col2 = st.columns(2) with col1: if st.button("✅ Yes, Delete", type="primary", use_container_width=True): try: # Execute delete delete_roster_entry( session=session, vendor_id=delete_data["vendor_id"], artist_uuid=delete_data["artist_uuid"], subaccount_id=delete_data["subaccount_id"], roster_type=delete_data["roster_type"], country_code=delete_data.get("country_code"), delete_all_countries=delete_data.get( "delete_all_countries", False ), ) st.session_state.delete_success = ( "✅ Roster entry deleted successfully!" ) st.session_state.pending_delete = None st.rerun() except Exception as e: st.session_state.delete_error = ( f"❌ Error deleting roster entry: {str(e)}" ) st.session_state.pending_delete = None st.rerun() with col2: if st.button("❌ Cancel", use_container_width=True): st.session_state.pending_delete = None st.rerun() show_delete_confirmation() # Show success/error messages if st.session_state.delete_success: st.success(st.session_state.delete_success) st.session_state.delete_success = None if st.session_state.delete_error: st.error(st.session_state.delete_error) st.session_state.delete_error = None