import re import gradio as gr CUSTOM_CSS = """ body { background: linear-gradient(180deg, #f4f6f8 0%, #edf1f5 100%); } .gradio-container { max-width: 1280px !important; padding-top: 20px !important; padding-bottom: 36px !important; font-family: "Aptos", "Segoe UI", "Helvetica Neue", sans-serif !important; color: #1c2733; } .hero-shell { background: linear-gradient(135deg, #11283f 0%, #1a3d5f 100%); border-radius: 10px; overflow: hidden; box-shadow: 0 18px 40px rgba(18, 36, 53, 0.16); margin-bottom: 18px; border: 1px solid rgba(255, 255, 255, 0.08); } .hero-inner { padding: 30px 34px; color: #ffffff; } .eyebrow { display: inline-block; font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #ffffff !important; margin-bottom: 12px; } .hero-title { margin: 0 0 12px 0; font-size: 2.5rem; line-height: 1.08; font-weight: 800; letter-spacing: -0.02em; color: #ffffff !important; opacity: 1 !important; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.18); } .hero-subtitle { max-width: 860px; margin: 0; font-size: 1.02rem; line-height: 1.75; color: #ffffff !important; } .hero-bar { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin-top: 22px; background: rgba(255, 255, 255, 0.12); border-radius: 8px; overflow: hidden; } .hero-stat { background: rgba(255, 255, 255, 0.08); padding: 14px 16px; } .hero-stat strong { display: block; font-size: 0.92rem; margin-bottom: 4px; color: #ffffff !important; } .hero-stat span { font-size: 0.84rem; color: #ffffff !important; line-height: 1.45; } .panel { background: #ffffff; border: 1px solid #d7dde5; border-radius: 10px; box-shadow: 0 8px 20px rgba(31, 47, 61, 0.06); overflow: hidden; } .panel-head { padding: 20px 22px 14px 22px; border-bottom: 1px solid #e3e8ee; background: linear-gradient(180deg, #fbfcfd 0%, #f5f8fb 100%); } .panel-title { margin: 0 0 6px 0; font-size: 1.2rem; font-weight: 800; color: #17324a; } .panel-copy { margin: 0; color: #56697a; font-size: 0.95rem; line-height: 1.6; } .panel-body { padding: 20px 22px 22px 22px; } .metric-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; } .metric-card { background: #f8fafc; border: 1px solid #dfe5eb; border-left: 4px solid #244e73; border-radius: 8px; padding: 14px 14px 12px 14px; } .metric-card strong { display: block; color: #173954; font-size: 1rem; margin-bottom: 4px; } .metric-card span { color: #607180; font-size: 0.88rem; line-height: 1.45; } /* Base Textbox & Textarea styling */ textarea, [data-testid="textbox"] textarea { background: #ffffff !important; border: 1px solid #ccd5de !important; border-radius: 8px !important; font-size: 0.98rem !important; color: #27272A !important; box-shadow: none !important; } textarea::placeholder, [data-testid="textbox"] textarea::placeholder { color: #7a8896 !important; } textarea:focus, [data-testid="textbox"] textarea:focus { border: 1px solid #244e73 !important; box-shadow: 0 0 0 3px rgba(36, 78, 115, 0.12) !important; } /* Button styling */ button.primary { background: #173a59 !important; color: #ffffff !important; border: 1px solid #173a59 !important; border-radius: 8px !important; min-height: 46px !important; font-weight: 700 !important; transition: all 0.2s ease !important; } button.primary:hover { background: #214d75 !important; border-color: #214d75 !important; color: #ffffff !important; } button.secondary { background: #ffffff !important; color: #173954 !important; border: 1px solid #ccd5de !important; border-radius: 8px !important; min-height: 46px !important; font-weight: 700 !important; transition: all 0.2s ease !important; } button.secondary:hover { background: #eef4f8 !important; color: #16324a !important; border-color: #b8c7d4 !important; } /* Output Prose styling */ .prose, .prose * { color: #1e2f3d !important; line-height: 1.72 !important; } .prose h1, .prose h2, .prose h3 { color: #16324a !important; font-weight: 800 !important; } /* Footer Banner */ .footer-banner { margin-top: 16px; background: #ffffff; border: 1px solid #d8dfe6; border-radius: 10px; padding: 16px 20px; color: #4d6272; font-size: 0.94rem; line-height: 1.65; box-shadow: 0 8px 18px rgba(31, 47, 61, 0.05); } .footer-banner strong { color: #16324a; } /* Keep hero stats white no matter what */ .hero-shell *, .hero-inner *, .hero-subtitle, .eyebrow, .hero-stat strong, .hero-stat span { color: #ffffff !important; } @media (max-width: 900px) { .hero-title { font-size: 2rem; } .hero-bar, .metric-strip { grid-template-columns: 1fr; } } /* ========================================= SLEDGEHAMMER OVERRIDES FOR GRADIO BUGS ========================================= */ /* 1. HERO TITLE FIX (Bypasses Gradio's internal h1 color reset) */ .gradio-container .hero-shell h1.hero-title, .gradio-container .hero-shell h1, .hero-shell .hero-title { color: #ffffff !important; -webkit-text-fill-color: #ffffff !important; /* Forces white even if webkit overrides it */ opacity: 1 !important; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.18) !important; margin: 0 0 12px 0 !important; font-size: 2.5rem !important; font-weight: 800 !important; } /* 2. TABS VISIBILITY FIX */ div.tab-nav { gap: 8px !important; padding: 14px 22px 0 22px !important; background: #ffffff !important; } button[role="tab"] { color: #41586b !important; /* Forces unselected text to be dark grey */ border-radius: 999px !important; border: 1px solid #d5dce4 !important; background: #f7f9fb !important; font-weight: 700 !important; transition: all 0.2s ease !important; } button[role="tab"]:hover { background: #edf3f8 !important; color: #173954 !important; border-color: #bfd0dd !important; } button[role="tab"].selected { background: #173a59 !important; /* Force background on active */ color: #ffffff !important; /* Force white text on active */ border-color: #173a59 !important; } /* 3. EXAMPLES LABEL ("Sample Business Scenarios") FIX */ .examples > div:first-child, .examples > div:first-child *, .examples .label-wrap, .examples .label-wrap *, div.examples label, div.examples p, div.examples span { color: #000000 !important; font-weight: 700 !important; opacity: 1 !important; } /* 4. EXAMPLES BUTTONS & HOVER STATE FIX */ .examples button, .examples [role="button"], .examples .gallery-item { background-color: #ffffff !important; color: #000000 !important; border: 1px solid #ccd5de !important; } /* Force text inside the un-hovered buttons to stay black */ .examples button *, .examples [role="button"] *, .examples .gallery-item * { color: #000000 !important; } /* Hover and Selected states for Examples */ .examples button:hover, .examples [role="button"]:hover, .examples .gallery-item:hover, .examples button.selected, .examples [aria-selected="true"] { background-color: #173a59 !important; /* Dark blue background so white text pops */ color: #ffffff !important; /* White text */ border-color: #173a59 !important; } /* Force text inside the hovered/selected buttons to turn white */ .examples button:hover *, .examples [role="button"]:hover *, .examples .gallery-item:hover *, .examples button.selected *, .examples [aria-selected="true"] * { color: #ffffff !important; } """ def guess_title(notes): text = notes.lower() if "checkout" in text or "cart" in text or "payment" in text: return "Checkout Experience Improvement" if "hospital" in text or "patient" in text or "reception" in text: return "Patient Registration Process Improvement" if "login" in text or "signup" in text or "authentication" in text: return "Login and Access Experience Improvement" if "dashboard" in text: return "Operational Dashboard Enhancement" return "Business Process Improvement Initiative" def extract_payment_methods(text): methods = [] options = ["upi", "cards", "wallets", "net banking", "cod", "cash on delivery"] lowered = text.lower() for item in options: if item in lowered: methods.append(item.upper() if item == "upi" else item.title()) return methods def build_objectives(text): objectives = [] lowered = text.lower() if "cart" in lowered or "checkout" in lowered: objectives.append("Reduce checkout drop-off and improve completion rate") if "mobile" in lowered: objectives.append("Improve the experience for mobile users") if "support" in lowered: objectives.append("Reduce support tickets related to the process") if "faster" in lowered or "slow" in lowered: objectives.append("Reduce time required to complete the process") if "payment" in lowered: objectives.append("Make payment completion simpler and more flexible") if "dashboard" in lowered: objectives.append("Provide clearer visibility for business users") if not objectives: objectives = [ "Improve process efficiency", "Increase consistency in user experience", "Reduce manual effort" ] return objectives[:4] def build_scope(text): scope = [] lowered = text.lower() if "checkout" in lowered or "cart" in lowered: scope.append("Covers the end-to-end checkout journey") if "payment" in lowered: scope.append("Includes payment option selection and payment completion flow") if "guest checkout" in lowered or "guest" in lowered: scope.append("Includes guest user purchase flow") if "mobile" in lowered or "tablet" in lowered: scope.append("Includes responsive experience on mobile and tablet devices") if "dashboard" in lowered: scope.append("Includes dashboard access for internal users") if not scope: scope = [ "Covers the target business process described in the stakeholder notes", "Includes the main user interactions and supporting business workflow" ] return scope def build_functional_requirements(text): reqs = [] lowered = text.lower() payments = extract_payment_methods(text) if "guest checkout" in lowered or "guest" in lowered: reqs.append("The system should allow users to continue without mandatory account login") if payments: reqs.append(f"The system should support the following payment methods: {', '.join(payments)}") if "mobile" in lowered: reqs.append("The system should provide a mobile-friendly experience") if "tablet" in lowered: reqs.append("The system should work effectively on tablet devices") if "otp" in lowered: reqs.append("The system should support OTP-based verification") if "dashboard" in lowered: reqs.append("The system should provide a dashboard for business users") if "login" in lowered and "optional" in lowered: reqs.append("The system should make login optional where appropriate") if "faster" in lowered or "slow" in lowered: reqs.append("The system should reduce unnecessary user steps in the process") if not reqs: reqs = [ "The system should support the core business process described by stakeholders", "The system should simplify the current user journey", "The system should improve consistency across the workflow" ] return reqs[:6] def build_non_functional_requirements(text): reqs = [ "The solution should be easy to use for target users", "The solution should be reliable during normal business usage" ] lowered = text.lower() if "mobile" in lowered or "tablet" in lowered: reqs.append("The interface should be responsive across supported screen sizes") if "payment" in lowered or "otp" in lowered or "login" in lowered: reqs.append("The solution should protect user and transaction data") if "faster" in lowered or "slow" in lowered: reqs.append("The process should complete within an acceptable response time") return reqs[:4] def build_assumptions(text): assumptions = [ "Stakeholder notes represent the primary business need for the first version", "Existing systems can be updated without a full platform replacement" ] lowered = text.lower() if "launch" in lowered or "month" in lowered: assumptions.append("Delivery timeline expectations are realistic and approved by stakeholders") if "payment" in lowered: assumptions.append("Payment integrations can be configured within the current technical landscape") return assumptions[:4] def build_risks(text): risks = [ "Incomplete stakeholder input may lead to missing requirements", "Tight delivery timelines may reduce testing time" ] lowered = text.lower() if "payment" in lowered: risks.append("Payment integration complexity may delay release") if "guest" in lowered: risks.append("Guest user journeys may create reporting or tracking challenges") if "mobile" in lowered or "tablet" in lowered: risks.append("Cross-device usability issues may affect adoption") return risks[:4] def build_success_metrics(text): metrics = [ "Reduced time spent preparing business documentation", "Improved consistency of requirement outputs" ] lowered = text.lower() if "cart" in lowered or "checkout" in lowered: metrics.append("Higher checkout completion rate") if "support" in lowered: metrics.append("Lower support ticket volume") if "mobile" in lowered: metrics.append("Improved mobile task completion rate") return metrics[:4] def build_user_stories(text): lowered = text.lower() stories = [] if "guest checkout" in lowered or "guest" in lowered: stories.append({ "role": "customer", "goal": "complete checkout without creating an account", "benefit": "I can purchase faster", "given": "I have items in my cart", "when": "I proceed to checkout", "then": "I should be able to continue as a guest" }) if "payment" in lowered or extract_payment_methods(text): methods = extract_payment_methods(text) method_text = ", ".join(methods) if methods else "multiple payment options" stories.append({ "role": "customer", "goal": f"choose from {method_text}", "benefit": "I can use my preferred payment method", "given": "I am on the payment step", "when": "I review the payment options", "then": f"I should see {method_text}" }) if "mobile" in lowered or "tablet" in lowered: device = "mobile device" if "mobile" in lowered else "tablet" stories.append({ "role": "user", "goal": f"complete the journey smoothly on my {device}", "benefit": "I can finish the task without frustration", "given": f"I am using a {device}", "when": "I open the process flow", "then": "the experience should be clear and easy to use" }) if "dashboard" in lowered: stories.append({ "role": "business user", "goal": "view information in a dashboard", "benefit": "I can manage work more efficiently", "given": "I log into the dashboard", "when": "I review current records or updates", "then": "I should be able to access the information I need clearly" }) if not stories: stories.append({ "role": "end user", "goal": "complete the main process with fewer steps", "benefit": "I can save time and effort", "given": "I start the process", "when": "I move through the required steps", "then": "the journey should feel simple and efficient" }) stories.append({ "role": "business stakeholder", "goal": "receive clearer requirement documentation", "benefit": "delivery can be more consistent", "given": "stakeholder notes are captured", "when": "documentation is generated", "then": "the output should be structured and easy to review" }) return stories[:5] def format_brd(notes): title = guess_title(notes) objectives = build_objectives(notes) scope = build_scope(notes) functional = build_functional_requirements(notes) non_functional = build_non_functional_requirements(notes) assumptions = build_assumptions(notes) risks = build_risks(notes) metrics = build_success_metrics(notes) brd = f"""# Business Requirements Document ## Project Title {title} ## Executive Summary This document translates unstructured stakeholder notes into a cleaner, review-ready business requirements format. It is designed to help Business Analysts move faster from conversation capture to formal documentation. ## Business Problem Stakeholder feedback suggests that the current process contains friction, inconsistency, or avoidable manual effort. A more structured solution is needed to improve user outcomes and business performance. ## Objectives """ + "\n".join([f"- {item}" for item in objectives]) + """ ## Scope """ + "\n".join([f"- {item}" for item in scope]) + """ ## Functional Requirements """ + "\n".join([f"- {item}" for item in functional]) + """ ## Non-Functional Requirements """ + "\n".join([f"- {item}" for item in non_functional]) + """ ## Assumptions """ + "\n".join([f"- {item}" for item in assumptions]) + """ ## Risks """ + "\n".join([f"- {item}" for item in risks]) + """ ## Success Metrics """ + "\n".join([f"- {item}" for item in metrics]) return brd def format_stories(notes): stories = build_user_stories(notes) sections = ["# BDD-Style User Stories"] for index, story in enumerate(stories, start=1): sections.append( f""" ## User Story {index} As a {story['role']}, I want to {story['goal']}, So that {story['benefit']}. ### Acceptance Criteria - Given {story['given']} - When {story['when']} - Then {story['then']} """.strip() ) return "\n\n".join(sections) def format_before_after(notes): cleaned = notes.strip() bullet_lines = [line.strip(" -•\t") for line in cleaned.splitlines() if line.strip()] if not bullet_lines: bullet_lines = [part.strip() for part in re.split(r"[.;]\s*", cleaned) if part.strip()] before = "\n".join([f"- {line}" for line in bullet_lines[:8]]) return f"""# Before vs After ## Before: Raw Stakeholder Notes {before} ## After: Business-Ready Output - Raw notes were grouped into a formal BRD structure - Requirements were separated into functional and non-functional areas - User needs were rewritten as BDD-style stories - The output is now easier to review, share, and present ## Portfolio Value This demonstrates BA lifecycle thinking, requirement structuring, and workflow automation in a recruiter-friendly format. """ def generate_documents(notes): notes = notes.strip() if not notes: msg = "Please paste stakeholder notes first." return msg, msg, msg brd = format_brd(notes) stories = format_stories(notes) before_after = format_before_after(notes) return brd, stories, before_after with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Base()) as demo: gr.HTML("""
Strategy • Requirements • Workflow Automation

Agentic BA

A Business Analyst portfolio solution that converts unstructured stakeholder notes into structured BRDs, BDD-style user stories, and a clear before-versus-after documentation view.

Structured Documentation Converts rough notes into formal requirement outputs.
Public Portfolio Demo Built to share with recruiters and hiring teams.
100% Free Stack No paid APIs, subscriptions, or hidden usage costs.
BA Lifecycle Focus Highlights analysis, structuring, and requirement thinking.
""") with gr.Row(equal_height=True): with gr.Column(scale=5): gr.HTML("""

Input Workspace

Provide stakeholder notes, workshop summaries, or interview observations. The application reorganizes them into clearer business deliverables.

BRD Output Formal business requirement structure
BDD Stories User-centered requirement framing
Showcase View Before-and-after transformation for interviews
""") notes_input = gr.Textbox( lines=16, label="Raw Stakeholder Notes", placeholder="Example: Customers are abandoning carts before payment. Stakeholders want easier checkout, UPI, cards, wallets, guest checkout, mobile-friendly flow, and fewer support issues." ) with gr.Row(): generate_btn = gr.Button("Generate Deliverables", variant="primary") clear_btn = gr.Button("Clear Input", variant="secondary") gr.Examples( examples=[ ["Customers are abandoning carts before payment. They want easier checkout, UPI, cards, wallets, mobile-friendly pages, guest checkout, and fewer support issues."], ["Hospital reception staff say patient registration is slow. They want OTP verification, fewer manual steps, tablet support, and a simple dashboard for receptionists."], ["Users find login confusing. Stakeholders want optional sign-in where possible, a simpler user flow, faster completion, and fewer helpdesk calls."] ], inputs=notes_input, label="Sample Business Scenarios" ) gr.HTML("
") with gr.Column(scale=7): gr.HTML("""

Output Review

Review the generated documentation in a format designed for portfolio presentation and rapid recruiter understanding.

""") with gr.Tabs(): with gr.Tab("BRD"): brd_output = gr.Markdown() with gr.Tab("BDD User Stories"): stories_output = gr.Markdown() with gr.Tab("Before vs After"): before_after_output = gr.Markdown() gr.HTML("
") gr.HTML(""" """) generate_btn.click( fn=generate_documents, inputs=notes_input, outputs=[brd_output, stories_output, before_after_output] ) clear_btn.click( fn=lambda: ("", "", "", ""), inputs=[], outputs=[notes_input, brd_output, stories_output, before_after_output] ) demo.launch()