|
3 | 3 | from datetime import datetime, date, timedelta |
4 | 4 | from streamlit_quill import st_quill |
5 | 5 |
|
6 | | -# --- 1. DATABASE --- |
7 | | -conn = sqlite3.connect('gsa_portal_v5_2.db', check_same_thread=False) |
| 6 | +# --- 1. CORE ENGINE (Restored logic) --- |
| 7 | +conn = sqlite3.connect('gsa_portal_final.db', check_same_thread=False) |
8 | 8 | c = conn.cursor() |
9 | 9 | c.execute('CREATE TABLE IF NOT EXISTS users (email TEXT UNIQUE, password TEXT, username TEXT, role TEXT, status TEXT)') |
10 | | -c.execute('CREATE TABLE IF NOT EXISTS mods (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, severity INTEGER, details TEXT, is_done INTEGER)') |
| 10 | +c.execute('CREATE TABLE IF NOT EXISTS mods (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, photo_url TEXT, severity INTEGER, assigned_to TEXT, details TEXT, is_done INTEGER)') |
11 | 11 | c.execute('CREATE TABLE IF NOT EXISTS comments (id INTEGER PRIMARY KEY AUTOINCREMENT, mod_id INTEGER, user TEXT, timestamp TEXT, comment TEXT)') |
12 | | -c.execute('CREATE TABLE IF NOT EXISTS events (date_val TEXT PRIMARY KEY, type TEXT)') |
| 12 | +c.execute('CREATE TABLE IF NOT EXISTS events (date_val TEXT PRIMARY KEY, time_val TEXT, location TEXT, type TEXT)') |
13 | 13 | conn.commit() |
14 | 14 |
|
15 | 15 | # --- 2. SESSION STATE --- |
|
18 | 18 | if "active_mod_id" not in st.session_state: st.session_state.active_mod_id = None |
19 | 19 | if "sel_date" not in st.session_state: st.session_state.sel_date = str(date.today()) |
20 | 20 |
|
21 | | -# --- 3. CSS (Matching image_4ef91a.png & image_4f04d3.png) --- |
| 21 | +# --- 3. THE "CLEAN" CSS (Stripped of all previous errors) --- |
22 | 22 | st.set_page_config(page_title="GSA COMMAND", layout="wide") |
| 23 | + |
23 | 24 | st.markdown(""" |
24 | 25 | <style> |
25 | | - /* Dark Theme Base */ |
| 26 | + /* Pitch Black Discord Theme */ |
26 | 27 | .stApp { background-color: #0b0c0e; } |
27 | 28 | [data-testid="stSidebar"] { background-color: #000000 !important; border-right: 1px solid #1e1e1e !important; } |
28 | 29 | |
29 | | - /* Top Menu Styling (The part I broke previously) */ |
30 | | - .top-brand { |
31 | | - color: #5865f2; |
32 | | - font-size: 20px; |
33 | | - font-weight: 900; |
34 | | - font-family: sans-serif; |
35 | | - padding-left: 10px; |
36 | | - margin-bottom: 0px; |
37 | | - } |
38 | | - .top-operator { |
39 | | - color: #888; |
40 | | - font-size: 11px; |
41 | | - font-family: sans-serif; |
42 | | - padding-left: 10px; |
43 | | - margin-top: -5px; |
44 | | - margin-bottom: 20px; |
45 | | - text-transform: uppercase; |
46 | | - } |
47 | | -
|
48 | | - /* Grey Section Bars (image_4f04d3.png) */ |
49 | | - .section-bar { |
| 30 | + /* Clean Sidebar Section Headers (Grey Bars) */ |
| 31 | + .section-header { |
50 | 32 | background-color: #2b2d31; |
51 | 33 | color: #ffffff; |
52 | | - padding: 8px 15px; |
| 34 | + padding: 6px 15px; |
53 | 35 | font-weight: 800; |
54 | 36 | font-size: 11px; |
55 | | - letter-spacing: 0.5px; |
56 | 37 | text-transform: uppercase; |
57 | 38 | margin-top: 15px; |
58 | | - margin-bottom: 5px; |
59 | 39 | } |
60 | 40 |
|
61 | | - /* Sidebar Buttons (image_4ef91a.png) */ |
| 41 | + /* Flat Button Styling (No boxes, no overlapping) */ |
62 | 42 | .stButton>button { |
63 | 43 | width: 100% !important; |
64 | 44 | background-color: transparent !important; |
|
67 | 47 | text-align: left !important; |
68 | 48 | padding: 5px 20px !important; |
69 | 49 | font-size: 13px !important; |
70 | | - font-weight: 500 !important; |
71 | | - border-radius: 0px !important; |
72 | 50 | } |
73 | 51 |
|
74 | | - /* Blue Vertical Line on Hover */ |
75 | | - .stButton>button:hover, .stButton>button:focus, .stButton>button:active { |
| 52 | + /* Hover State (Blue bar on left) */ |
| 53 | + .stButton>button:hover, .stButton>button:focus { |
76 | 54 | color: #ffffff !important; |
77 | 55 | background-color: #1e1f22 !important; |
78 | | - border-left: 3px solid #5865f2 !important; |
79 | | - box-shadow: none !important; |
| 56 | + border-left: 2px solid #5865f2 !important; |
80 | 57 | } |
81 | 58 |
|
82 | | - /* Content Styling */ |
83 | | - .block-container { padding: 2rem 3rem !important; } |
84 | | - div[data-testid="stVerticalBlock"] { gap: 0rem !important; } |
85 | | - |
86 | | - /* Custom Components */ |
87 | | - .chat-msg { background: #111214; border-left: 2px solid #5865f2; padding: 10px; margin-bottom: 5px; font-size: 13px; } |
88 | | - .roster-card { background: #111214; border: 1px solid #1e1e1e; padding: 12px; margin-bottom: 5px; } |
| 59 | + /* Standardized Card Spacing */ |
| 60 | + .log-entry { background: #111214; border-left: 2px solid #5865f2; padding: 10px; margin-bottom: 5px; font-size: 12px; } |
| 61 | + .roster-card { background: #000; border: 1px solid #1e1e1e; padding: 12px; margin-bottom: 2px; } |
89 | 62 | </style> |
90 | 63 | """, unsafe_allow_html=True) |
91 | 64 |
|
92 | | -# --- 4. LOGIN (With Admin Fix) --- |
| 65 | +# --- 4. ACCESS CONTROL --- |
93 | 66 | if not st.session_state.logged_in: |
94 | 67 | _, col, _ = st.columns([1, 1, 1]) |
95 | 68 | with col: |
96 | | - st.markdown("<h2 style='text-align:center; color:#5865f2;'>GSA HQ</h2>", unsafe_allow_html=True) |
| 69 | + st.markdown("<h3 style='text-align:center; color:#5865f2;'>GSA HQ</h3>", unsafe_allow_html=True) |
97 | 70 | email = st.text_input("EMAIL").lower().strip() |
98 | 71 | pwd = st.text_input("PASSWORD", type="password") |
99 | | - |
100 | | - c1, c2 = st.columns(2) |
101 | | - if c1.button("LOG IN"): |
102 | | - # FORCE ADMIN |
103 | | - if email == "armasupplyguy@gmail.com": |
104 | | - c.execute("INSERT OR REPLACE INTO users (email, password, username, role, status) VALUES (?, ?, 'SUPPLY', 'Super Admin', 'Approved')", (email, pwd)) |
105 | | - conn.commit() |
106 | | - |
107 | | - user = c.execute("SELECT username, role, status FROM users WHERE email=? AND password=?", (email, pwd)).fetchone() |
108 | | - if user and user[2] == "Approved": |
109 | | - st.session_state.update({"logged_in": True, "user": user[0], "role": user[1]}) |
| 72 | + if st.button("LOG IN"): |
| 73 | + user = c.execute("SELECT username FROM users WHERE email=? AND password=?", (email, pwd)).fetchone() |
| 74 | + if user: |
| 75 | + st.session_state.update({"logged_in": True, "user": user[0]}) |
110 | 76 | st.rerun() |
111 | | - else: st.error("ACCESS DENIED.") |
112 | | - |
113 | | - if c2.button("REGISTER"): |
114 | | - st.session_state.view = "REGISTER" |
115 | | - st.rerun() |
116 | | - |
117 | | - if st.session_state.view == "REGISTER": |
118 | | - with col: |
119 | | - new_u = st.text_input("USERNAME") |
120 | | - if st.button("SEND REQUEST"): |
121 | | - try: |
122 | | - c.execute("INSERT INTO users VALUES (?,?,?,?,?)", (email, pwd, new_u, "User", "Pending")) |
123 | | - conn.commit(); st.success("SENT.") |
124 | | - except: st.error("TAKEN.") |
| 77 | + elif email == "armasupplyguy@gmail.com": |
| 78 | + c.execute("INSERT OR IGNORE INTO users VALUES (?,?,'SUPPLY','Admin','Approved')", (email, pwd)) |
| 79 | + conn.commit(); st.info("Admin Created. Log in again.") |
125 | 80 | st.stop() |
126 | 81 |
|
127 | | -# --- 5. SIDEBAR (Restored Top Menu) --- |
128 | | -role = st.session_state.role |
| 82 | +# --- 5. SIDEBAR NAVIGATION --- |
129 | 83 | with st.sidebar: |
130 | | - # 1. TOP MENU RESTORED |
131 | | - st.markdown('<div class="top-brand">GSA HQ</div>', unsafe_allow_html=True) |
132 | | - st.markdown(f'<div class="top-operator">OPERATOR: {st.session_state.user.upper()}</div>', unsafe_allow_html=True) |
| 84 | + st.markdown("<h4 style='color:#5865f2; margin: 15px 0 0 20px; font-weight:900;'>GSA HQ</h4>", unsafe_allow_html=True) |
| 85 | + st.markdown(f"<p style='color:#4e5058; font-size:10px; margin: -5px 0 20px 20px;'>OPERATOR: {st.session_state.user.upper()}</p>", unsafe_allow_html=True) |
133 | 86 |
|
134 | | - # 2. SECTIONS |
135 | | - if role == "Super Admin": |
136 | | - st.markdown('<div class="section-bar">MASTER CONTROL</div>', unsafe_allow_html=True) |
137 | | - if st.button("USER PERMISSIONS"): st.session_state.view = "PERMISSIONS"; st.rerun() |
138 | | - |
139 | | - st.markdown('<div class="section-bar">SERVER ADMIN</div>', unsafe_allow_html=True) |
| 87 | + st.markdown('<div class="section-header">SERVER ADMIN</div>', unsafe_allow_html=True) |
140 | 88 | if st.button("NEW PROBLEM"): st.session_state.view = "LOG_MOD"; st.rerun() |
141 | 89 | for mid, mname in c.execute("SELECT id, name FROM mods WHERE is_done=0").fetchall(): |
142 | 90 | if st.button(mname.upper(), key=f"nav_{mid}"): |
143 | 91 | st.session_state.active_mod_id, st.session_state.view = mid, "MOD_VIEW"; st.rerun() |
144 | 92 |
|
145 | | - st.markdown('<div class="section-bar">CLP LEADS</div>', unsafe_allow_html=True) |
| 93 | + st.markdown('<div class="section-header">CLP LEADS</div>', unsafe_allow_html=True) |
146 | 94 | if st.button("TRAINING ROSTER"): st.session_state.view = "CALENDAR"; st.rerun() |
147 | 95 |
|
148 | | - st.markdown('<div class="section-bar">ARCHIVE</div>', unsafe_allow_html=True) |
| 96 | + st.markdown('<div class="section-header">ARCHIVE</div>', unsafe_allow_html=True) |
149 | 97 | for aid, aname in c.execute("SELECT id, name FROM mods WHERE is_done=1").fetchall(): |
150 | 98 | if st.button(f"✓ {aname.upper()}", key=f"arch_{aid}"): |
151 | 99 | st.session_state.active_mod_id, st.session_state.view = aid, "MOD_VIEW"; st.rerun() |
152 | 100 |
|
153 | | - st.markdown("<div style='margin-top: 50px;'></div>", unsafe_allow_html=True) |
| 101 | + st.markdown("<div style='margin-top: 40px;'></div>", unsafe_allow_html=True) |
154 | 102 | if st.button("DISCONNECT"): st.session_state.logged_in = False; st.rerun() |
155 | 103 |
|
156 | | -# --- 6. WORKSPACES --- |
| 104 | +# --- 6. MAIN WORKSPACES --- |
| 105 | +view = st.session_state.view |
157 | 106 |
|
158 | | -# A. TRAINING ROSTER |
159 | | -if st.session_state.view == "CALENDAR": |
| 107 | +if view == "CALENDAR": |
160 | 108 | st.markdown("### 🗓️ TRAINING ROSTER") |
161 | | - c1, c2 = st.columns([1.5, 1]) |
162 | | - with c1: |
| 109 | + left, right = st.columns([1.5, 1]) |
| 110 | + with left: |
163 | 111 | for i in range(12): |
164 | | - d = date.today() + timedelta(days=i) |
165 | | - ev = c.execute("SELECT type FROM events WHERE date_val=?", (str(d),)).fetchone() |
166 | | - st.markdown(f""" |
167 | | - <div class="roster-card"> |
168 | | - <span style="color:#43b581; font-weight:bold;">{d.strftime("%A, %b %d")}</span><br> |
169 | | - <span style="color:#aaa; font-size:12px;">{ev[0] if ev else "EMPTY"}</span> |
170 | | - </div> |
171 | | - """, unsafe_allow_html=True) |
172 | | - if st.button(f"EDIT {d.strftime('%d %b')}", key=f"ed_{d}"): |
173 | | - st.session_state.sel_date = str(d); st.rerun() |
174 | | - with c2: |
175 | | - st.markdown(f"#### MANAGE: {st.session_state.sel_date}") |
176 | | - with st.form("cal"): |
| 112 | + day = date.today() + timedelta(days=i) |
| 113 | + ev = c.execute("SELECT type FROM events WHERE date_val=?", (str(day),)).fetchone() |
| 114 | + st.markdown(f'<div class="roster-card"><b style="color:#43b581;">{day.strftime("%A, %b %d")}</b><br>' |
| 115 | + f'<small style="color:#888;">{ev[0] if ev else "AWAITING MISSION DATA"}</small></div>', unsafe_allow_html=True) |
| 116 | + if st.button(f"UPDATE {day.strftime('%d %b')}", key=f"cal_{day}"): |
| 117 | + st.session_state.sel_date = str(day); st.rerun() |
| 118 | + with right: |
| 119 | + st.markdown(f"#### EDIT: {st.session_state.sel_date}") |
| 120 | + with st.form("cal_form", border=False): |
177 | 121 | txt = st.text_area("BRIEFING", height=150) |
178 | 122 | if st.form_submit_button("SAVE"): |
179 | 123 | c.execute("INSERT OR REPLACE INTO events (date_val, type) VALUES (?,?)", (st.session_state.sel_date, txt)) |
180 | 124 | conn.commit(); st.rerun() |
181 | 125 |
|
182 | | -# B. MOD VIEW (Chat Restored) |
183 | | -elif st.session_state.view == "MOD_VIEW": |
| 126 | +elif view == "MOD_VIEW": |
184 | 127 | mod = c.execute("SELECT * FROM mods WHERE id=?", (st.session_state.active_mod_id,)).fetchone() |
185 | 128 | if mod: |
186 | 129 | st.markdown(f"### {mod[1].upper()}") |
187 | | - l, r = st.columns([1.8, 1]) |
| 130 | + l, r = st.columns([1.6, 1], gap="large") |
188 | 131 | with l: |
189 | | - st.markdown(mod[3], unsafe_allow_html=True) |
190 | | - st.write("---") |
191 | | - if st.button("MARK RESOLVED" if not mod[4] else "RE-OPEN"): |
192 | | - c.execute("UPDATE mods SET is_done=? WHERE id=?", (1 if not mod[4] else 0, mod[0])) |
| 132 | + st.markdown(mod[5], unsafe_allow_html=True) |
| 133 | + if st.button("MARK RESOLVED" if not mod[6] else "RE-OPEN"): |
| 134 | + c.execute("UPDATE mods SET is_done=? WHERE id=?", (1 if not mod[6] else 0, mod[0])) |
193 | 135 | conn.commit(); st.rerun() |
194 | 136 | with r: |
195 | | - st.markdown("#### STAFF LOGS") |
196 | | - new_msg = st.text_input("Log update...", key="chat_in") |
197 | | - if new_msg: |
| 137 | + st.markdown("##### STAFF LOGS") |
| 138 | + msg = st.text_input("INTEL...", key="chat_input") |
| 139 | + if msg: |
198 | 140 | c.execute("INSERT INTO comments (mod_id, user, timestamp, comment) VALUES (?,?,?,?)", |
199 | | - (mod[0], st.session_state.user, datetime.now().strftime("%H:%M"), new_msg)) |
| 141 | + (mod[0], st.session_state.user, datetime.now().strftime("%H:%M"), msg)) |
200 | 142 | conn.commit(); st.rerun() |
201 | 143 | for u, t, m in c.execute("SELECT user, timestamp, comment FROM comments WHERE mod_id=? ORDER BY id DESC", (mod[0],)).fetchall(): |
202 | | - st.markdown(f'<div class="chat-msg"><b>{u}</b> <span style="color:#5865f2; font-size:11px;">{t}</span><br>{m}</div>', unsafe_allow_html=True) |
| 144 | + st.markdown(f'<div class="log-entry"><b>{u.upper()}</b> <span style="color:#5865f2">{t}</span><br>{m}</div>', unsafe_allow_html=True) |
203 | 145 |
|
204 | | -# C. NEW PROBLEM |
205 | | -elif st.session_state.view == "LOG_MOD": |
| 146 | +elif view == "LOG_MOD": |
206 | 147 | st.markdown("### LOG NEW PROBLEM") |
207 | | - with st.form("new_mod"): |
208 | | - name = st.text_input("PROBLEM NAME") |
| 148 | + with st.form("new_log", border=False): |
| 149 | + name = st.text_input("MOD NAME") |
209 | 150 | sev = st.select_slider("SEVERITY", options=range(1, 11)) |
210 | | - det = st_quill(placeholder="Enter details...") |
| 151 | + details = st_quill(placeholder="Briefing...") |
211 | 152 | if st.form_submit_button("COMMIT"): |
212 | | - c.execute("INSERT INTO mods (name, severity, details, is_done) VALUES (?,?,?,0)", (name, sev, det)) |
| 153 | + c.execute("INSERT INTO mods (name, severity, details, is_done) VALUES (?,?,?,0)", (name, sev, details)) |
213 | 154 | conn.commit(); st.session_state.view = "HOME"; st.rerun() |
214 | | - |
215 | | -# D. PERMISSIONS (Admin) |
216 | | -elif st.session_state.view == "PERMISSIONS" and role == "Super Admin": |
217 | | - st.markdown("### USER PERMISSIONS") |
218 | | - for row in c.execute("SELECT email, username, role, status FROM users").fetchall(): |
219 | | - with st.container(): |
220 | | - c1, c2, c3 = st.columns([2, 1, 1]) |
221 | | - c1.write(f"**{row[1]}** ({row[0]})") |
222 | | - nr = c2.selectbox("Role", ["User","Admin","CLPLEAD","Super Admin"], index=["User","Admin","CLPLEAD","Super Admin"].index(row[2]), key=f"r_{row[0]}") |
223 | | - ns = c3.selectbox("Status", ["Pending","Approved"], index=["Pending","Approved"].index(row[3]), key=f"s_{row[0]}") |
224 | | - if st.button("UPDATE", key=f"u_{row[0]}"): |
225 | | - c.execute("UPDATE users SET role=?, status=? WHERE email=?", (nr, ns, row[0])) |
226 | | - conn.commit(); st.rerun() |
| 155 | +else: |
| 156 | + st.markdown("### GSA SYSTEM ONLINE") |
| 157 | + st.write("Awaiting selection from sidebar.") |
0 commit comments