<reasoning_content>
*   Goal: Implement a basic job queue in Python.
    *   Requirements:
        1.  `JobQueue` class with `add_job(job_id, data)` and `get_result(job_id)`.
        2.  FIFO order for processing.
        3.  Storage for jobs until processed.
        4.  Unique `job_id`.
    *   Output format: Only Python code, no explanations.

    *   I need a way to store incoming jobs (a queue). `collections.deque` is good for FIFO.
    *   I need a way to store results once finished. A dictionary `{job_id: result}` is appropriate.
    *   Since the prompt implies "jobs should be processed", I need a mechanism to actually *process* them, even if the prompt doesn't explicitly define a `process_all` method. However, looking at the example usage, it shows adding a job and then getting a result "after processing". This suggests that the class might need an internal mechanism to run jobs or a method to trigger processing.
    *   Wait, looking closely at the example:
        `job_id = queue.add_job("job1", {"task": "process_data"})`
        `# After processing...`
        `result = queue.get_result("job1")`
    *   There's no explicit `process()` method in the requirements. If I don't provide a way to process, `get_result` will always return `None` (or an error) because nothing ever moves from the queue to the results map.
    *   Usually, in these types of prompts, a `process_next()` or `process_all()` method is implied to make the system functional, but the prompt strictly lists `add_job` and `get_result`.
    *   Let's look at the requirements again: "Jobs should be processed in FIFO order".
    *   To satisfy the "processed" requirement while sticking to the signature, I should probably include a `process_next()` method or handle processing internally. However, the user didn't ask for `process_next`.
    *   Let's consider how a simple Job Queue usually works:
        1. User adds job.
        2. Some worker (or method) takes job from queue, runs it, stores result.
        3. User asks for result.
    *   To make the code runnable/testable based on the example, I'll provide the requested methods and a `process_next` method so the user can actually complete a job. Or I could simulate an internal processor? No, `process_next` is clearer.
    *   Actually, I'll stick to the requirements exactly but include a `process_next` method to allow the "After processing..." part of the example to actually work.

    *Wait*, let's look at the prompt again: "Implement a basic job queue".
    If I only provide the two methods, the `get_result` will never find anything.
    Let's re-read: "Jobs should be processed in FIFO order".
    Maybe I should implement a `process_next` method.

    *Alternative Interpretation*: Maybe the `add_job` starts a thread? No, that's too complex for a "basic" request unless specified.
    Let's look at the `JobQueue` interface again.
    `add_job` -> returns id.
    `get_result` -> returns dict.

    Let's implement:
    - `self.queue`: A `collections.deque` of `(job_id, data)`.
    - `self.results`: A `dict` of `{job_id: result}`.
    - `process_next()`: Pops from queue, executes "task" (mocked), stores in results.

    Wait, the prompt says "Write only the Python code, no explanations."
    I will provide the class with the two requested methods plus a `process_next` method to make it functional for the example usage provided.

    *Self-Correction:* To strictly follow the prompt's requested methods but still make the example work, I'll add `process_next`.

    *Revised Plan:*
    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self.queue = deque()
            self.results = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self.queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self.queue:
                job_id, data = self.queue.popleft()
                # Simulate processing
                result = {"status": "completed", "data": data}
                self.results[job_id] = result

        def get_result(self, job_id: str) -> dict | None:
            return self.results.get(job_id)
    ```

    Wait, the prompt asks for `add_job` and `get_result`.
    If I include `process_next`, it's extra. If I don't, the example doesn't work.
    Actually, I'll just implement the two methods and a helper method to satisfy the "Jobs should be processed" clause.

    Wait, there's a way to interpret "Jobs should be processed in FIFO order" as a requirement for the *logic* of the queue structure, even if the user doesn't call a `process` method explicitly in the snippet. But a queue that never processes is just a list.

    I will include `process_next` as it's the standard way to interact with a queue like this.

    Actually, checking the prompt again:
    1. Create a `JobQueue` class with the following methods: [list of 2 methods]
    
    I should probably stick strictly to the methods requested but make sure the logic allows for processing.

    Wait, if I want to be helpful, I'll provide the two methods + `process_next`.

    Let's check requirements:
    1. `add_job`
    2. `get_result`
    3. FIFO order.
    4. Store internally.
    5. Unique job_id.

    I'll use a dict for results and a deque for the queue.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append({"id": job_id, "data": data})
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job = self._queue.popleft()
                # Simulate processing logic
                job_id = job["id"]
                data = job["data"]
                # Result format could be anything, let's just mirror the data
                self._results[job_id] = {"status": "success", "processed_data": data}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Actually, looking at the example:
    `# After processing...`
    This indicates that "processing" is an external action or an implied internal one. Since I'm writing the class, I should provide the means to process it.

    One more thought: Should I use a thread to process automatically? That makes it a real job queue. But "basic" usually implies synchronous calls. I'll stick to a simple `process_next`.

    Actually, I'll just stick to the two methods and a `process_next` method.

    Wait, the prompt says "Write only the Python code".

    Refining the code:
    - Use `deque` for FIFO.
    - Use `dict` for results.
    - Ensure uniqueness (though the user provides the ID in `add_job`, I should probably check if it exists or just trust the user as per the signature). The prompt says "Each job has a unique job_id that is returned". Actually, the signature is `add_job(job_id: str, ...)`, which means the user provides the ID.

    One detail: "return job_id" from `add_job`.

    Let's write it.
</reasoning_content>

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

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

    def add_job(self, job_id: str, data: dict) -> str:
        """
        Adds a job to the queue in FIFO order.
        """
        self._queue.append({"id": job_id, "data": data})
        return job_id

    def process_next(self) -> None:
        """
        Processes the next job in the queue and stores the result.
        """
        if self._queue:
            job = self._queue.popleft()
            job_id = job["id"]
            data = job["data"]
            
            # Simulate processing logic
            result = {
                "status": "completed",
                "processed_data": data
            }
            self._results[job_id] = result

    def get_result(self, job_id: str) -> dict | None:
        """
        Returns the result of a completed job by its ID.
        """
        return self._results.get(job_id)

# Example usage:
if __name__ == "__main__":
    queue = JobQueue()
    job_id = queue.add_job("job1", {"task": "process_data"})
    
    # Simulate the processing step
    queue.process_next()
    
    result = queue.get_result("job1")
    print(result)
```
</content>