| import gradio as gr |
|
|
| from document_structure_ui import Document_structureUI |
| from report_creator_ui import ReportCreatorUI |
|
|
| |
|
|
| |
| VALID_USERNAME = "mfrs_neoai_pj" |
| VALID_PASSWORD = "K7m2P9x4Q8n6R3" |
|
|
|
|
| def authenticate(username: str, password: str) -> tuple[bool, str]: |
| """ログイン認証""" |
| if username == VALID_USERNAME and password == VALID_PASSWORD: |
| return True, "ログイン成功" |
| return False, "ユーザー名またはパスワードが正しくありません" |
|
|
|
|
| def get_custom_css() -> str: |
| """統合カスタムCSS""" |
| return """ |
| .status-approved { |
| color: #059669; |
| background-color: #d1fae5; |
| padding: 4px 12px; |
| border-radius: 9999px; |
| font-size: 0.875rem; |
| font-weight: 600; |
| display: inline-block; |
| } |
| .status-pending { |
| color: #d97706; |
| background-color: #fef3c7; |
| padding: 4px 12px; |
| border-radius: 9999px; |
| font-size: 0.875rem; |
| font-weight: 600; |
| display: inline-block; |
| } |
| .status-saved { |
| color: #059669; |
| font-size: 0.875rem; |
| font-weight: 600; |
| } |
| |
| .theme-highlight { |
| background: #eff6ff; |
| border-left: 4px solid #60a5fa; |
| padding: 12px; |
| border-radius: 6px; |
| } |
| .panel-gray { |
| background: #eff6ff; |
| border-left: 4px solid #60a5fa; |
| padding: 12px; |
| border-radius: 6px; |
| } |
| .small-hint { |
| font-size: 12px; |
| color: #1e40af; |
| } |
| |
| /* 青いボタンスタイル - より強い優先度 */ |
| button.btn-blue, |
| .btn-blue button, |
| button[variant="primary"], |
| .gradio-container button[variant="primary"], |
| .main-content button[variant="primary"], |
| .primary, |
| button:is([variant="primary"]) { |
| background: linear-gradient(135deg, #4F7FFF 0%, #1D4ED8 100%) !important; |
| background-color: #1D4ED8 !important; |
| color: #ffffff !important; |
| border: none !important; |
| border-color: #1D4ED8 !important; |
| box-shadow: 0 2px 8px rgba(29, 78, 216, 0.25) !important; |
| } |
| button.btn-blue:hover, |
| .btn-blue button:hover, |
| button[variant="primary"]:hover, |
| .gradio-container button[variant="primary"]:hover, |
| .main-content button[variant="primary"]:hover, |
| .primary:hover, |
| button:is([variant="primary"]):hover { |
| background: linear-gradient(135deg, #6366F1 0%, #3B82F6 100%) !important; |
| background-color: #3B82F6 !important; |
| filter: brightness(1.05) !important; |
| transform: translateY(-1px) !important; |
| } |
| |
| /* タブボタンも青に */ |
| button[role="tab"][aria-selected="true"] { |
| background: #eff6ff !important; |
| color: #1d4ed8 !important; |
| } |
| |
| button[role="tab"] { |
| position: relative !important; |
| border-bottom: 2px solid transparent !important; |
| box-shadow: inset 0 -2px transparent !important; |
| } |
| button[role="tab"]::after { |
| content: ""; |
| position: absolute; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| height: 2px; |
| background: transparent; |
| } |
| button[role="tab"][aria-selected="true"] { |
| background: #eff6ff !important; |
| color: #1d4ed8 !important; |
| } |
| button[role="tab"][aria-selected="true"]::after { |
| background: #1d4ed8 !important; |
| } |
| button[role="tab"]:hover { |
| color: #1d4ed8 !important; |
| } |
| button[role="tab"]:hover::after { |
| background: #60a5fa !important; |
| } |
| |
| details > summary { |
| font-weight: 700 !important; |
| font-size: 1.05rem !important; |
| color: #111827 !important; |
| } |
| |
| .msg-approved textarea { |
| background-color: #d1fae5 !important; |
| color: #059669 !important; |
| border: 1px solid #059669 !important; |
| border-left: 4px solid #059669 !important; |
| } |
| |
| |
| .block { |
| gap: 0 !important; |
| } |
| |
| .gap { |
| gap: 0 !important; |
| } |
| |
| .main-content button[variant="primary"], |
| .main-content .primary { |
| background: linear-gradient(135deg, #4F7FFF 0%, #1D4ED8 100%) !important; |
| color: #ffffff !important; |
| border: none !important; |
| box-shadow: 0 2px 8px rgba(29, 78, 216, 0.25) !important; |
| } |
| |
| .main-content button[variant="primary"]:hover, |
| .main-content .primary:hover { |
| filter: brightness(1.05) !important; |
| transform: translateY(-1px) !important; |
| } |
| |
| /* ログイン画面 */ |
| .login-container { |
| max-width: 400px; |
| margin: 100px auto; |
| padding: 40px; |
| background: white; |
| border-radius: 12px; |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| } |
| |
| .login-title { |
| text-align: center; |
| font-size: 1.5rem; |
| font-weight: 700; |
| color: #1f2937; |
| margin-bottom: 30px; |
| } |
| |
| .error-message { |
| color: #dc2626; |
| background-color: #fee2e2; |
| padding: 12px; |
| border-radius: 6px; |
| margin-top: 10px; |
| text-align: center; |
| } |
| |
| /* 決議種別ドロップダウンの文字色 */ |
| .resolution-special { |
| color: #ea580c !important; /* orange-600 */ |
| } |
| .resolution-conflict { |
| color: #2563eb !important; /* blue-600 */ |
| } |
| .resolution-normal { |
| color: #000000 !important; /* black */ |
| } |
| |
| /* ドロップダウンの選択肢の文字色 */ |
| .resolution-special option[value="特別決議"] { |
| color: #ea580c !important; /* orange-600 */ |
| } |
| .resolution-conflict option[value="利益相反"] { |
| color: #2563eb !important; /* blue-600 */ |
| } |
| .resolution-normal option[value="普通決議"] { |
| color: #000000 !important; /* black */ |
| } |
| |
| /* ドロップダウンを開いたときの選択肢の色 */ |
| .gradio-container .resolution-special option, |
| .gradio-container .resolution-special option[value="特別決議"] { |
| color: #ea580c !important; |
| } |
| .gradio-container .resolution-conflict option, |
| .gradio-container .resolution-conflict option[value="利益相反"] { |
| color: #2563eb !important; |
| } |
| .gradio-container .resolution-normal option, |
| .gradio-container .resolution-normal option[value="普通決議"] { |
| color: #000000 !important; |
| } |
| |
| /* Gradioのドロップダウン用のより強力なセレクタ */ |
| .gradio-container .resolution-special, |
| .gradio-container .resolution-special *, |
| .gradio-container .resolution-special input, |
| .gradio-container .resolution-special select, |
| .gradio-container .resolution-special .wrap, |
| .gradio-container .resolution-special .wrap *, |
| .gradio-container .resolution-special .wrap input, |
| .gradio-container .resolution-special .wrap select { |
| color: #ea580c !important; |
| } |
| .gradio-container .resolution-conflict, |
| .gradio-container .resolution-conflict *, |
| .gradio-container .resolution-conflict input, |
| .gradio-container .resolution-conflict select, |
| .gradio-container .resolution-conflict .wrap, |
| .gradio-container .resolution-conflict .wrap *, |
| .gradio-container .resolution-conflict .wrap input, |
| .gradio-container .resolution-conflict .wrap select { |
| color: #2563eb !important; |
| } |
| .gradio-container .resolution-normal, |
| .gradio-container .resolution-normal *, |
| .gradio-container .resolution-normal input, |
| .gradio-container .resolution-normal select, |
| .gradio-container .resolution-normal .wrap, |
| .gradio-container .resolution-normal .wrap *, |
| .gradio-container .resolution-normal .wrap input, |
| .gradio-container .resolution-normal .wrap select { |
| color: #000000 !important; |
| } |
| |
| /* さらに強力なセレクタ - すべての子要素を対象 */ |
| .resolution-special, |
| .resolution-special *, |
| .resolution-special input, |
| .resolution-special select, |
| .resolution-special option, |
| .resolution-special span, |
| .resolution-special div, |
| .resolution-special .wrap, |
| .resolution-special .wrap * { |
| color: #ea580c !important; |
| } |
| .resolution-conflict, |
| .resolution-conflict *, |
| .resolution-conflict input, |
| .resolution-conflict select, |
| .resolution-conflict option, |
| .resolution-conflict span, |
| .resolution-conflict div, |
| .resolution-conflict .wrap, |
| .resolution-conflict .wrap * { |
| color: #2563eb !important; |
| } |
| .resolution-normal, |
| .resolution-normal *, |
| .resolution-normal input, |
| .resolution-normal select, |
| .resolution-normal option, |
| .resolution-normal span, |
| .resolution-normal div, |
| .resolution-normal .wrap, |
| .resolution-normal .wrap * { |
| color: #000000 !important; |
| } |
| """ |
|
|
|
|
| def create_main_app(): |
| """メインアプリケーションを作成""" |
|
|
| with gr.Blocks(title="総会議案書作成システム", css=get_custom_css()) as app: |
| |
| login_state = gr.State(False) |
|
|
| |
| with gr.Column(visible=True, elem_classes=["login-container"]) as login_page: |
| gr.HTML("<h1 class='login-title'>総会議案書作成システム</h1>") |
| gr.Markdown("### ログイン") |
|
|
| username_input = gr.Textbox(label="ユーザー名") |
| password_input = gr.Textbox(label="パスワード", type="password") |
|
|
| login_btn = gr.Button("ログイン", variant="primary", size="lg") |
| login_message = gr.HTML(visible=False) |
|
|
| |
| with gr.Column(visible=False) as main_content: |
| |
| gr.HTML( |
| "<h1 style='text-align: left; font-size: 1.5rem; font-weight: 700; margin: 20px 0; color: #1f2937;'>総会議案書作成システム</h1>" |
| ) |
|
|
| |
| with gr.Tabs(): |
| |
| with gr.Tab("総会資料作成"): |
| report_ui = ReportCreatorUI() |
| report_ui.create_interface() |
|
|
| |
| with gr.Tab("資料登録"): |
| structure_ui = Document_structureUI() |
| structure_ui.create_interface() |
|
|
| |
| def handle_login(username: str, password: str): |
| is_valid, message = authenticate(username, password) |
| if is_valid: |
| return ( |
| True, |
| gr.update(visible=False), |
| gr.update(visible=True), |
| gr.update(visible=False), |
| ) |
| return ( |
| False, |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(value=f"<div class='error-message'>{message}</div>", visible=True), |
| ) |
|
|
| login_btn.click( |
| fn=handle_login, |
| inputs=[username_input, password_input], |
| outputs=[login_state, login_page, main_content, login_message], |
| ) |
|
|
| |
| password_input.submit( |
| fn=handle_login, |
| inputs=[username_input, password_input], |
| outputs=[login_state, login_page, main_content, login_message], |
| ) |
|
|
| return app |
|
|
|
|
| if __name__ == "__main__": |
| app = create_main_app() |
| app.launch(server_name="0.0.0.0", server_port=7860, share=True) |
|
|