import dash
from dash import dcc, html, Input, Output, State, dash_table
import plotly.express as px
import pandas as pd
import numpy as np
# Initialize the Dash app
app = dash.Dash(__name__)
# Custom CSS for dark theme
app.index_string = '''
<!DOCTYPE html>
<html>
<head>
{%metas%}
<title>{%title%}</title>
{%favicon%}
{%css%}
<style>
body {
background-color: #1a1d24;
color: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 20px;
}
.dash-table-container {
background-color: #2a2e35;
border-radius: 8px;
padding: 15px;
}
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th {
background-color: #2a2e35;
color: #ffffff;
border-color: #3a3f48;
}
.dash-dropdown .Select-control {
background-color: #2a2e35;
border-color: #3a3f48;
color: #ffffff;
}
.dash-dropdown .Select-menu-outer {
background-color: #2a2e35;
border-color: #3a3f48;
}
.dash-dropdown .Select-option {
background-color: #2a2e35;
color: #ffffff;
}
.dash-dropdown .Select-option:hover {
background-color: #3a3f48;
}
input {
background-color: #2a2e35;
border: 1px solid #3a3f48;
color: #ffffff;
border-radius: 4px;
padding: 8px;
}
button {
background-color: #4287f5;
border: none;
color: white;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
button:hover {
background-color: #2d5cb8;
}
.card {
background-color: #2a2e35;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
h1, h3 {
color: #ffffff;
margin-bottom: 20px;
}
.label {
color: #a0aec0;
margin-bottom: 8px;
font-size: 14px;
}
</style>
</head>
<body>
{%app_entry%}
<footer>
{%config%}
{%scripts%}
{%renderer%}
</footer>
</body>
</html>
'''
# Initial data tables
cost_data = pd.DataFrame({
'item': ['apple', 'orange'],
'cost': [1, 2]
})
price_data = pd.DataFrame({
'item': ['apple', 'orange'],
'price': [40, 45]
})
# Store for results
initial_results = pd.DataFrame(columns=['item', 'discount', 'rebate', 'final_sales', 'profit', 'offer_name'])
# App layout
app.layout = html.Div([
html.H1('Offer Analysis Dashboard', style={'padding': '20px 0'}),
# Data Tables Display
html.Div([
html.Div([
html.H3('Reference Data', style={'margin-bottom': '20px'}),
html.Div([
html.Div([
html.H3('Cost Table', style={'font-size': '16px'}),
dash_table.DataTable(
id='cost-table',
data=cost_data.to_dict('records'),
columns=[{'name': i.capitalize(), 'id': i} for i in cost_data.columns],
style_table={'backgroundColor': '#2a2e35'},
style_header={
'backgroundColor': '#3a3f48',
'color': 'white',
'fontWeight': 'bold',
'textAlign': 'left',
'padding': '12px'
},
style_cell={
'backgroundColor': '#2a2e35',
'color': 'white',
'textAlign': 'left',
'padding': '12px',
'fontFamily': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto'
}
),
], className='card', style={'flex': 1, 'margin-right': '20px'}),
html.Div([
html.H3('Original Price Table', style={'font-size': '16px'}),
dash_table.DataTable(
id='price-table',
data=price_data.to_dict('records'),
columns=[{'name': i.capitalize(), 'id': i} for i in price_data.columns],
style_table={'backgroundColor': '#2a2e35'},
style_header={
'backgroundColor': '#3a3f48',
'color': 'white',
'fontWeight': 'bold',
'textAlign': 'left',
'padding': '12px'
},
style_cell={
'backgroundColor': '#2a2e35',
'color': 'white',
'textAlign': 'left',
'padding': '12px',
'fontFamily': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto'
}
),
], className='card', style={'flex': 1}),
], style={'display': 'flex', 'gap': '20px'}),
], className='card'),
# Input Form
html.Div([
html.H3('Create New Offer', style={'margin-bottom': '20px'}),
html.Div([
html.Label('Choose Item:', className='label'),
dcc.Dropdown(
id='item-dropdown',
options=[{'label': i.capitalize(), 'value': i} for i in cost_data['item']],
value=cost_data['item'].iloc[0],
className='dash-dropdown'
),
], style={'marginBottom': '20px'}),
html.Div([
html.Label('Discount (0.8-1.0):', className='label'),
dcc.Input(
id='discount-input',
type='number',
min=0.8,
max=1,
step=0.01,
value=0.9
),
], style={'marginBottom': '20px'}),
html.Div([
html.Label('Rebate ($):', className='label'),
dcc.Input(
id='rebate-input',
type='number',
min=0,
max=5,
step=0.5,
value=1
),
], style={'marginBottom': '20px'}),
html.Div([
html.Label('Offer Name:', className='label'),
dcc.Input(
id='offer-name',
type='text',
value='Offer 1'
),
], style={'marginBottom': '20px'}),
html.Button('Calculate Results', id='submit-button', n_clicks=0),
dcc.Store(id='results-store', data=initial_results.to_dict('records')),
], className='card'),
# Results Section
html.Div([
html.H3('Results', style={'margin-bottom': '20px'}),
dash_table.DataTable(
id='results-table',
columns=[
{'name': i, 'id': i.lower().replace(' ', '_')} for i in [
'Item', 'Discount', 'Rebate', 'Final Sales', 'Profit', 'Offer Name'
]
],
data=[],
style_table={'backgroundColor': '#2a2e35'},
style_header={
'backgroundColor': '#3a3f48',
'color': 'white',
'fontWeight': 'bold',
'textAlign': 'left',
'padding': '12px'
},
style_cell={
'backgroundColor': '#2a2e35',
'color': 'white',
'textAlign': 'left',
'padding': '12px',
'fontFamily': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto'
}
),
], className='card'),
# Visualization
html.Div([
html.H3('Profit Visualization', style={'margin-bottom': '20px'}),
dcc.Graph(
id='profit-chart',
config={'displayModeBar': False},
style={'backgroundColor': '#2a2e35'}
)
], className='card'),
], style={'maxWidth': '1200px', 'margin': '0 auto'}),
])
@app.callback(
[Output('results-store', 'data'),
Output('results-table', 'data'),
Output('profit-chart', 'figure')],
Input('submit-button', 'n_clicks'),
[State('item-dropdown', 'value'),
State('discount-input', 'value'),
State('rebate-input', 'value'),
State('offer-name', 'value'),
State('results-store', 'data')],
prevent_initial_call=True
)
def update_results(n_clicks, item, discount, rebate, offer_name, current_results):
if n_clicks is None or item is None:
raise dash.exceptions.PreventUpdate
# Input validation
if not (0.8 <= float(discount) <= 1.0):
raise dash.exceptions.PreventUpdate
if not (0 <= float(rebate) <= 5):
raise dash.exceptions.PreventUpdate
# Get item cost and price
item_cost = float(cost_data[cost_data['item'] == item]['cost'].iloc[0])
item_price = float(price_data[price_data['item'] == item]['price'].iloc[0])
# Calculate results
final_sales = item_price * discount - rebate
profit = final_sales - item_cost
# Create new result
new_result = {
'item': item.capitalize(),
'discount': round(discount, 2),
'rebate': round(rebate, 2),
'final_sales': round(final_sales, 2),
'profit': round(profit, 2),
'offer_name': offer_name
}
# Update results
if current_results is None:
current_results = []
updated_results = current_results + [new_result]
# Create visualization
results_df = pd.DataFrame(updated_results)
fig = px.bar(
results_df,
x='offer_name',
y='profit',
color='item',
title='Profit by Offer',
template='plotly_dark',
color_discrete_sequence=['#4287f5', '#f542a7']
)
fig.update_layout(
plot_bgcolor='#2a2e35',
paper_bgcolor='#2a2e35',
font_color='#ffffff',
title_font_color='#ffffff',
xaxis_title="Offer Name",
yaxis_title="Profit ($)",
bargap=0.2,
margin=dict(t=40, r=20, b=40, l=20)
)
fig.update_xaxes(showgrid=False)
fig.update_yaxes(showgrid=True, gridcolor='#3a3f48')
return updated_results, updated_results, fig
if __name__ == '__main__':
app.run_server(debug=True)