<reasoning_content>
*   Class: `JobQueue`.
    *   Method 1: `add_job(job_id: str, data: dict) -> str`.
        *   Adds a job to a queue.
        *   Returns `job_id`.
        *   Note: Requirement 4 says `job_id` is unique and returned when added. Usually, `add_job` receives a `job_id`, but the prompt says "Each job has a unique job_id that is returned when added" and the example shows `queue.add_job("job1", ...)`. Wait, let's look at the signature: `add_job(job_id: str, data: dict) -> str`. This means the user provides the ID, and the function returns it.
    *   Method 2: `get_result(job_id: str) -> dict | None`.
        *   Returns result if processed, else `None`.
    *   Constraint 1: FIFO order.
    *   Constraint 2: Store jobs until processed.
    *   Requirement 3 (Implied): Since it's a *job queue*, there needs to be a way to process jobs. However, the prompt doesn't explicitly ask for a `process_next_job()` method, but mentions "After processing...". To make it functional, I should probably include a processing mechanism or a way to simulate it, but the prompt only specifies two methods.
    *   Refining `get_result`: If I only have `add_job` and `get_result`, how does the "processing" happen? Usually, a job queue has a worker or a `process` method.
    *   Let's re-read: "Jobs should be processed in FIFO order". If I don't have a `process` method in the signature, I should probably implement a simple internal simulation or a way for the user to trigger processing, OR just store the jobs and have a placeholder for the result.
    *   Actually, looking at the requirements:
        1. `add_job`
        2. `get_result`
        3. FIFO order.
        4. Store until processed.
    *   Since there is no `process` method requested in the specific method list, but "Jobs should be processed" is a requirement, I'll add a `process_jobs()` method to make the class actually useful, or I can assume a separate logic handles it. However, to satisfy "processed in FIFO order", I need a way to mark a job as finished.
    *   Wait, the prompt says: "Write only the Python code".
    *   Let's look at the example:
        `job_id = queue.add_job("job1", {"task": "process_data"})`
        `# After processing...`
        `result = queue.get_result("job1")`
    *   This implies that the "processing" happens outside or via some internal mechanism. To make `get_result` return anything, there must be a way to transition a job from "pending" to "completed".
    *   I will include a `process_next_job` method to fulfill the "FIFO" and "processed" requirements, even though it wasn't in the explicit list of two methods, because without it, `get_result` will always be `None`. Or I can just provide the two requested methods and a way to manually set a result for testing, but `process_next_job` is the standard way.
    *   Actually, let's stick strictly to the signature provided but ensure the logic supports FIFO. I'll add `process_next_job` to satisfy the "FIFO" requirement.

    *   `self.queue`: A `collections.deque` to store `(job_id, data)`.
    *   `self.results`: A `dict` to store `{job_id: result}`.

    *   `add_job(job_id, data)`:
        *   Check if `job_id` exists (optional but good).
        *   Append to `self.queue`.
        *   Return `job_id`.
    *   `get_result(job_id)`:
        *   Return `self.results.get(job_id)`.
    *   `process_next_job()` (Necessary for FIFO logic):
        *   Pop from left of `self.queue`.
        *   Do something (simulated).
        *   Store in `self.results`.

    Wait, if I only implement `add_job` and `get_result`, how does a job get processed?
    Maybe the user wants me to implement a `process` method too?
    The prompt says: "Create a `JobQueue` class with the following methods: [list of 2]".
    If I strictly follow "only these methods", `get_result` will always be `None`.
    However, "Jobs should be processed in FIFO order" is a requirement.
    To satisfy FIFO, I must have a way to process them.
    I'll include a `process_jobs` method to make it a functional "Job Queue".

    Wait, looking at the example:
    `# After processing...`
    This suggests that the processing happens elsewhere. But how does the `JobQueue` know it's done?
    Maybe I should provide a way to "complete" a job? No, that's not FIFO.
    I will implement `process_next_job` as it is the only logical way to satisfy the FIFO requirement.

    *   `collections.deque` for the queue.
    *   `dict` for results.
    *   `job_id` uniqueness: I should check if it exists.

    Actually, I'll just implement the two requested methods and a `process_next_job` method so the example actually works.

    Wait, I'll stick to the requested methods plus a `process_next_job` to satisfy the FIFO requirement.

    Let's refine:
    `add_job` -> adds to `deque`.
    `process_next_job` -> pops from `deque`, computes result, puts in `results` dict.
    `get_result` -> returns from `results` dict.

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

    ```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_job(self):
            if self.queue:
                job_id, data = self.queue.popleft()
                # Simulate processing
                result = {"status": "completed", "input": data}
                self.results[job_id] = result
            return None

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

    Wait, I'll check if I should strictly have ONLY those two methods.
    If I only have those two, the requirement "Jobs should be processed in FIFO order" is impossible to satisfy because there's no method to trigger processing.
    I'll include `process_next_job`.

    Actually, I'll check the prompt again.
    "Create a `JobQueue` class with the following methods: [list]"
    It doesn't say "ONLY the following methods".
    So I can add the processing method.

    Actually, let's make sure `job_id` is unique as per requirement 4.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._job_ids:
                raise ValueError("Job ID already exists")
            self._queue.append((job_id, data))
            self._job_ids.add(job_id)
            return job_id

        def process_next_job(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Simulate actual processing logic here
                self._results[job_id] = {"result": f"Processed {data.get('task', '')}"}

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

    Wait, the prompt doesn't say I need to handle errors or uniqueness strictly, but "Each job has a unique job_id".
    The prompt says: "Create a `JobQueue` class with the following methods".
    I'll stick to the requested methods + the processing one.

    One small detail: `dict | None` is Python 3.10+ syntax. I should use that.

    Actually, looking at the prompt "Write only the Python code", I should minimize extra fluff.

    Final structure:
    - `collections.deque` for FIFO.
    - `dict` for results.
    - `add_job`
    - `process_next_job` (implied by "processed in FIFO")
    - `get_result`

    Wait, if I don't provide `process_next_job`, how can I show it works? I'll provide it.

    Actually, I'll just stick to the two requested methods and see if it's enough. But "processed in FIFO order" strongly implies a `process` method exists.

    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[tuple[str, dict]] = deque()
        self._results: Dict[str, dict] = {}
        self._known_ids: set[str] = set()

    def add_job(self, job_id: str, data: dict) -> str:
        if job_id in self._known_ids:
            raise ValueError(f"Job ID '{job_id}' already exists.")
        
        self._queue.append((job_id, data))
        self._known_ids.add(job_id)
        return job_id

    def process_next_job(self) -> None:
        """
        Processes the next job in the queue (FIFO).
        This method is necessary to satisfy the FIFO requirement 
        and allow get_result to return data.
        """
        if self._queue:
            job_id, data = self._queue.popleft()
            # Simulate processing logic
            self._results[job_id] = {"status": "completed", "data": data}

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

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