From 70e9a21b879f2f33791e8796442824939aefdd67 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 26 Feb 2026 00:24:17 +0000 Subject: [PATCH] Autosave: 20260226-002417 --- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 1515 bytes core/__pycache__/mcp.cpython-311.pyc | Bin 0 -> 6164 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 3651 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 641 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 5336 bytes core/admin.py | 16 +- core/mcp.py | 190 ++++++++++++ core/migrations/0001_initial.py | 41 +++ core/migrations/0002_slacksettings.py | 24 ++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 2537 bytes .../0002_slacksettings.cpython-311.pyc | Bin 0 -> 1143 bytes core/models.py | 42 ++- core/templates/base.html | 64 +++- core/templates/core/index.html | 279 +++++++++--------- core/templates/core/request_detail.html | 98 ++++++ core/urls.py | 10 +- core/views.py | 119 +++++++- requirements.txt | 1 + static/css/custom.css | 158 +++++++++- 19 files changed, 861 insertions(+), 181 deletions(-) create mode 100644 core/__pycache__/mcp.cpython-311.pyc create mode 100644 core/mcp.py create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/0002_slacksettings.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0002_slacksettings.cpython-311.pyc create mode 100644 core/templates/core/request_detail.html diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 2964e119884bc4661330a4d6c594e246c1597120..e3c9c0078260ad14093f47e9970bbb99f7f423a4 100644 GIT binary patch literal 1515 zcma)6y-yTD6rb6T`+~be5LALlVk{PgYc(;bvCu?716JE?mYV?=_T!k@jnbV7jh!8I zb{NAy0|^D0ifBc51S%@u+xw6^&V#&ll`bXyM$+XzhCq(mc}uDACPZtlUZeYl!KBX0;Zwh1#K zE8D)ir$Y{0&9MhR`|Sm>b3KfDAI1zYW^c@x?_o6hFy??Ue`Cf1D=ZUh;1sL2s4`s^ zc;P9kwscimc>UpH6!}YhGvP8;#kWb|hD%)5qEK>GT=w1S#xjp%FImEBx&g<4;{*{) ze4GzD&Sv8Jt;nG7$=G3@toiQNFLI9cT+UQJMFFoQD@hn975;@+lQ1fbqIPDY;8=kXA?+FAWIk3+auvu!ZlHkkq1O*PW(Yg4?T zG>@r#41T9tRYt~coJduu3f{89(!?En_85Sri&324MHm2Bv@&847e#~;LK#2}q`s_r ze$0g`0~d=zf6H0*xX+{*!n{-v{#D|38*(s__W`aNFjt>H9=7W5j>n$X-{aBTiJSUc zfra+70&FnUa92CETu4Q!tZ21dK9O9cexyRdfR5-z%j~CDp#8nFCh#5t+9J`itFm5z ztg-2ko!`Yun_pc5coY%IlU~|IZUzu8(d{elH%<$s;=1 z&?ud0kn6BJdUVe^!@vc?nNG9^o%{^ZhEkT&A%(jS8k$a@HOO_?eRMQFeTIPx1Z0`& ddxIwN%s}=}cVDw)tU8!l8{RMwhQR4sr delta 151 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 VS}FLR(+Gxc+b9s>k3(}${{yWu4SoOs literal 0 HcmV?d00001 diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 18a063c85a879cc83d00a9a0383835944a371483..0e37a4a34666532d296578160d673864a072c0b0 100644 GIT binary patch literal 3651 zcmaKvO>7&-6@YiS%bz91KbcnQy0R9Il9VkXxv3DiwHm{rMA?o_1tJxrOp(QkGnPm# zcbVCxVhaVrLk=pCgAWE=pb26Wg)1XE=wK8%`k3Pq*n_awoP3iZpLA&Z-Y!=XDaIr6 zac16|nKwW0o6*0M$v6k;KY#nK?nXH7CoBXh+{+w%4VgzA;RvsBO}@(Wm=9^8YAD1l za76fuBO>J+LeTIDRtv zRi$H(pM*dU-=02BB=9osCd-;yTQ5_~s_U!f=7(j=VAPbYH7a`=`C6+bSB!P4%cdc> zP0CD}82USwyrynY*)n8G>Xtl3^$q!MU8CmIi~qq8m8XImZ>skcjq0n`nj2oxRDInQ z^|q!x<=vRtwhTo#zNj5}42>|4dvXBVjJwH$hoRD8%6BC2@hJx#h$hwUn%F=-oSI%U znsmCo($=l^G`&Y_&^ep_!eHxWORdrA8cb>0--fByeK)Eoro|Lx^K9STr+U>AbZW*x zbhy3rgq@x^fIQc?eSy;lqoC>EK5z4d!_oe(Z&OQUDKIYOMK`7>y4s|Q;>HyP+-hr> zA5oOg+p5;noY|mk#W1OF+0xof)mR!jU^x^xt}A-IjzM+=z9wcmM}K=4rtH{x|Kqng z5BgF*!QOyh_6ro}K;UW+qoybfSKgHhYbq<&sYYCB#jq3$civ6TsTQr&o7C6D+bseW zL{Tkv20Oy82fSl=ggqfb#{3e*3FpLHws>nVBR_~Z8F{;~YdxQ|GYj3!f|FV3+}O*$ z^vifZtjbGSl;K3M1&7dV{uTt#JX2Q6#gopTYY;OgxUdtT|M5* zRXBeC91@N+(l{A*gMk_I0uzsrmq>UgG7y+?&ms|6Jb}#jL4m7|4786@fy8#Cf$&cb zSj9 z^&y|n3$A3rl6t^!BjBplHr;rQ(I8SY(7#Vi4& z!sl`2U!VH9-MMFrehy`<=*3nke|8WqsdD97Wx1>rt}osw%$IwNSzf*|=aWQix9h|W zqnvR=b;8DBOm-QBE8Q$FmU<*o*=oB9hrnciKvmMjJ4>mpsk%-zH|7^D(@nzF)iQLz zX2UdehO=OQL?NT_P}R>+b`cdQu-=TosvlEoKO~Mt_PIcGO8apx{>!Z~M>_YIzzW>_ z@vJQ^cBMr}TI_syOfx%kshhdvWG(^8dWh?0E;^ZuP)?3-ec~j?w?}tB`|;0q@=iB- z$4TDl6!&tI4~kB1ayRtDq@BCk&0TeJS39@i0@~Ve(i7Y7?N;ph&+N3)O)E}X>0A#Q zY)|eIJ9njm@g9VUHP;=&Av<$pnQ3`+=q zp&>$*mk3Y84Lsp~DYPRDKqug`0&sbCEH9IWI340}x2uot9|{q#uJgFu(+5d=>&SrZc24q%h_%F;NA^^UtA6WnZ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ebb8c6ea5957e01e2e132c7f2744cb84e6f77cdc..fdbc9677dae9ed19c8473311269ec43aa59e2362 100644 GIT binary patch literal 641 zcmah_ziZn-6nsZe9!}H@H(t<0T%tWC;{13P#o)v1&=Do}@ZWAq{lN z*r_#iXs7<5{bOYCU~cV{&5$iqPpUh#1p4mX@$UQH`*`ntX*M@N+p9MpgQo=GXZiVK z_ByzJr-S#vfRPX);t~R^#*DBQ8Lknbi;?M?k>y$hYQPCYZZPwQ_1OUUuJ3QFm^w#h z-^w`LWE;0K4Q{c`f0+|>>#TVU-PTnp-z5qYFB@O!5ul68VO}~O2~RS$8TrYO$1D*+ zoT+9Sdj4cM<)g6>6V;OZdCt>p$av-jp#p_t5%E$k_9A|Ux|m8!i=MUCHMCNovR;Di zbv@nB^ME}_CI?iOTRZC7s==Ol@rl^WWthrx4a$5G@M$Vd9aox_MwPD`I!h0baT%xI z;96(E%{^N7Li14`XF27k+|M&1=~T!`ns`2^zL1*M@_Wh48+$(zED!mCZ0q8359tT} ztPDcP5_XHfeF=|>|M|)S)44djY+pW^Jua{_$Ib#fOSCn%^LF>sQGvZV_7>Qyu)?`B od-(Nrf&Dr57ua8ecW39t{lfwWa~v#iuyV|;+C`&0a$EQJ3&7m3ZvX%Q delta 209 zcmZoav7r-85vTTQkZj? za+#x;C;G{lr87pcq_77wXmY#+Y0+f7#Zr)1lJSxe%E`#jO})iX1TqgKmzS8E>Zi#% zIf_wkayz3h7avd@q_o&;@;yc=iw6wi7f{g$HU?JC2A2+@j*uB_7g*#kvdCXyk^jKV O#Lv{g4T42HKTC{j*X3Z1+!_mG;LB8i~e9pkU}X~ikTPRK;{l&5lf{}+(@Z7rI#&n z3#68`HDin0GWNKgBJI|+Bjb!aGp@KxFWb`YOjW!}&)d_Uj5qGp^Nw_N#uxWN-pRVs z{!Abq$kfDZGPUtqz0REuWbv!7o4#n$O&utX1hc@qPZ4GQSv^BES zIEwk6lRl#{i{^b-;ImgbA;)J0PV)|lVy;wzRtM&?EQht4DNaoA6Ej>A5}fGm>WxvF zOH5_BB|ghR-jfvYWP-cNWpW}EtA)jEG9mH_hGd>oFj>s1`9}K3uJL^OD);L-P7t-~ zp}7pRGkNuRnn})%bE25aP6;qd?KG2R(_A8x%q6hVCrO4X1sh~OhX0HI0=a}JIFUS> zDYe4c%vwnS2Sq(K8dwW!O;N1vq5VFb=7Z9SqH)r$of9IWBsjd*^;-c1VqmcH|v5Gm_{2%yVsewRJO+4OV_Jt2qR1?OLJKf zFVZ*>P(0 z`F~(TF^A^J=shrznr55e*+tF4a!DR^#|KrIoD?X4vv`WQp72%UqS_@`Mq~ui7dH2E z$MI=C!yTWSn9GWD$3dl%b0UwAFYtI)$T3Ooc#_B5@%a?DAav#yH80T)&{ACzxETiV z!e96ukpIDN1r@x=<-dJvt$x#aKyn_~_BXFxm!fB+@Y!$sCI67@A5#27l56Oxr}oox zTb`CpPs`eZ>}gXxZ7UbHn~rTNd7k2-=_H6Bv;!r zPs3WZ?1?I#sAP-2fTbXl;yR#$Z1=?)Nc9d92}3c=l$C3}tc;~LlA1649%@>lh(zUo zW7$_8#;mq8_EH0DGpig?TA@=U@+^j3t8

cBkgmEnD+e?8{qcTqPdt++A9>h`~|| zLX`@?Y|Gp7lvuaJF;l)i=6S@Ot)ZFf62F-zZ^IVWo~2j^>-@sLY!~;J_+Vzly7Kn> za3T-LsptWWqMUtMhTjaonf+5d;(S>r1-36 z&GHLihxKJ8inrFUU!4?~^qZa2VkRB)V!GXD9S*-}>~Hdia#IZ*;A3 ztx@{rXu;#~I|>NM%HT^M^46}lNzKPLrex1a#dA`!oy1|-)SHcm2Fm&x;^{_XmBO;y zgeE)41}Iln@`~$D<*9XIsb33h1s1W^S$!VdYC<%)u6y*aFf6GG0|nT-SI^3s?Z(Wo z!?r#9I8w-r?ZlAnv9ikXoyDEK+OvntEH&YWJvrTGPB4dit)hzcRIJ#xC$E>SuHd!j z?aPim`F&HB(r~i=Wmn#b!+9qg*s+~^NT}wuwYk8oPn%$&3k@^45p7W?8K7 z@|$)z%_e)6Q)^ObVnVbkfAkgA8Knn{Xg}EGffr1V} z0yD2SaT%HsXp^^XbjR?(0f$Xo%yC*^4yTFPPEDqgj7T8Foty?d;IdPk=Htlxi6oRK z0D{0%BxTK+;RFHDP;(GONU@sRSVuw>U{tW%8jL;%AhhItFFOl}l%?N;62Xx)CyZiZ zMKlkYIEPd70FB`FyNX~S#B4fP;I|2rn~ajkG8s;Dk zp147p!$5G|q1D_2H;HFWL}iEdXT_MufHxa)Xz@AH>n%dwCS9HS4pJ<>NUHh?86@?d zUC$eh;sm1EI?x%!fV*l#C=15f%5kJ=c%=ep}!Z`e2_H+CqE9jkU=4(+>h`m@t(@5{kfCD^** z-VC0Qf+rpy{%7o)*tbLSnGxm82vn%_;ji3Zx;NTo`lLdiTpd=M>3gT|o?d@{+-KdVMSU;b!zhIhs(Si8YVf3^4EBH%Nh>u>YtMKCu-(wHZF89_djJ$JC=e zYV1V8jrO1Z5!v^*{6t8Bke6Q6+V#l&ZA6M(lp`0F$VDl9@ztwse`5hz+@W(X;OclA*KD$9zv9_1+4c*E zK_~urzV2ca{i}5VLGoR+YOvY*T^BuAZ~Z<%LH_%CJJ6(#-@r`*v8u!;c=0zN@Ej>W zK@bvm@#U$9l-d3fT;1_3-uxIb+w?s!rUHeRt-JjSaL)lb^H$dOh2=FT-M-X%ffE_9 z2rN8%xdob^q#0TO&pgf1BKW2>v00s9S1h?%+yWb1YSg{pq6eB__1=a%Y|*Sxt+`7N zQvv){;&>V+;RBM35pt9e-JdO*s2fnBdC7F84;7ej3j~44H8w7>&papo5L%}R8_BGo zXTjFL{_3$s3Z9jNXO-aDmCI@%tOTN4fuoy&qjKPw5;(RM=-UkRJ<7;|3rgU^%J4H! z4e=QJRDZKt*HW-sPk}cHVy%sK38BcQZ?8gke*o98=O$ZZ*K=U%fR0?yG zo#b;6CfE<34B+bk3_;ADZd;8Ph83)#@y&od<#pd)w|2U)zDD{+bZP$VItL@nrMX|? zN#GK6r6CSA}}uyj#iXn>SCNd-%}C^+ud Ii7vGN1Mzq5-2eap literal 1364 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 - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}MCP Slack Server{% endblock %} + {% if project_description %} + + {% endif %} + {% load static %} + + + + + + {% block head %}{% endblock %} - {% block content %}{% endblock %} +

+ +
+ {% block content %}{% endblock %} +
+ +
+
+

© 2026 MCP Slack Bridge • Powered by Flatlogic AI

+
+
+ + + + {% block scripts %}{% endblock %} - + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..4ccc125 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,144 @@ {% extends "base.html" %} +{% load static %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% block title %}Dashboard | MCP Slack Bridge{% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+
+
+

AI &Human Slack Bridge

+

+ Bridging the gap between autonomous AI agents and human workers. + Exposing tools to request design, feedback, or any manual task directly via Slack. +

+
+ + + Server: {{ server_status }} + + + DJANGO 5.2 • MCP v1.0 + +
+
+
+
+
MCP Endpoint
+ /mcp/ +

Post JSON-RPC here

+
+
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
-{% endblock %} \ No newline at end of file + + +
+
+
+ {% if not is_slack_configured %} +
+
Configuration Required
+

+ Set SLACK_BOT_TOKEN in your environment to enable message delivery. +

+
+ {% endif %} + +
+
+
+
Recent Tool Activity
+ Showing last 20 requests +
+
+ + + + + + + + + + + + {% for r in requests %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
TIMESTAMPTOOL NAMECHANNELSTATUSACTION
{{ r.created_at|date:"Y-m-d H:i" }}{{ r.tool_name }}#{{ r.slack_channel }} + {% if r.status == 'SENT' %} + SENT + {% elif r.status == 'ERROR' %} + ERROR + {% else %} + PENDING + {% endif %} + + Details +
+ No activity logged yet. Connect an MCP client to start. +
+
+
+
+ +
+
+
+
How to use (Tools/Call)
+
+{
+  "jsonrpc": "2.0",
+  "method": "tools/call",
+  "params": {
+    "name": "send_slack_message",
+    "arguments": {
+      "channel": "C12345",
+      "text": "Hello Human!"
+    }
+  },
+  "id": 1
+}
+
+
+
+
+
MCP Server Info
+
    +
  • + Status: + ONLINE +
  • +
  • + Version: + 1.0.0 +
  • +
  • + Available Tools: + 2 +
  • +
  • + Python: + {{ python_version|default:"3.11" }} +
  • +
+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/request_detail.html b/core/templates/core/request_detail.html new file mode 100644 index 0000000..5c1aca9 --- /dev/null +++ b/core/templates/core/request_detail.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Request Details | MCP Slack Bridge{% endblock %} + +{% block content %} +
+
+
+ + +
+
+

Tool Call Details

+ {% if req.status == 'SENT' %} + SENT + {% elif req.status == 'ERROR' %} + ERROR + {% else %} + PENDING + {% endif %} +
+ +
+
+

Tool Name

+
{{ req.tool_name }}
+
+
+

Slack Channel

+
#{{ req.slack_channel|default:"N/A" }}
+
+
+

Slack Timestamp

+
{{ req.slack_ts|default:"N/A" }}
+
+
+ +
+ +
+
Arguments
+
{{ req.arguments|json_script:"args-data" }}
+
+
+ +
+
Slack API Response
+
{{ req.response_json|json_script:"resp-data" }}
+
+
+ +
+
+ Human Responses + {{ responses.count }} +
+ + {% for r in responses %} +
+
+ {{ r.user_name|default:"Human" }} + {{ r.created_at|date:"Y-m-d H:i" }} +
+

{{ r.text }}

+ {% if r.file_url %} + + {% endif %} +
+ {% empty %} +
+

No responses recorded yet.

+ AI agent can check for updates using list_responses +
+ {% endfor %} +
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..82a0e3c 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,9 @@ from django.urls import path - -from .views import home +from .views import dashboard, mcp_endpoint, slack_webhook, request_detail urlpatterns = [ - path("", home, name="home"), -] + path("", dashboard, name="home"), + path("mcp/", mcp_endpoint, name="mcp_endpoint"), + path("webhook/slack/", slack_webhook, name="slack_webhook"), + path("request//", request_detail, name="request_detail"), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..b42d10a 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,112 @@ +import json import os import platform - -from django import get_version as django_version -from django.shortcuts import render +from django.http import JsonResponse, HttpResponse +from django.shortcuts import render, get_object_or_404 from django.utils import timezone +from django.views.decorators.csrf import csrf_exempt +from asgiref.sync import sync_to_async +from .models import MCPToolRequest, HumanResponse, SlackSettings +from .mcp import handle_mcp_request +@csrf_exempt +async def mcp_endpoint(request): + """ + Main entry point for MCP clients via HTTP. + Accepts JSON-RPC 2.0 payloads. + """ + if request.method != "POST": + return JsonResponse({"error": "Method not allowed"}, status=405) + + body = request.body.decode("utf-8") + response_data = await handle_mcp_request(body) + + if response_data is None: + return HttpResponse(status=204) + + return JsonResponse(response_data) -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() +def dashboard(request): + """ + Polished landing page showing active server status and recent logs. + """ + requests_list = MCPToolRequest.objects.all().order_by('-created_at')[:20] + + # Slack config check + slack_token = os.getenv("SLACK_BOT_TOKEN") + if not slack_token: + config = SlackSettings.objects.first() + if config and config.bot_token: + slack_token = config.bot_token + is_slack_configured = bool(slack_token) + context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + "is_slack_configured": is_slack_configured, + "requests": requests_list, + "django_version": platform.python_version(), + "now": timezone.now(), + "server_status": "ONLINE" if is_slack_configured else "CONFIG_PENDING" } return render(request, "core/index.html", context) + +@csrf_exempt +def slack_webhook(request): + """ + Slack Event API Webhook. + Captures threaded replies and saves them as HumanResponse. + """ + if request.method == "POST": + try: + data = json.loads(request.body) + + # 1. Verification Challenge + if data.get("type") == "url_verification": + return JsonResponse({"challenge": data.get("challenge")}) + + # 2. Event Callback + if data.get("type") == "event_callback": + event = data.get("event", {}) + + # We only care about user messages (no bot_id) in threads (has thread_ts) + if event.get("type") == "message" and not event.get("bot_id"): + thread_ts = event.get("thread_ts") + text = event.get("text", "") + user_id = event.get("user", "Unknown User") + + # Basic extraction + user_name = user_id + + files = event.get("files", []) + file_url = None + if files: + file_url = files[0].get("url_private", "") + + if thread_ts: + # Find the corresponding tool request + mcp_request = MCPToolRequest.objects.filter(slack_ts=thread_ts).first() + if mcp_request: + # Avoid duplicate responses if Slack retries + # We can just check if we already have this text/user in last 10 seconds, or just save it + HumanResponse.objects.create( + request=mcp_request, + text=text, + user_name=user_name, + file_url=file_url + ) + return HttpResponse("Created", status=201) + except Exception as e: + print("Webhook error:", e) + return HttpResponse("Error", status=400) + + return HttpResponse("OK") + +def request_detail(request, pk): + """Detailed view of a single tool call.""" + tool_request = get_object_or_404(MCPToolRequest, pk=pk) + responses = tool_request.responses.all().order_by('-created_at') + + return render(request, "core/request_detail.html", { + "req": tool_request, + "responses": responses + }) diff --git a/requirements.txt b/requirements.txt index e22994c..15a35fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django==5.2.7 mysqlclient==2.2.7 python-dotenv==1.1.1 +httpx==0.27.0 \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..24063b1 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,156 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +:root { + --bg-color: #0d1117; + --card-bg: #161b22; + --text-primary: #c9d1d9; + --text-secondary: #8b949e; + --accent-blue: #58a6ff; + --accent-green: #238636; + --border-color: #30363d; } + +body { + background-color: var(--bg-color); + color: var(--text-primary); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + margin: 0; + padding: 0; +} + +.navbar { + background-color: var(--card-bg) !important; + border-bottom: 1px solid var(--border-color); +} + +.navbar-brand { + font-weight: 700; + letter-spacing: -0.5px; + color: var(--accent-blue) !important; +} + +.nav-link { + color: var(--text-secondary) !important; +} + +.nav-link:hover { + color: var(--text-primary) !important; +} + +.card { + background-color: var(--card-bg); + border: 1px solid var(--border-color); + border-radius: 12px; + color: var(--text-primary); +} + +.card-title { + color: var(--accent-blue); + font-weight: 600; +} + +.btn-primary { + background-color: var(--accent-blue); + border-color: var(--accent-blue); + border-radius: 8px; + font-weight: 600; +} + +.btn-primary:hover { + background-color: #388bfd; + border-color: #388bfd; +} + +.badge-online { + background-color: var(--accent-green); + color: white; + padding: 4px 8px; + border-radius: 20px; + font-size: 0.75rem; + text-transform: uppercase; + font-weight: 700; + box-shadow: 0 0 10px rgba(35, 134, 54, 0.4); +} + +.badge-pending { + background-color: #d29922; + color: white; + padding: 4px 8px; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 700; +} + +.badge-error { + background-color: #f85149; + color: white; + padding: 4px 8px; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 700; +} + +.table { + color: var(--text-primary); +} + +.table thead th { + border-bottom: 1px solid var(--border-color); + color: var(--text-secondary); + font-weight: 500; + font-size: 0.85rem; +} + +.table td { + border-bottom: 1px solid var(--border-color); + vertical-align: middle; +} + +.log-entry:hover { + background-color: rgba(88, 166, 255, 0.05); + cursor: pointer; +} + +.hero-section { + padding: 80px 0; + background: radial-gradient(circle at top right, rgba(88, 166, 255, 0.1), transparent); +} + +.status-dot { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; + margin-right: 8px; +} + +.status-dot.online { + background-color: var(--accent-green); + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(35, 134, 54, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(35, 134, 54, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(35, 134, 54, 0); + } +} + +code { + background-color: #0d1117; + padding: 2px 6px; + border-radius: 4px; + color: #ff7b72; +} + +pre { + background-color: #0d1117; + padding: 16px; + border-radius: 8px; + border: 1px solid var(--border-color); + color: var(--text-primary); + overflow-x: auto; +} \ No newline at end of file