<reasoning_content>
*   Goal: Implement a basic `JobQueue` class in Python.
    *   Methods required:
        *   `add_job(job_id: str, data: dict) -> str`: Add job, return `job_id`.
        *   `get_result(job_id: str) -> dict | None`: Get result of completed job.
    *   Constraint 1: FIFO order.
    *   Constraint 2: Store jobs internally until processed.
    *   Constraint 3: Unique `job_id`.
    *   Implicit Requirement: The prompt says "Jobs should be processed in FIFO order" and "After processing...", but doesn't provide a `process_job` method or an asynchronous worker. However, to satisfy the "get result" part, there must be a way for a job to transition from "pending" to "completed". Since the user didn't explicitly ask for a `process` method in the requirements list, but implied processing exists in the example, I should probably add a `process_next_job` method or similar to make the class functional/testable, OR assume the "processing" happens via some mechanism.
    *   Wait, looking closely at the prompt:
        1. `add_job`
        2. `get_result`
        The requirement says "Jobs should be processed in FIFO order". If I only have `add_job` and `get_result`, there's no way to trigger processing. I'll add a `process_next_job` method to make the logic complete, or perhaps the user expects me to simulate a worker?
    *   Actually, looking at standard interview/coding test patterns: Usually, there's a `process_job` method. Let's include a `process_next_job` method to ensure the "FIFO" requirement is actually demonstrable.

    *Refined plan:*
    *   `self.queue`: A `collections.deque` to store job IDs for FIFO order.
    *   `self.jobs`: A dictionary mapping `job_id` to its data/status/result.
    *   `add_job`: Append to deque, add to dict.
    *   `process_next_job`: Pop from deque, "process" (simulate), store result in dict.
    *   `get_result`: Check dict for result.

    *Wait, the requirements list is strict:*
    - `add_job`
    - `get_result`
    If I add `process_next_job`, I'm technically exceeding requirements but making the code usable. If I don't, the code is non-functional. I'll add a simple `process_next_job` method.

    *Self-Correction:* The prompt asks to "Implement a basic job queue". It doesn't explicitly forbid extra methods. To make "FIFO" and "get_result" meaningful, a mechanism to process must exist.

    *Implementation details:*
    - `from collections import deque`
    - `JobQueue` class.
    - `self.queue = deque()`
    - `self.jobs = {}` (stores `{job_id: {"data": data, "result": result, "status": status}}`)

    Wait, the prompt says `get_result` returns `dict | None`. This implies the result is a dict.

    Let's write the code.

    ```python
    from collections import deque
    from typing import Dict, Any, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {}  # Stores job details: {'data': dict, 'result': dict, 'status': str}

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError(f"Job ID {job_id} already exists.")
            
            self._jobs[job_id] = {
                "data": data,
                "result": None,
                "status": "pending"
            }
            self._queue.append(job_id)
            return job_id

        def process_next_job(self):
            """Processes the next job in the queue (FIFO)."""
            if not self._queue:
                return

            job_id = self._queue.popleft()
            job = self._jobs[job_id]
            
            # Simulate processing: return the data back or something similar
            # In a real scenario, this would be a worker function
            job["result"] = {"processed": True, "original_task": job["data"]}
            job["status"] = "completed"

        def get_result(self, job_id: str) -> Optional[Dict[str, Any]]:
            job = self._jobs.get(job_id)
            if job and job["status"] == "completed":
                return job["result"]
            return None
    ```

    Re-reading the requirements: "Jobs should be processed in FIFO order". It doesn't say *how*.
    I will add `process_next_job` to satisfy the logic.

    Wait, I should check if there's a way to do this without an explicit `process_next_job`. If it's a single-threaded object, the only way to "process" is to call a method.

    One more thought: Is it possible the user wants a worker thread? The prompt says "basic". A worker thread might be overkill. I'll stick to a method `process_next_job`.

    Actually, I'll provide the two requested methods and one helper method `process_next_job` to make it work.

    Final check on requirements:
    - `add_job(job_id: str, data: dict) -> str` (Done)
    - `get_result(job_id: str) -> dict | None` (Done)
    - FIFO (Done via deque)
    - Store internally (Done via dict/deque)
    - Unique job_id (Handled)

    Let's refine the `process_next_job` simulation so it makes sense for the example.

    ```python
    from collections import deque
    from typing import Dict, Any, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._jobs[job_id] = {"data": data, "result": None, "status": "pending"}
            self._queue.append(job_id)
            return job_id

        def process_next_job(self):
            if not self._queue:
                return
            
            job_id = self._queue.popleft()
            job = self._jobs[job_id]
            
            # Simulating task processing: just echo the data back as a result
            job["result"] = {"status": "success", "data": job["data"]}
            job["status"] = "completed"

        def get_result(self, job_id: str) -> Optional[Dict[str, Any]]:
            job = self._jobs.get(job_id)
            if job and job["status"] == "completed":
                return job["result"]
            return None
    ```

    Wait, if I add `process_next_job`, I am technically deviating from the strict "implement the following methods" part, but the "Jobs should be processed in FIFO" instruction is a *functional* requirement. I'll include it.

    Actually, looking at the example provided:
    ```python
    queue = JobQueue()
    job_id = queue.add_job("job1", {"task": "process_data"})
    # After processing...
    result = queue.get_result("job1")
    ```
    The comment `# After processing...` implies that processing is an external or implicit event. If I don't provide a way to process, the code is just a data structure that holds things. I'll add a `process_next_job` method.

    One detail: `get_result` should return `dict | None`. Using `Optional[dict]` is the same.

    Final Code Structure:
    `from collections import deque`
    `from typing import Dict, Any, Optional`
    `class JobQueue:` ...

    Wait, I'll make `process_next_job` more generic or just a simple placeholder.

    One edge case: If the user calls `add_job` with a duplicate ID. I should handle it.

    Final Polish:
    Requirement: "Write only the Python code, no explanations."

    ```python
    from collections import deque
    from typing import Dict, Any, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {}

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError(f"Job ID '{job_id}' already exists.")
            
            self._jobs[job_id] = {
                "data": data,
                "result": None,
                "completed": False
            }
            self._queue.append(job_id)
            return job_id

        def process_next_job(self):
            if not self._queue:
                return

            job_id = self._queue.popleft()
            job = self._jobs[job_id]
            
            # Simulation of processing logic
            job["result"] = {"processed_data": job["data"]}
            job["completed"] = True

        def get_result(self, job_id: str) -> Optional[Dict[str, Any]]:
            job = self._jobs.get(job_id)
            if job and job["completed"]:
                return job["result"]
            return None
    ```

    Wait, I should probably make `process_next_job` take a callback or something? No, the prompt is "basic". A simple simulation is fine.

    Actually, I'll check if there's any other way to interpret "Jobs should be processed in FIFO order". Maybe they want a background thread?
    "Implement a basic job queue".
    A background thread makes it a real queue.
    A method `process_next_job` makes it a manual queue.
    Given the "basic" constraint and the example usage, a manual method is safer.

    One more look: `get_result(job_id: str) -> dict | None`. Python 3.10+ syntax. I'll use that.

    Let's go.
</reasoning_content>

<content>
```python
from collections import deque
from typing import Dict, Any, Optional

class JobQueue:
    def __init__(self):
        self._queue = deque()
        self._jobs: Dict[str, Dict[str, Any]] = {}

    def add_job(self, job_id: str, data: dict) -> str:
        if job_id in self._jobs:
            raise ValueError(f"Job ID '{job_id}' already exists.")
        
        self._jobs[job_id] = {
            "data": data,
            "result": None,
            "completed": False
        }
        self._queue.append(job_id)
        return job_id

    def process_next_job(self):
        """
        Processes the next job in the queue using FIFO order.
        In a real application, this might involve running a worker function.
        """
        if not self._queue:
            return

        job_id = self._queue.popleft()
        job = self._jobs[job_id]
        
        # Simulating job processing logic
        # Here we simply return the data back in a result dictionary
        job["result"] = {"status": "success", "output": job["data"]}
        job["completed"] = True

    def get_result(self, job_id: str) -> Optional[Dict[str, Any]]:
        job = self._jobs.get(job_id)
        if job and job["completed"]:
            return job["result"]
        return None
```
</content>