Update app.py
Browse files
app.py
CHANGED
|
@@ -1110,75 +1110,19 @@ def batch_update_cells_with_tracking(sheet, changes, df, editor_email, log_sheet
|
|
| 1110 |
else:
|
| 1111 |
return False, f"❌ Fel vid sparande: {error_msg}. Kontakta migration@chargenode.eu om problemet kvarstår."
|
| 1112 |
|
| 1113 |
-
def calculate_completion_stats(df, editable_cols):
|
| 1114 |
-
"""Beräkna kompletteringsstatistik för editerbara kolumner"""
|
| 1115 |
-
stats = {}
|
| 1116 |
-
|
| 1117 |
-
for col in editable_cols:
|
| 1118 |
-
if col in df.columns:
|
| 1119 |
-
total = len(df)
|
| 1120 |
-
# Räkna celler som har värde (inte nan, inte tom sträng)
|
| 1121 |
-
filled = df[col].apply(lambda x: bool(x and str(x).strip() != '')).sum()
|
| 1122 |
-
stats[col] = {
|
| 1123 |
-
'filled': filled,
|
| 1124 |
-
'total': total,
|
| 1125 |
-
'percentage': (filled / total * 100) if total > 0 else 0
|
| 1126 |
-
}
|
| 1127 |
-
|
| 1128 |
-
# Totala stats
|
| 1129 |
-
total_cells = sum(s['total'] for s in stats.values())
|
| 1130 |
-
total_filled = sum(s['filled'] for s in stats.values())
|
| 1131 |
-
overall_percentage = (total_filled / total_cells * 100) if total_cells > 0 else 0
|
| 1132 |
-
|
| 1133 |
-
return {
|
| 1134 |
-
'by_column': stats,
|
| 1135 |
-
'overall': {
|
| 1136 |
-
'filled': total_filled,
|
| 1137 |
-
'total': total_cells,
|
| 1138 |
-
'percentage': overall_percentage
|
| 1139 |
-
}
|
| 1140 |
-
}
|
| 1141 |
-
|
| 1142 |
-
def get_incomplete_rows(df, editable_cols):
|
| 1143 |
-
"""Hitta rader där minst ett editerbart fält är tomt"""
|
| 1144 |
-
incomplete_mask = pd.Series([False] * len(df), index=df.index)
|
| 1145 |
-
|
| 1146 |
-
for col in editable_cols:
|
| 1147 |
-
if col in df.columns:
|
| 1148 |
-
# Markera rader där cellen är tom
|
| 1149 |
-
empty_mask = df[col].apply(lambda x: not bool(x and str(x).strip() != ''))
|
| 1150 |
-
incomplete_mask = incomplete_mask | empty_mask
|
| 1151 |
-
|
| 1152 |
-
return incomplete_mask
|
| 1153 |
-
|
| 1154 |
-
def style_empty_cells(df, editable_cols):
|
| 1155 |
-
"""Returnera styling-funktion för att markera tomma celler"""
|
| 1156 |
-
def highlight_empty(row):
|
| 1157 |
-
styles = [''] * len(row)
|
| 1158 |
-
for idx, (col_name, value) in enumerate(row.items()):
|
| 1159 |
-
if col_name in editable_cols:
|
| 1160 |
-
# Markera om värdet är tomt eller None
|
| 1161 |
-
if pd.isna(value) or str(value).strip() == '' or str(value).strip() == '0':
|
| 1162 |
-
styles[idx] = 'background-color: #FFE5E5; border: 2px solid #DC3545;'
|
| 1163 |
-
return styles
|
| 1164 |
-
|
| 1165 |
-
return highlight_empty
|
| 1166 |
-
|
| 1167 |
def get_column_config(df):
|
| 1168 |
"""Generera kolumnkonfiguration för data_editor med beskrivningar och dropdowns"""
|
| 1169 |
config = {}
|
| 1170 |
|
| 1171 |
-
# Beskrivningar för editerbara kolumner
|
| 1172 |
-
base_suffix = "\n\n🔺 Vänligen fyll i detta fält. Data uppdateras automatiskt."
|
| 1173 |
-
|
| 1174 |
editable_descriptions = {
|
| 1175 |
-
'Namn': 'Huvudkontaktperson för detta område. Personen vi kontaktar vid frågor eller problem.'
|
| 1176 |
-
'Email adress': 'Ange individuell e-postadress till kontaktpersonen för detta område.'
|
| 1177 |
-
'Telefon': 'Telefonnummer till huvudkontaktpersonen. Format: +46XXXXXXXXX eller 07X-XXXXXXX'
|
| 1178 |
-
'Tillgänglighet': 'Välj i dropdown-menyn om detta är ett område som vi alltid kan komma åt, eller om det är stängt. Om det är stängt var vänlig fyll i fältet Access info.'
|
| 1179 |
-
'Access info': 'Om området inte alltid är tillgängligt, vänligen ange hur vi kan få tillgång. T.ex. "Nycklar finns i receptionen" eller "Ring Pelle på 07XXXXXXXX när ni är på gång".'
|
| 1180 |
-
'Fritext meddelande': 'Övrig information eller meddelanden som är viktiga för oss att veta.'
|
| 1181 |
-
'Main Contact': 'Ange om denna person är huvudansvarig för kontot. Välj "Huvudkontakt" för helst endast en person per företag.'
|
| 1182 |
}
|
| 1183 |
|
| 1184 |
# Beskrivningar för readonly kolumner
|
|
@@ -1202,7 +1146,7 @@ def get_column_config(df):
|
|
| 1202 |
if col == 'Tillgänglighet':
|
| 1203 |
# Dropdown för tillgänglighet
|
| 1204 |
config[col] = st.column_config.SelectboxColumn(
|
| 1205 |
-
|
| 1206 |
help=editable_descriptions.get(col, ''),
|
| 1207 |
width="medium",
|
| 1208 |
options=[
|
|
@@ -1214,7 +1158,7 @@ def get_column_config(df):
|
|
| 1214 |
elif col == 'Main Contact':
|
| 1215 |
# Dropdown för huvudkontakt
|
| 1216 |
config[col] = st.column_config.SelectboxColumn(
|
| 1217 |
-
|
| 1218 |
help=editable_descriptions.get(col, ''),
|
| 1219 |
width="medium",
|
| 1220 |
options=[
|
|
@@ -1226,7 +1170,7 @@ def get_column_config(df):
|
|
| 1226 |
else:
|
| 1227 |
# Vanlig textkolumn
|
| 1228 |
config[col] = st.column_config.TextColumn(
|
| 1229 |
-
|
| 1230 |
help=editable_descriptions.get(col, ''),
|
| 1231 |
width="medium",
|
| 1232 |
required=False
|
|
@@ -1263,76 +1207,6 @@ st.markdown("""
|
|
| 1263 |
padding-bottom: 0rem;
|
| 1264 |
}
|
| 1265 |
|
| 1266 |
-
/* Röd triangel-indikator för editerbara kolumner */
|
| 1267 |
-
/* Markera editerbara kolumnrubriker */
|
| 1268 |
-
[data-testid="stDataFrame"] thead th[aria-label*="🔺"] {
|
| 1269 |
-
position: relative;
|
| 1270 |
-
background-color: #FFF4E6 !important;
|
| 1271 |
-
border: 2px solid #FF9800 !important;
|
| 1272 |
-
font-weight: bold !important;
|
| 1273 |
-
}
|
| 1274 |
-
|
| 1275 |
-
[data-testid="stDataFrame"] thead th[aria-label*="🔺"]::after {
|
| 1276 |
-
content: "";
|
| 1277 |
-
position: absolute;
|
| 1278 |
-
top: 0;
|
| 1279 |
-
right: 0;
|
| 1280 |
-
width: 0;
|
| 1281 |
-
height: 0;
|
| 1282 |
-
border-style: solid;
|
| 1283 |
-
border-width: 0 15px 15px 0;
|
| 1284 |
-
border-color: transparent #DC3545 transparent transparent;
|
| 1285 |
-
z-index: 10;
|
| 1286 |
-
}
|
| 1287 |
-
|
| 1288 |
-
/* Hover-effekt för editerbara kolumnrubriker */
|
| 1289 |
-
[data-testid="stDataFrame"] thead th[aria-label*="🔺"]:hover {
|
| 1290 |
-
background-color: #FFE5CC !important;
|
| 1291 |
-
}
|
| 1292 |
-
|
| 1293 |
-
/* VIKTIGT: Markera alla CELLER i editerbara kolumner med ljusorange bakgrund */
|
| 1294 |
-
/* Vi använder nth-child för att rikta in oss på specifika kolumner */
|
| 1295 |
-
/* Detta måste justeras baserat på vilka kolumner som är editerbara */
|
| 1296 |
-
[data-testid="stDataFrame"] tbody td {
|
| 1297 |
-
position: relative;
|
| 1298 |
-
}
|
| 1299 |
-
|
| 1300 |
-
/* Lägg till en subtil border på alla celler för bättre läsbarhet */
|
| 1301 |
-
[data-testid="stDataFrame"] td,
|
| 1302 |
-
[data-testid="stDataFrame"] th {
|
| 1303 |
-
border: 1px solid #E0E0E0 !important;
|
| 1304 |
-
}
|
| 1305 |
-
|
| 1306 |
-
/* Styla editerbara celler i data_editor - de som inte är disabled */
|
| 1307 |
-
div[data-testid="stDataFrameResizable"] input:not(:disabled),
|
| 1308 |
-
div[data-testid="stDataFrameResizable"] textarea:not(:disabled) {
|
| 1309 |
-
background-color: #FFF9F0 !important;
|
| 1310 |
-
border: 2px solid #FFB74D !important;
|
| 1311 |
-
border-radius: 4px;
|
| 1312 |
-
}
|
| 1313 |
-
|
| 1314 |
-
/* Hover-effekt på editerbara celler */
|
| 1315 |
-
div[data-testid="stDataFrameResizable"] input:not(:disabled):hover,
|
| 1316 |
-
div[data-testid="stDataFrameResizable"] textarea:not(:disabled):hover {
|
| 1317 |
-
background-color: #FFF3E0 !important;
|
| 1318 |
-
border-color: #FF9800 !important;
|
| 1319 |
-
}
|
| 1320 |
-
|
| 1321 |
-
/* Focus-effekt på editerbara celler */
|
| 1322 |
-
div[data-testid="stDataFrameResizable"] input:not(:disabled):focus,
|
| 1323 |
-
div[data-testid="stDataFrameResizable"] textarea:not(:disabled):focus {
|
| 1324 |
-
background-color: #FFFFFF !important;
|
| 1325 |
-
border-color: #FF5722 !important;
|
| 1326 |
-
box-shadow: 0 0 0 2px rgba(255, 87, 34, 0.2) !important;
|
| 1327 |
-
}
|
| 1328 |
-
|
| 1329 |
-
/* Disabled/readonly celler får neutral färg */
|
| 1330 |
-
div[data-testid="stDataFrameResizable"] input:disabled,
|
| 1331 |
-
div[data-testid="stDataFrameResizable"] textarea:disabled {
|
| 1332 |
-
background-color: #F5F5F5 !important;
|
| 1333 |
-
border: 1px solid #E0E0E0 !important;
|
| 1334 |
-
}
|
| 1335 |
-
|
| 1336 |
.excel-info-box {
|
| 1337 |
background-color: #E3F2FD;
|
| 1338 |
border-left: 4px solid #2196F3;
|
|
@@ -1967,176 +1841,24 @@ else:
|
|
| 1967 |
st.markdown(f"*...och {len(visible_readonly) - 15} fält till*")
|
| 1968 |
|
| 1969 |
with col_help2:
|
| 1970 |
-
st.markdown("####
|
| 1971 |
-
st.markdown("Dessa fält
|
| 1972 |
visible_editable = [col for col in EDITABLE_COLUMNS if col not in HIDDEN_COLUMNS]
|
| 1973 |
for col in visible_editable:
|
| 1974 |
if col in company_areas.columns:
|
| 1975 |
-
st.markdown(f"•
|
| 1976 |
|
| 1977 |
# Huvudtabell
|
| 1978 |
st.markdown("## 📊 Dataöversikt")
|
| 1979 |
st.markdown("""
|
| 1980 |
-
Kolumner med **
|
| 1981 |
|
| 1982 |
**Kontrollera att uppgifterna i låsta celler stämmer.** Om något verkar fel, mejla till [migration@chargenode.eu](mailto:migration@chargenode.eu) så hjälper vi dig.
|
| 1983 |
""")
|
| 1984 |
-
|
| 1985 |
-
# ============================================================================
|
| 1986 |
-
# KOMPLETTERINGSSTATISTIK - Ny sektion
|
| 1987 |
-
# ============================================================================
|
| 1988 |
-
st.markdown("---")
|
| 1989 |
-
st.markdown("### 📈 Kompletteringsstatus")
|
| 1990 |
-
|
| 1991 |
-
# Beräkna komplettering
|
| 1992 |
-
visible_editable = [col for col in EDITABLE_COLUMNS if col in company_areas.columns and col not in HIDDEN_COLUMNS]
|
| 1993 |
-
|
| 1994 |
-
if visible_editable:
|
| 1995 |
-
# Total komplettering
|
| 1996 |
-
total_cells = len(company_areas) * len(visible_editable)
|
| 1997 |
-
filled_cells = 0
|
| 1998 |
-
|
| 1999 |
-
for col in visible_editable:
|
| 2000 |
-
filled_cells += company_areas[col].notna().sum()
|
| 2001 |
-
# Räkna också icke-tomma strängar
|
| 2002 |
-
filled_cells += (company_areas[col].astype(str).str.strip() != '').sum()
|
| 2003 |
-
# Dra bort dubbelräkning
|
| 2004 |
-
filled_cells -= company_areas[col].notna().sum()
|
| 2005 |
-
|
| 2006 |
-
# Bättre beräkning: räkna celler som har värde OCH inte är tomma strängar
|
| 2007 |
-
filled_cells = 0
|
| 2008 |
-
for col in visible_editable:
|
| 2009 |
-
filled_cells += ((company_areas[col].notna()) & (company_areas[col].astype(str).str.strip() != '')).sum()
|
| 2010 |
-
|
| 2011 |
-
completion_pct = (filled_cells / total_cells * 100) if total_cells > 0 else 0
|
| 2012 |
-
|
| 2013 |
-
# Metrics i kolumner
|
| 2014 |
-
metric_col1, metric_col2, metric_col3, metric_col4 = st.columns(4)
|
| 2015 |
-
|
| 2016 |
-
with metric_col1:
|
| 2017 |
-
st.metric(
|
| 2018 |
-
label="✅ Kompletteringsgrad",
|
| 2019 |
-
value=f"{completion_pct:.0f}%",
|
| 2020 |
-
help="Procent av alla editerbara fält som är ifyllda"
|
| 2021 |
-
)
|
| 2022 |
-
|
| 2023 |
-
with metric_col2:
|
| 2024 |
-
st.metric(
|
| 2025 |
-
label="📝 Ifyllda fält",
|
| 2026 |
-
value=f"{filled_cells}/{total_cells}",
|
| 2027 |
-
help="Antal ifyllda fält av totalt antal editerbara fält"
|
| 2028 |
-
)
|
| 2029 |
-
|
| 2030 |
-
with metric_col3:
|
| 2031 |
-
# Räkna kompletta rader (alla editerbara fält ifyllda)
|
| 2032 |
-
complete_rows = 0
|
| 2033 |
-
for idx in range(len(company_areas)):
|
| 2034 |
-
row_complete = True
|
| 2035 |
-
for col in visible_editable:
|
| 2036 |
-
val = company_areas.iloc[idx][col]
|
| 2037 |
-
if pd.isna(val) or str(val).strip() == '':
|
| 2038 |
-
row_complete = False
|
| 2039 |
-
break
|
| 2040 |
-
if row_complete:
|
| 2041 |
-
complete_rows += 1
|
| 2042 |
-
|
| 2043 |
-
st.metric(
|
| 2044 |
-
label="✨ Kompletta områden",
|
| 2045 |
-
value=f"{complete_rows}/{len(company_areas)}",
|
| 2046 |
-
help="Områden där alla editerbara fält är ifyllda"
|
| 2047 |
-
)
|
| 2048 |
-
|
| 2049 |
-
with metric_col4:
|
| 2050 |
-
incomplete_rows = len(company_areas) - complete_rows
|
| 2051 |
-
st.metric(
|
| 2052 |
-
label="⚠️ Ofullständiga områden",
|
| 2053 |
-
value=incomplete_rows,
|
| 2054 |
-
help="Områden som behöver mer information"
|
| 2055 |
-
)
|
| 2056 |
-
|
| 2057 |
-
# Progress bar
|
| 2058 |
-
st.progress(completion_pct / 100, text=f"Total komplettering: {completion_pct:.1f}%")
|
| 2059 |
-
|
| 2060 |
-
# Status per kolumn - Expanderbar sektion
|
| 2061 |
-
with st.expander("📋 Detaljerad status per fält", expanded=False):
|
| 2062 |
-
st.markdown("**Se hur många områden som har fyllt i varje fält:**")
|
| 2063 |
-
|
| 2064 |
-
for col in visible_editable:
|
| 2065 |
-
filled = ((company_areas[col].notna()) & (company_areas[col].astype(str).str.strip() != '')).sum()
|
| 2066 |
-
total = len(company_areas)
|
| 2067 |
-
pct = (filled / total * 100) if total > 0 else 0
|
| 2068 |
-
|
| 2069 |
-
# Färgkod baserat på komplettering
|
| 2070 |
-
if pct >= 90:
|
| 2071 |
-
status_emoji = "🟢"
|
| 2072 |
-
elif pct >= 50:
|
| 2073 |
-
status_emoji = "🟡"
|
| 2074 |
-
else:
|
| 2075 |
-
status_emoji = "🔴"
|
| 2076 |
-
|
| 2077 |
-
col_left, col_right = st.columns([4, 1])
|
| 2078 |
-
with col_left:
|
| 2079 |
-
st.progress(pct / 100, text=f"{status_emoji} {col}")
|
| 2080 |
-
with col_right:
|
| 2081 |
-
st.caption(f"{filled}/{total} ({pct:.0f}%)")
|
| 2082 |
-
|
| 2083 |
-
# Filter-sektion
|
| 2084 |
-
st.markdown("---")
|
| 2085 |
-
filter_col1, filter_col2 = st.columns([2, 3])
|
| 2086 |
-
|
| 2087 |
-
with filter_col1:
|
| 2088 |
-
view_filter = st.radio(
|
| 2089 |
-
"🔍 Visa:",
|
| 2090 |
-
["📋 Alla områden", "⚠️ Endast ofullständiga", "✅ Endast kompletta"],
|
| 2091 |
-
horizontal=False,
|
| 2092 |
-
help="Filtrera tabellen för att fokusera på det som behöver göras"
|
| 2093 |
-
)
|
| 2094 |
-
|
| 2095 |
-
with filter_col2:
|
| 2096 |
-
if view_filter == "⚠️ Endast ofullständiga":
|
| 2097 |
-
st.info("💡 **Tips:** Fokusera på att fylla i de röd-markerade fälten i dessa områden först!")
|
| 2098 |
-
elif view_filter == "✅ Endast kompletta":
|
| 2099 |
-
st.success("🎉 **Bra jobbat!** Dessa områden har all nödvändig information ifylld.")
|
| 2100 |
-
else:
|
| 2101 |
-
st.info(f"📊 Visar alla {len(company_areas)} områden")
|
| 2102 |
-
|
| 2103 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 2104 |
|
| 2105 |
-
# Applicera filter baserat på användarens val
|
| 2106 |
-
company_areas_filtered = company_areas.copy()
|
| 2107 |
-
|
| 2108 |
-
if visible_editable and 'view_filter' in locals():
|
| 2109 |
-
if view_filter == "⚠️ Endast ofullständiga":
|
| 2110 |
-
# Filtrera till rader där minst ett editerbart fält är tomt
|
| 2111 |
-
mask = pd.Series([False] * len(company_areas))
|
| 2112 |
-
for idx in range(len(company_areas)):
|
| 2113 |
-
for col in visible_editable:
|
| 2114 |
-
val = company_areas.iloc[idx][col]
|
| 2115 |
-
if pd.isna(val) or str(val).strip() == '':
|
| 2116 |
-
mask.iloc[idx] = True
|
| 2117 |
-
break
|
| 2118 |
-
company_areas_filtered = company_areas[mask]
|
| 2119 |
-
|
| 2120 |
-
if len(company_areas_filtered) == 0:
|
| 2121 |
-
st.success("🎉 Fantastiskt! Alla områden är kompletta!")
|
| 2122 |
-
st.balloons()
|
| 2123 |
-
|
| 2124 |
-
elif view_filter == "✅ Endast kompletta":
|
| 2125 |
-
# Filtrera till rader där alla editerbara fält är ifyllda
|
| 2126 |
-
mask = pd.Series([True] * len(company_areas))
|
| 2127 |
-
for idx in range(len(company_areas)):
|
| 2128 |
-
for col in visible_editable:
|
| 2129 |
-
val = company_areas.iloc[idx][col]
|
| 2130 |
-
if pd.isna(val) or str(val).strip() == '':
|
| 2131 |
-
mask.iloc[idx] = False
|
| 2132 |
-
break
|
| 2133 |
-
company_areas_filtered = company_areas[mask]
|
| 2134 |
-
|
| 2135 |
-
if len(company_areas_filtered) == 0:
|
| 2136 |
-
st.warning("⚠️ Inga kompletta områden än. Börja fylla i de editerbara fälten!")
|
| 2137 |
-
|
| 2138 |
# Ta bort dolda kolumner och sätt kolumnordning med Fastighetsnamn först
|
| 2139 |
-
company_areas_display =
|
| 2140 |
|
| 2141 |
# Skapa kolumnordning med Fastighetsnamn först (för bättre synlighet vid scrolling)
|
| 2142 |
column_order = ['Fastighetsnamn'] + [col for col in company_areas_display.columns if col != 'Fastighetsnamn']
|
|
@@ -2144,15 +1866,6 @@ else:
|
|
| 2144 |
|
| 2145 |
column_config = get_column_config(company_areas_display)
|
| 2146 |
|
| 2147 |
-
# Visa antal rader som visas
|
| 2148 |
-
if len(company_areas_display) > 0:
|
| 2149 |
-
st.markdown(f"""
|
| 2150 |
-
<div style='background-color: #E3F2FD; padding: 1rem; border-radius: 8px; margin: 1rem 0; border-left: 4px solid #2196F3;'>
|
| 2151 |
-
<strong>📍 Visar {len(company_areas_display)} av {len(company_areas)} områden</strong><br>
|
| 2152 |
-
<small>💡 <strong>Tips:</strong> Editerbara celler (🔺) har orange bakgrund och ram. Klicka i en cell för att börja redigera. Ändringar sparas automatiskt.</small>
|
| 2153 |
-
</div>
|
| 2154 |
-
""", unsafe_allow_html=True)
|
| 2155 |
-
|
| 2156 |
# Container för att förhindra scroll-hopp
|
| 2157 |
table_container = st.container()
|
| 2158 |
|
|
|
|
| 1110 |
else:
|
| 1111 |
return False, f"❌ Fel vid sparande: {error_msg}. Kontakta migration@chargenode.eu om problemet kvarstår."
|
| 1112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1113 |
def get_column_config(df):
|
| 1114 |
"""Generera kolumnkonfiguration för data_editor med beskrivningar och dropdowns"""
|
| 1115 |
config = {}
|
| 1116 |
|
| 1117 |
+
# Beskrivningar för editerbara kolumner
|
|
|
|
|
|
|
| 1118 |
editable_descriptions = {
|
| 1119 |
+
'Namn': 'Huvudkontaktperson för detta område. Personen vi kontaktar vid frågor eller problem.',
|
| 1120 |
+
'Email adress': 'Ange individuell e-postadress till kontaktpersonen för detta område.',
|
| 1121 |
+
'Telefon': 'Telefonnummer till huvudkontaktpersonen. Format: +46XXXXXXXXX eller 07X-XXXXXXX',
|
| 1122 |
+
'Tillgänglighet': 'Välj i dropdown-menyn om detta är ett område som vi alltid kan komma åt, eller om det är stängt. Om det är stängt var vänlig fyll i fältet Access info.',
|
| 1123 |
+
'Access info': 'Om området inte alltid är tillgängligt, vänligen ange hur vi kan få tillgång. T.ex. "Nycklar finns i receptionen" eller "Ring Pelle på 07XXXXXXXX när ni är på gång".',
|
| 1124 |
+
'Fritext meddelande': 'Övrig information eller meddelanden som är viktiga för oss att veta.',
|
| 1125 |
+
'Main Contact': 'Ange om denna person är huvudansvarig för kontot. Välj "Huvudkontakt" för helst endast en person per företag.'
|
| 1126 |
}
|
| 1127 |
|
| 1128 |
# Beskrivningar för readonly kolumner
|
|
|
|
| 1146 |
if col == 'Tillgänglighet':
|
| 1147 |
# Dropdown för tillgänglighet
|
| 1148 |
config[col] = st.column_config.SelectboxColumn(
|
| 1149 |
+
col,
|
| 1150 |
help=editable_descriptions.get(col, ''),
|
| 1151 |
width="medium",
|
| 1152 |
options=[
|
|
|
|
| 1158 |
elif col == 'Main Contact':
|
| 1159 |
# Dropdown för huvudkontakt
|
| 1160 |
config[col] = st.column_config.SelectboxColumn(
|
| 1161 |
+
col,
|
| 1162 |
help=editable_descriptions.get(col, ''),
|
| 1163 |
width="medium",
|
| 1164 |
options=[
|
|
|
|
| 1170 |
else:
|
| 1171 |
# Vanlig textkolumn
|
| 1172 |
config[col] = st.column_config.TextColumn(
|
| 1173 |
+
col,
|
| 1174 |
help=editable_descriptions.get(col, ''),
|
| 1175 |
width="medium",
|
| 1176 |
required=False
|
|
|
|
| 1207 |
padding-bottom: 0rem;
|
| 1208 |
}
|
| 1209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1210 |
.excel-info-box {
|
| 1211 |
background-color: #E3F2FD;
|
| 1212 |
border-left: 4px solid #2196F3;
|
|
|
|
| 1841 |
st.markdown(f"*...och {len(visible_readonly) - 15} fält till*")
|
| 1842 |
|
| 1843 |
with col_help2:
|
| 1844 |
+
st.markdown("#### ✏️ Editerbara fält")
|
| 1845 |
+
st.markdown("Dessa fält kan du klicka i och redigera:")
|
| 1846 |
visible_editable = [col for col in EDITABLE_COLUMNS if col not in HIDDEN_COLUMNS]
|
| 1847 |
for col in visible_editable:
|
| 1848 |
if col in company_areas.columns:
|
| 1849 |
+
st.markdown(f"• ✏️ {col}")
|
| 1850 |
|
| 1851 |
# Huvudtabell
|
| 1852 |
st.markdown("## 📊 Dataöversikt")
|
| 1853 |
st.markdown("""
|
| 1854 |
+
Kolumner med **🔒** (gult hänglås) kan inte editeras. Klicka i andra fält för att redigera. **Ändringar sparas automatiskt.**
|
| 1855 |
|
| 1856 |
**Kontrollera att uppgifterna i låsta celler stämmer.** Om något verkar fel, mejla till [migration@chargenode.eu](mailto:migration@chargenode.eu) så hjälper vi dig.
|
| 1857 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1858 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 1859 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1860 |
# Ta bort dolda kolumner och sätt kolumnordning med Fastighetsnamn först
|
| 1861 |
+
company_areas_display = company_areas.drop(columns=HIDDEN_COLUMNS, errors='ignore')
|
| 1862 |
|
| 1863 |
# Skapa kolumnordning med Fastighetsnamn först (för bättre synlighet vid scrolling)
|
| 1864 |
column_order = ['Fastighetsnamn'] + [col for col in company_areas_display.columns if col != 'Fastighetsnamn']
|
|
|
|
| 1866 |
|
| 1867 |
column_config = get_column_config(company_areas_display)
|
| 1868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1869 |
# Container för att förhindra scroll-hopp
|
| 1870 |
table_container = st.container()
|
| 1871 |
|