Repository: mlgym/mlgym (language: python)

Issue:
Accelerate support, Modelboard progress and Checkpointing via RESTful API



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 40fecb18..c479bf82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,4 +128,4 @@ event_storage
 *.whl
 *.arrow
 transformer/
-
+core.*
\ No newline at end of file
diff --git a/example/bert/run.py b/example/bert/run.py
index cb25ffb4..0669168b 100644
--- a/example/bert/run.py
+++ b/example/bert/run.py
@@ -1,4 +1,3 @@
-
 from bert_lm_blueprint import BERTLMBluePrint
 from ml_gym.cmd_entrypoint.cmd import get_args, run
 
diff --git a/example/cnn_ddp/conv_net.py b/example/cnn_ddp/conv_net.py
index 189b176f..68e2319e 100644
--- a/example/cnn_ddp/conv_net.py
+++ b/example/cnn_ddp/conv_net.py
@@ -41,10 +41,13 @@ def forward_impl(self, inputs: torch.Tensor) -> Dict[str, torch.Tensor]:
         output = F.dropout(output, p=0.25, training=True)
 
         output = output.view(inputs.shape[0], -1)
-        output = self.fc_layers[0](output)
-        output = F.relu(output)
-        output = F.dropout(output, p=0.5, training=True)
-        output = self.fc_layers[1](output)
+
+        for layer in self.fc_layers[:-1]:
+            output = layer(output)
+            output = F.relu(output)
+            output = F.dropout(output, p=0.5, training=True)
+
+        output = self.fc_layers[-1](output)
 
         return {self.prediction_publication_key: output}
 
diff --git a/example/cnn_ddp/train_cnn_accelerate.sh b/example/cnn_ddp/train_cnn_accelerate.sh
index 6f0ec452..3b523c86 100644
--- a/example/cnn_ddp/train_cnn_accelerate.sh
+++ b/example/cnn_ddp/train_cnn_accelerate.sh
@@ -2,4 +2,5 @@
 #!/bin/sh
 
 
-accelerate launch --multi_gpu --num_processes=8 --gpu_ids 0,1,2,3,4,5,6,7 run.py --config_path /scratch/max/mlgym/example/cnn_ddp/run_config.yml
+accelerate launch --multi_gpu --gpu_ids "0,1,3" --num_processes=3  \
+                /scratch/max/mlgym/example/cnn_ddp/run.py --config_path /scratch/max/mlgym/example/cnn_ddp/run_config.yml
diff --git a/example/cnn_zero_stage_3/ac_config.yaml b/example/cnn_zero_stage_3/ac_config.yaml
new file mode 100644
index 00000000..0b61e4c1
--- /dev/null
+++ b/example/cnn_zero_stage_3/ac_config.yaml
@@ -0,0 +1,18 @@
+compute_environment: LOCAL_MACHINE
+deepspeed_config:
+  deepspeed_config_file: /scratch/max/mlgym/example/cnn_zero_stage_3/ds_config.json
+  zero3_init_flag: true
+distributed_type: DEEPSPEED
+downcast_bf16: "no"
+dynamo_backend: "NO"
+fsdp_config: {}
+machine_rank: 0
+main_training_function: main
+megatron_lm_config: {}
+num_machines: 1
+num_processes: 2
+rdzv_backend: static
+same_network: true
+use_cpu: false
+gpu_ids: "0,1"
+
diff --git a/example/cnn_zero_stage_3/cnn_experiment_config.yml b/example/cnn_zero_stage_3/cnn_experiment_config.yml
new file mode 100644
index 00000000..2a77b6dd
--- /dev/null
+++ b/example/cnn_zero_stage_3/cnn_experiment_config.yml
@@ -0,0 +1,267 @@
+global_config:
+  storage_connector_path: &storage_path_anchor ./file_storage/
+  seed: &seed_value 2
+  target_key: &target_key_anchor target_key
+  model_prediction_key: &model_prediction_key_anchor model_prediction_key
+  postprocessing_argmax_key: &postprocessing_argmax_key_anchor postprocessing_argmax_key
+
+dataset_repository:
+  component_type_key: DATASET_REPOSITORY
+  variant_key: DEFAULT
+  config:
+    storage_connector_path: *storage_path_anchor
+
+dataset_iterators:
+  component_type_key: DATASET_ITERATORS
+  variant_key: DEFAULT
+  requirements:
+    - name: repository
+      component_name: dataset_repository
+  config:
+    dataset_identifier: mnist
+    split_configs:
+      - split: train
+      - split: test
+
+splitted_dataset_iterators:
+  component_type_key: SPLITTED_DATASET_ITERATORS
+  variant_key: RANDOM
+  requirements:
+    - name: iterators
+      component_name: dataset_iterators
+      subscription:
+        - train
+        - test
+  config:
+    split_configs:
+      train:
+        train: 0.7
+        val: 0.3
+    seed: 2
+
+data_collator:
+  component_type_key: DATA_COLLATOR
+  variant_key: DEFAULT
+  config:
+    collator_type:
+      injectable:
+        id: id_conv_mnist_standard_collator
+    collator_params:
+      target_publication_key: *target_key_anchor
+
+data_loaders:
+  component_type_key: DATA_LOADER
+  variant_key: DEFAULT
+  requirements:
+    - name: iterators
+      component_name: splitted_dataset_iterators
+      subscription: [train, val, test]
+    - name: data_collator
+      component_name: data_collator
+  config:
+    batch_size: 100
+    weigthed_sampling_split_name: train
+    seeds:
+      train: 0
+      val: 1
+      test: 2
+
+model_registry:
+  component_type_key: MODEL_REGISTRY
+  variant_key: DEFAULT
+
+model:
+  component_type_key: MODEL
+  variant_key: DEFAULT
+  requirements:
+    - name: model_registry
+      component_name: model_registry
+      subscription: conv_net
+  config:
+    model_definition:
+      layer_config:
+        - params:
+            in_channels: 1
+            kernel_size: 3
+            out_channels: 32
+            stride: 1
+          type: conv
+        - params:
+            in_channels: 32
+            kernel_size: 3
+            out_channels: 64
+            stride: 1
+          type: conv
+        - params:
+            in_features: 9216
+            out_features: 128
+          type: fc
+        - params:
+            in_features: 128
+            out_features: 30000
+          type: fc
+        # - params:
+        #     in_features: 30000
+        #     out_features: 30000
+        #   type: fc
+        # - params:
+        #     in_features: 30000
+        #     out_features: 30000
+        #   type: fc
+        # - params:
+        #     in_features: 30000
+        #     out_features: 30000
+        #   type: fc
+        - params:
+            in_features: 30000
+            out_features: 10
+          type: fc
+    prediction_publication_keys:
+      prediction_publication_key: *model_prediction_key_anchor
+    seed: *seed_value
+
+optimizer:
+  component_type_key: OPTIMIZER
+  variant_key: DEFAULT
+  config:
+    optimizer_key: ADAM
+    params:
+      lr: 
+        sweep: absolute
+        values: [0.001] # [1, 0.1, 0.01, 0.001, 0.0001]
+
+lr_scheduler:
+  component_type_key: LR_SCHEDULER
+  variant_key: DEFAULT
+  config:
+    lr_scheduler_key: dummy
+
+loss_function_registry:
+  component_type_key: LOSS_FUNCTION_REGISTRY
+  variant_key: DEFAULT
+
+metric_registry:
+  component_type_key: METRIC_REGISTRY
+  variant_key: DEFAULT
+
+prediction_postprocessing_registry:
+  component_type_key: PREDICTION_POSTPROCESSING_REGISTRY
+  variant_key: DEFAULT
+
+train_component:
+  component_type_key: TRAIN_COMPONENT
+  variant_key: ACCELERATE
+  requirements:
+    - name: loss_function_registry
+      component_name: loss_function_registry
+    - name: prediction_postprocessing_registry
+      component_name: prediction_postprocessing_registry
+  config:
+    loss_fun_config:
+      prediction_subscription_key: *model_prediction_key_anchor
+      target_subscription_key: *target_key_anchor
+      key: CrossEntropyLoss
+      tag: cross_entropy_loss
+
+trainer:
+  component_type_key: TRAINER
+  variant_key: ACCELERATE
+  requirements:
+    - name: train_component
+      component_name: train_component
+    - name: model
+      component_name: model
+      subscription: null
+    - name: data_loaders
+      component_name: data_loaders
+      subscription: train
+
+eval_component:
+  component_type_key: EVAL_COMPONENT
+  variant_key: ACCELERATE
+  requirements:
+    - name: model
+      component_name: model
+      subscription: null
+    - name: data_loaders
+      component_name: data_loaders
+      subscription: [train, val, test]
+    - name: loss_function_registry
+      component_name: loss_function_registry
+    - name: metric_registry
+      component_name: metric_registry
+    - name: prediction_postprocessing_registry
+      component_name: prediction_postprocessing_registry
+  config:
+    cpu_target_subscription_keys:
+      - *target_key_anchor
+    cpu_prediction_subscription_keys:
+      - *postprocessing_argmax_key_anchor
+      - *model_prediction_key_anchor
+    post_processors_config:
+      - key: "ARG_MAX"
+        prediction_subscription_key: *model_prediction_key_anchor
+        prediction_publication_key: *postprocessing_argmax_key_anchor
+    metrics_config:
+      - key: F1_SCORE
+        params:
+          average: macro
+        prediction_subscription_key: *postprocessing_argmax_key_anchor
+        target_subscription_key: *target_key_anchor
+        tag: F1_SCORE_macro
+      - key: PRECISION
+        params:
+          average: macro
+        prediction_subscription_key: *postprocessing_argmax_key_anchor
+        target_subscription_key: *target_key_anchor
+        tag: PRECISION_macro
+      - key: RECALL
+        params:
+          average: macro
+        prediction_subscription_key: *postprocessing_argmax_key_anchor
+        target_subscription_key: *target_key_anchor
+        tag: RECALL_macro
+    loss_funs_config:
+      - prediction_subscription_key: *model_prediction_key_anchor
+        target_subscription_key: *target_key_anchor
+        key: CrossEntropyLoss
+        tag: cross_entropy_loss
+
+evaluator:
+  component_type_key: EVALUATOR
+  variant_key: ACCELERATE
+  requirements:
+    - name: eval_component
+      component_name: eval_component
+
+early_stopping_strategy_registry:
+  component_type_key: EARLY_STOPPING_STRATEGY_REGISTRY
+  variant_key: DEFAULT
+
+early_stopping_strategy:
+  component_type_key: EARLY_STOPPING_STRATEGY
+  variant_key: DEFAULT
+  requirements:
+    - name: early_stopping_strategy_registry
+      component_name: early_stopping_strategy_registry
+  config:
+    early_stopping_key: LAST_K_EPOCHS_IMPROVEMENT_STRATEGY
+    early_stopping_config:
+      min_relative_improvement: 0.00001
+      epochs_window: 5
+      split_name: val
+      monitoring_key: "F1_SCORE_macro"
+      is_increase_task: true
+
+checkpointing_strategy_registry:
+  component_type_key: CHECKPOINTING_STRATEGY_REGISTRY
+  variant_key: DEFAULT
+
+checkpointing_strategy:
+  component_type_key: CHECKPOINTING_STRATEGY
+  variant_key: DEFAULT
+  requirements:
+    - name: checkpointing_strategy_registry
+      component_name: checkpointing_strategy_registry
+  config:
+    checkpointing_key: SAVE_LAST_EPOCH_ONLY_CHECKPOINTING_STRATEGY
diff --git a/example/cnn_zero_stage_3/conv_net.py b/example/cnn_zero_stage_3/conv_net.py
new file mode 100644
index 00000000..68e2319e
--- /dev/null
+++ b/example/cnn_zero_stage_3/conv_net.py
@@ -0,0 +1,55 @@
+from ml_gym.models.nn.net import NNModel
+from typing import Dict
+from torch import nn
+import torch
+import torch.nn.functional as F
+
+
+class ConvNet(NNModel):
+
+    def __init__(self, prediction_publication_key: str, layer_config: Dict, seed: int = 0):
+        super().__init__(seed=seed)
+        self.prediction_publication_key = prediction_publication_key
+        self.conv_layers = nn.ModuleList([])
+        self.fc_layers = nn.ModuleList([])
+        for layer in layer_config:
+            if layer["type"] == "conv":
+                self.conv_layers.append(ConvNet.create_conv_layer_from_config(layer["params"]))
+            elif layer["type"] == "fc":
+                self.fc_layers.append(ConvNet.create_fc_layer_from_config(layer["params"]))
+
+    @staticmethod
+    def create_conv_layer_from_config(layer_dict) -> nn.Module:
+        return nn.Conv2d(in_channels=layer_dict["in_channels"],
+                         out_channels=layer_dict["out_channels"],
+                         kernel_size=layer_dict["kernel_size"],
+                         stride=layer_dict["stride"])
+
+    @staticmethod
+    def create_fc_layer_from_config(layer_dict) -> nn.Module:
+        return nn.Linear(in_features=layer_dict["in_features"],
+                         out_features=layer_dict["out_features"])
+
+    def forward_impl(self, inputs: torch.Tensor) -> Dict[str, torch.Tensor]:
+        output = inputs
+        output = self.conv_layers[0](output)
+        output = F.relu(output)
+
+        output = self.conv_layers[1](output)
+        output = F.relu(output)
+        output = F.max_pool2d(output, 2, 2)
+        output = F.dropout(output, p=0.25, training=True)
+
+        output = output.view(inputs.shape[0], -1)
+
+        for layer in self.fc_layers[:-1]:
+            output = layer(output)
+            output = F.relu(output)
+            output = F.dropout(output, p=0.5, training=True)
+
+        output = self.fc_layers[-1](output)
+
+        return {self.prediction_publication_key: output}
+
+    def forward(self, inputs: torch.Tensor) -> Dict[str, torch.Tensor]:
+        return self.forward_impl(inputs)
diff --git a/example/cnn_zero_stage_3/conv_net_blueprint.py b/example/cnn_zero_stage_3/conv_net_blueprint.py
new file mode 100644
index 00000000..ee740104
--- /dev/null
+++ b/example/cnn_zero_stage_3/conv_net_blueprint.py
@@ -0,0 +1,71 @@
+from typing import Dict, List, Any
+from ml_gym.modes import RunMode
+import torch
+from ml_gym.blueprints.constructables import ModelRegistryConstructable
+from ml_gym.blueprints.blue_prints import BluePrint
+from ml_gym.batching.batch import DatasetBatch
+from dataclasses import dataclass
+from ml_gym.blueprints.component_factory import ComponentFactory, Injector
+from ml_gym.data_handling.postprocessors.collator import Collator
+from conv_net import ConvNet
+
+
+@dataclass
+class MNISTCollator(Collator):
+    target_publication_key: str = None
+
+    def __call__(self, batch: List[torch.Tensor]):
+        """
+        :param batch: batch format [no_samples, height, width, channels]
+        :return:
+        """
+        # batch contains a list of tuples of structure (sequence, target)
+        inputs = [item[0].to(self.device) for item in batch]
+        inputs = torch.stack(inputs)
+        # transform into vector
+        # inputs = inputs.view(inputs.shape[0], -1)
+        # transform into CHW matrix
+        # inputs = inputs.permute(0, 3, 1, 2)
+        inputs = inputs.reshape(-1, 1, 28, 28)
+        targets_tensor = torch.tensor([item[1] for item in batch]).to(inputs[0].device)
+        target_partitions = {self.target_publication_key: targets_tensor}
+        return DatasetBatch(targets=target_partitions, tags=None, samples=inputs)
+
+
+@dataclass
+class MyModelRegistryConstructable(ModelRegistryConstructable):
+    def _construct_impl(self):
+        super()._construct_impl()
+        self.model_registry.add_class("conv_net", ConvNet)
+        return self.model_registry
+
+
+class ConvNetBluePrint(BluePrint):
+    def __init__(self, run_mode: RunMode, config: Dict, grid_search_id: str,
+                 experiment_id: str, external_injection: Dict[str, Any] = None, warm_start_epoch: int = 0):
+        super().__init__(run_mode, config, grid_search_id, experiment_id, external_injection, warm_start_epoch)
+
+    @staticmethod
+    def construct_components(config: Dict, component_names: List[str], device: torch.device,
+                             external_injection: Dict[str, Any] = None) -> Dict[str, Any]:
+        if external_injection is not None:
+            injection_mapping = {"id_conv_mnist_standard_collator": MNISTCollator,
+                                 "id_computation_device": device,
+                                 **external_injection}
+            injector = Injector(injection_mapping, raise_mapping_not_found=True)
+
+        else:
+            injection_mapping = {"id_conv_mnist_standard_collator": MNISTCollator}
+            injector = Injector(injection_mapping, raise_mapping_not_found=False)
+
+        component_factory = ComponentFactory(injector)
+        component_factory.register_component_type("MODEL_REGISTRY", "DEFAULT", MyModelRegistryConstructable)
+
+        components = component_factory.build_components_from_config(config, component_names)
+        return components
+
+    def construct(self, device: torch.device = None) -> Dict[str, Any]:
+        component_names = ["model", "trainer", "optimizer", "lr_scheduler", "evaluator", "early_stopping_strategy", "checkpointing_strategy"]
+        components = ConvNetBluePrint.construct_components(self.config, component_names, device, self.external_injection)
+
+        return components
diff --git a/example/cnn_zero_stage_3/ds_config.json b/example/cnn_zero_stage_3/ds_config.json
new file mode 100644
index 00000000..c502a389
--- /dev/null
+++ b/example/cnn_zero_stage_3/ds_config.json
@@ -0,0 +1,20 @@
+{
+    "bf16": {
+        "enabled": false 
+    },
+    "zero_optimization": {
+        "stage": 3,
+        "stage3_gather_16bit_weights_on_model_save": false,
+        "offload_optimizer": {
+            "device": "none"
+        },
+        "offload_param": {
+            "device": "none"
+        }
+    },
+    "gradient_clipping": 1.0,
+    "train_batch_size": "auto",
+    "train_micro_batch_size_per_gpu": "auto",
+    "gradient_accumulation_steps": 1,
+    "steps_per_print": 2000000
+}
\ No newline at end of file
diff --git a/example/cnn_zero_stage_3/run.py b/example/cnn_zero_stage_3/run.py
new file mode 100644
index 00000000..9cdd525b
--- /dev/null
+++ b/example/cnn_zero_stage_3/run.py
@@ -0,0 +1,8 @@
+
+from conv_net_blueprint import ConvNetBluePrint
+from ml_gym.cmd_entrypoint.cmd import get_args, run
+
+if __name__ == '__main__':
+    blueprint_class = ConvNetBluePrint
+    config_path = get_args()
+    run(blueprint_class=blueprint_class, run_configuration_file_path=config_path)
diff --git a/example/cnn_zero_stage_3/run_config.yml b/example/cnn_zero_stage_3/run_config.yml
new file mode 100644
index 00000000..974cce66
--- /dev/null
+++ b/example/cnn_zero_stage_3/run_config.yml
@@ -0,0 +1,14 @@
+run_configuration:
+  type: train # train, warmstart
+  config:
+    num_epochs: 10 # Number of epochs
+    # num_batches_per_epoch: 100
+    gs_config_path: /scratch/max/mlgym/example/cnn_zero_stage_3/cnn_experiment_config.yml
+
+environment:
+  type: accelerate # multiprocessing,  main_process, accelerate
+
+logging:
+  websocket_logging_servers: # List of websocket logging servers, e.g., http://127.0.0.1:9090 http://127.0.0.1:8080
+    - http://127.0.0.1:5002
+  gs_rest_api_endpoint: http://127.0.0.1:5001 # Endpoint for the grid search API, e.g., http://127.0.0.1:8080
diff --git a/example/cnn_zero_stage_3/train_cnn_accelerate.sh b/example/cnn_zero_stage_3/train_cnn_accelerate.sh
new file mode 100644
index 00000000..947b743f
--- /dev/null
+++ b/example/cnn_zero_stage_3/train_cnn_accelerate.sh
@@ -0,0 +1,6 @@
+ 
+#!/bin/sh
+
+
+accelerate launch --config_file /scratch/max/mlgym/example/cnn_zero_stage_3/ac_config.yaml \
+                /scratch/max/mlgym/example/cnn_zero_stage_3/run.py --config_path /scratch/max/mlgym/example/cnn_zero_stage_3/run_config.yml
diff --git a/example/grid_search_example/conv_net.py b/example/grid_search_example/conv_net.py
index 189b176f..68e2319e 100644
--- a/example/grid_search_example/conv_net.py
+++ b/example/grid_search_example/conv_net.py
@@ -41,10 +41,13 @@ def forward_impl(self, inputs: torch.Tensor) -> Dict[str, torch.Tensor]:
         output = F.dropout(output, p=0.25, training=True)
 
         output = output.view(inputs.shape[0], -1)
-        output = self.fc_layers[0](output)
-        output = F.relu(output)
-        output = F.dropout(output, p=0.5, training=True)
-        output = self.fc_layers[1](output)
+
+        for layer in self.fc_layers[:-1]:
+            output = layer(output)
+            output = F.relu(output)
+            output = F.dropout(output, p=0.5, training=True)
+
+        output = self.fc_layers[-1](output)
 
         return {self.prediction_publication_key: output}
 
diff --git a/example/grid_search_example/run_config.yml b/example/grid_search_example/run_config.yml
index ff7d7965..d7c9fc2b 100644
--- a/example/grid_search_example/run_config.yml
+++ b/example/grid_search_example/run_config.yml
@@ -1,14 +1,15 @@
 run_configuration:
   type: train # train, warmstart
   config:
-    num_epochs: 10 # Number of epochs
+    num_epochs: 50 # Number of epochs
+    num_batches_per_epoch: 100
     gs_config_path: ./gs_config.yml
 
 environment:
   type: multiprocessing # multiprocessing,  main_process, accelerate
   config:
-    process_count: 1 # Max. number of processes running at a time.
-    computation_device_ids: null # Indices of GPUs to distribute the GS over
+    process_count: 3 # Max. number of processes running at a time.
+    computation_device_ids: [0, 1, 5] # Indices of GPUs to distribute the GS over
 
 logging:
   websocket_logging_servers: # List of websocket logging servers, e.g., http://127.0.0.1:9090 http://127.0.0.1:8080
diff --git a/example/grid_search_example/train_gs_mnist_conv_net_websocket.sh b/example/grid_search_example/train_gs_mnist_conv_net_websocket.sh
index 008d1ba9..faf35e71 100644
--- a/example/grid_search_example/train_gs_mnist_conv_net_websocket.sh
+++ b/example/grid_search_example/train_gs_mnist_conv_net_websocket.sh
@@ -2,9 +2,4 @@
 #!/bin/sh
 
 
-python run.py --process_count 21 \
-              --num_epochs 100 \
-              --websocket_logging_servers http://127.0.0.1:5002 \
-              --gs_rest_api_endpoint http://127.0.0.1:5001 \
-              train \
-              --gs_config_path gs_config.yml
+python run.py --config_path run_config.yml
diff --git a/src/ml_board/README.md b/src/ml_board/README.md
index df2d86fa..1b926acd 100644
--- a/src/ml_board/README.md
+++ b/src/ml_board/README.md
@@ -74,7 +74,7 @@ payload:
 
 Fetch System details like CPU & GPU for model card.
 
-_GET /system-info_
+_GET /system-info/<grid_search_id>/<experiment_id>_
 
 ```json
 {
@@ -83,6 +83,8 @@ _GET /system-info_
     "architecture": <system architecture ex: AMD64>,
     "processor": <processor name>,
     "ram": <ram in GB>,
+    "python-version": <version>,
+    "python-packages": <List of pip packages>,
     "CUDNN_version":<cudnn version>,
     "num_cuda_device": <number of cuda devices>,
     "cuda_device_list": [
@@ -101,6 +103,7 @@ _GET /grid_searches/<grid_search_id>/experiments_
 
 TODO need to check "experiments" in URL
 
+TODO need to check "experiments" in URL  
 ```json
 [
     {
@@ -120,15 +123,15 @@ Available <checkpoint_resource> = [model, optimizer, stateful_component, lr_sche
 
 Get all Checkpoint names for single experiment:
 
-_GET /checkpoints/<grid_search_id>/<experiment_id>_
+_GET /checkpoint_list/<grid_search_id>/<experiment_id>_
 
-_Ex: GET /checkpoints/2023-04-12--23-42-17/0_
+_Ex: GET /checkpoint_list/2023-04-12--23-42-17/0_
 
 Get all Checkpoint names for single epoch in an experiment:
 
-_GET /checkpoints/<grid_search_id>/<experiment_id>/<checkpoint_id>_
+_GET /checkpoint_list/<grid_search_id>/<experiment_id>/<checkpoint_id>_
 
-_Ex: GET /checkpoints/2023-04-12--23-42-17/0/0_
+_Ex: GET /checkpoint_list/2023-04-12--23-42-17/0/0_
 
 Return:
 ```json
@@ -182,6 +185,17 @@ _DELETE /checkpoints/<grid_search_id><experiment_id>/<checkpoint_id>/<checkpoint
 
 _Ex: DELETE /checkpoints/2023-04-12--23-42-17/0/0/model_
 
+*PUT /checkpoints/<grid_search_id><experiment_id>/<checkpoint_id>/<model, optimizer, lr_scheduler, stateful_component>*
+```json
+{
+        "<model, optimizer, lr_scheduler, stateful_component>": <binary stream>,
+        "chunk_id": <chunk_id>,
+        "num_chunks": <num_chunks>
+```
+
+*DELETE /checkpoints/<grid_search_id><experiment_id>/<checkpoint_id>*
+
+
 ## Websocket API
 
 Every event message has the following structure:
@@ -309,6 +323,12 @@ metric scores of a model at a specific epoch.
 
 # Implementation idea:
 
+**Checkpointing (legacy)**:
+
+After each epoch and if condition is fulfilled (based on strategy), the model is binarized and sent to the server as a checkpoint.
+
+Create checkpoint:
+
 Add subscribers to trainer, evaluator and gymjob. We need to inject them via
 For trainer and evaluator we add the subscribers within the blueprints. E.g., trainer.add_subscriber(subscriber)
 
diff --git a/src/ml_board/backend/dummy_server/dummy_server.py b/src/ml_board/backend/dummy_server/dummy_server.py
index ffc95b52..7d11c802 100644
--- a/src/ml_board/backend/dummy_server/dummy_server.py
+++ b/src/ml_board/backend/dummy_server/dummy_server.py
@@ -151,7 +151,7 @@ def terminate(text:str) -> None:
         # run the Server
         WebSocketWrapper(
             flask_app=create_flask_server(),
-            host="localhost",
+            host="127.0.0.1",
             port=args.get('port', 7000),
             async_mode=None,
             message_delay=float(args.get('delay', 0.1)),  # in seconds
diff --git a/src/ml_board/backend/restful_api/data_access.py b/src/ml_board/backend/restful_api/data_access.py
index 0e20db59..bf0cabcb 100644
--- a/src/ml_board/backend/restful_api/data_access.py
+++ b/src/ml_board/backend/restful_api/data_access.py
@@ -4,6 +4,7 @@
 import re
 import shutil
 from typing import Dict, List
+from fastapi import UploadFile
 from ml_gym.error_handling.exception import InvalidPathError
 import json
 from ml_board.backend.restful_api.data_models import RawTextFile, CheckpointResource, ExperimentStatus
@@ -41,21 +42,17 @@ def get_checkpoint_list(self, grid_search_id: str, experiment_id: str) -> List[D
         raise NotImplementedError
 
     @abstractmethod
-    def get_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource
-    ) -> Generator:
+    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str,
+                                checkpoint_resource: str) -> Generator:
         raise NotImplementedError
 
     @abstractmethod
-    def add_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource, payload_pickle: bytes
-    ) -> None:
+    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_file: UploadFile) -> None:
         raise NotImplementedError
 
     @abstractmethod
     def delete_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource
-    ) -> None:
+        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: str) -> None:
         raise NotImplementedError
 
     @abstractmethod
@@ -261,7 +258,7 @@ def get_checkpoint_dict_epoch(self, grid_search_id: str, experiment_id: str, epo
             files = FileDataAccess.get_checkpoint_files(requested_full_path, base_path=self.top_level_logging_path)
             for file_num in range(len(files)):
                 split = os.path.normpath(files[file_num]).split(os.sep)
-                checkpoints.append(os.path.basename(split[-1]).split(".")[0])
+                checkpoints.append(os.path.basename(split[-1]))
             response.append({"experiment_id": experiment_id, "epoch": epoch, "checkpoints": checkpoints})
             return response
 
@@ -292,9 +289,9 @@ def get_checkpoint_list(self, grid_search_id: str, experiment_id) -> List[Dict]:
                     response.append({"experiment_id": experiment_id, "epoch": last_epoch, "checkpoints": checkpoints})
                     last_epoch = epoch
                     checkpoints = []
-                    checkpoints.append(os.path.basename(split[-1]).split(".")[0])
+                    checkpoints.append(os.path.basename(split[-1]))
                 else:
-                    checkpoints.append(os.path.basename(split[-1]).split(".")[0])
+                    checkpoints.append(os.path.basename(split[-1]))
 
             response.append({"experiment_id": experiment_id, "epoch": epoch, "checkpoints": checkpoints})
             return response
@@ -303,7 +300,7 @@ def get_checkpoint_list(self, grid_search_id: str, experiment_id) -> List[Dict]:
             raise InvalidPathError(f"File path {requested_full_path} is not safe.")
 
     def get_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource
+        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: str
     ) -> Generator:
         """
         Fetch checkpoint resource pickle file given the experiment ID & grid search ID from event storage.
@@ -316,9 +313,8 @@ def get_checkpoint_resource(
 
         :returns: bytes response of pickle file
         """
-        requested_full_path = os.path.realpath(
-            os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch), f"{checkpoint_resource}.pickle")
-        )
+        requested_full_path = os.path.realpath(os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id),
+                                                            str(epoch), checkpoint_resource))
         if FileDataAccess.is_safe_path(base_dir=self.top_level_logging_path, requested_path=requested_full_path):
             if not os.path.isfile(requested_full_path):
                 raise InvalidPathError(f"Resource {requested_full_path} not found.")
@@ -328,9 +324,7 @@ def get_checkpoint_resource(
         else:
             raise InvalidPathError(f"File path {requested_full_path} is not safe.")
 
-    def add_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource, payload_pickle: bytes
-    ) -> None:
+    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_file: UploadFile) -> None:
         """
         Add a checkpoint resource pickle file given the epoch, experiment ID & grid search ID to event storage.
 
@@ -343,15 +337,14 @@ def add_checkpoint_resource(
 
         :returns: Pickle file Stream response
         """
-
-        requested_full_path = os.path.realpath(
-            os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch), f"{checkpoint_resource}.pickle")
-        )
+        file_name = checkpoint_file.filename
+        file_path = os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch), file_name)
+        requested_full_path = os.path.realpath(file_path)
 
         if FileDataAccess.is_safe_path(base_dir=self.top_level_logging_path, requested_path=requested_full_path):
             os.makedirs(os.path.dirname(requested_full_path), exist_ok=True)
             with open(requested_full_path, "wb") as fd:
-                fd.write(payload_pickle)
+                shutil.copyfileobj(checkpoint_file.file, fd)
         else:
             raise InvalidPathError(f"File path {requested_full_path} is not safe.")
 
@@ -365,18 +358,16 @@ def delete_checkpoints(self, grid_search_id: str, experiment_id: str, epoch: str
             - epoch (str): Epoch number
         """
 
-        requested_full_path = os.path.realpath(
-            os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch))
-        )
+        requested_full_path = os.path.realpath(os.path.join(self.top_level_logging_path, str(grid_search_id),
+                                                            str(experiment_id), str(epoch)))
 
         if FileDataAccess.is_safe_path(base_dir=self.top_level_logging_path, requested_path=requested_full_path):
             shutil.rmtree(requested_full_path)
         else:
-            raise FileNotFoundError(f"Folder in path {requested_full_path} not found.")
+            raise InvalidPathError(f"The path {requested_full_path} is not safe.")
 
-    def delete_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource
-    ) -> None:
+    def delete_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str,
+                                   checkpoint_resource: str) -> None:
         """
         Delete checkpoint resource pickle file from the event storage
         given the epoch, experiment ID & grid search ID.
@@ -388,13 +379,12 @@ def delete_checkpoint_resource(
         """
 
         folder_path = os.path.realpath(os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch)))
-        requested_full_path = os.path.realpath(
-            os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id), str(epoch), f"{checkpoint_resource}.pickle")
-        )
+        requested_full_path = os.path.realpath(os.path.join(self.top_level_logging_path, str(grid_search_id), str(experiment_id),
+                                                            str(epoch), checkpoint_resource))
 
         if FileDataAccess.is_safe_path(base_dir=self.top_level_logging_path, requested_path=requested_full_path):
             os.remove(requested_full_path)
             if len([name for name in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, name))]) == 0:
                 os.rmdir(folder_path)
         else:
-            raise FileNotFoundError(f"File in path {requested_full_path} not found.")
\ No newline at end of file
+            raise InvalidPathError(f"Path {requested_full_path} is not safe.")
diff --git a/src/ml_board/backend/restful_api/data_models.py b/src/ml_board/backend/restful_api/data_models.py
index 238f63dc..32410129 100644
--- a/src/ml_board/backend/restful_api/data_models.py
+++ b/src/ml_board/backend/restful_api/data_models.py
@@ -19,8 +19,10 @@ class ExperimentStatus(BaseModel):
     experiment_config: Dict
 
 
+# TODO Check if Enum is still needed
 class CheckpointResource(str, Enum):
     model = "model"
     optimizer = "optimizer"
     lr_scheduler = "lr_scheduler"
     stateful_components = "stateful_components"
+    accelerate = "accelerate_zip"
diff --git a/src/ml_board/backend/restful_api/restful_api_server.py b/src/ml_board/backend/restful_api/restful_api_server.py
index 5d54bc00..30b878c9 100644
--- a/src/ml_board/backend/restful_api/restful_api_server.py
+++ b/src/ml_board/backend/restful_api/restful_api_server.py
@@ -1,5 +1,4 @@
-import base64
-from fastapi import FastAPI, File
+from fastapi import FastAPI, UploadFile
 from fastapi import status, HTTPException
 from fastapi.responses import StreamingResponse
 from ml_board.backend.restful_api.data_access import DataAccessIF
@@ -53,7 +52,7 @@ def __init__(self, data_access: DataAccessIF):
             endpoint=self.get_checkpoint_resource,
         )
         self.app.add_api_route(
-            path="/checkpoints/{grid_search_id}/{experiment_id}/{epoch}/{checkpoint_resource}",
+            path="/checkpoints/{grid_search_id}/{experiment_id}/{epoch}",
             methods=["POST"],
             endpoint=self.add_checkpoint_resource,
         )
@@ -211,7 +210,7 @@ def get_checkpoint_list(self, grid_search_id: str, experiment_id: str):
                 status_code=status.HTTP_403_FORBIDDEN, detail="Provided invalid parameters for fetching checkpoint list."
             ) from e
 
-    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource):
+    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: str):
         """
         ``HTTP GET`` Fetch checkpoint resource pickle file
           given the experiment ID & grid search ID.
@@ -232,7 +231,7 @@ def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch
         except InvalidPathError as e:
             raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Provided invalid parameters for checkpoint resource.") from e
 
-    def delete_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource):
+    def delete_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: str):
         """
         ``HTTP DELETE`` Delete checkpoint resource pickle file
           given the epoch, experiment ID & grid search ID.
@@ -243,12 +242,14 @@ def delete_checkpoint_resource(self, grid_search_id: str, experiment_id: str, ep
             - checkpoint_resource (CheckpointResource) : CheckpointResource type
         """
         try:
-            self.data_access.delete_checkpoint_resource(
-                grid_search_id=grid_search_id, experiment_id=experiment_id, epoch=epoch, checkpoint_resource=checkpoint_resource
-            )
-
+            self.data_access.delete_checkpoint_resource(grid_search_id=grid_search_id, experiment_id=experiment_id,
+                                                        epoch=epoch, checkpoint_resource=checkpoint_resource)
+        except FileNotFoundError as e:
+            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
+                                detail=f"Checkpoint resource {grid_search_id}/{experiment_id}/{epoch}/{checkpoint_resource} not found.") from e
         except InvalidPathError as e:
-            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Provided invalid parameters for checkpoint resource.") from e
+            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN,
+                                detail=f"Provided invalid parameters for checkpoint resource {grid_search_id}/{experiment_id}/{epoch}/{checkpoint_resource}.") from e
 
     def delete_checkpoints(self, grid_search_id: str, experiment_id: str, epoch: str):
         """
@@ -261,13 +262,14 @@ def delete_checkpoints(self, grid_search_id: str, experiment_id: str, epoch: str
         """
         try:
             self.data_access.delete_checkpoints(grid_search_id=grid_search_id, experiment_id=experiment_id, epoch=epoch)
-
+        except FileNotFoundError as e:
+            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
+                                detail=f"Checkpoint resource {grid_search_id}/{experiment_id}/{epoch} not found.") from e
         except InvalidPathError as e:
-            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Provided invalid parameters for checkpoint resource.") from e
+            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN,
+                                detail=f"Provided invalid parameters for checkpoint resource {grid_search_id}/{experiment_id}/{epoch}.") from e
 
-    def add_checkpoint_resource(
-        self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_resource: CheckpointResource, file: bytes = File(...)
-    ):
+    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: str, checkpoint_file: UploadFile):
         """
         ``HTTP POST`` Add a checkpoint resource pickle file
           given the epoch, experiment ID & grid search ID.
@@ -277,23 +279,16 @@ def add_checkpoint_resource(
             - epoch (str): Epoch number
             - checkpoint_resource (CheckpointResource) : CheckpointResource type
             - file (bytes): Pickle file to be added
-
-        :returns: Pickle file Stream response
         """
         try:
-            payload_pickle = base64.b64decode(file)
-            self.data_access.add_checkpoint_resource(
-                grid_search_id=grid_search_id,
-                experiment_id=experiment_id,
-                epoch=epoch,
-                checkpoint_resource=checkpoint_resource,
-                payload_pickle=payload_pickle,
-            )
+            self.data_access.add_checkpoint_resource(grid_search_id=grid_search_id,
+                                                     experiment_id=experiment_id,
+                                                     epoch=epoch,
+                                                     checkpoint_file=checkpoint_file)
         except InvalidPathError as e:
-            raise HTTPException(
-                status_code=status.HTTP_403_FORBIDDEN,
-                detail=f"Provided invalid payload or grid_search_id {grid_search_id}, experiment_id {experiment_id} or epoch {epoch}.",
-            ) from e
+            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN,
+                                detail=f"Provided invalid payload or grid_search_id {grid_search_id}, experiment_id {experiment_id} or epoch {epoch}.",
+                                ) from e
     
     def get_system_info(self, grid_search_id: str, experiment_id: str):
         """
diff --git a/src/ml_board/frontend/Dashboard/package-lock.json b/src/ml_board/frontend/Dashboard/package-lock.json
index b0614fa8..f299a4fc 100644
--- a/src/ml_board/frontend/Dashboard/package-lock.json
+++ b/src/ml_board/frontend/Dashboard/package-lock.json
@@ -16,9 +16,12 @@
         "@testing-library/jest-dom": "^5.16.5",
         "@testing-library/react": "^13.4.0",
         "@testing-library/user-event": "^13.5.0",
+        "@textea/json-viewer": "^2.16.2",
         "ag-grid-community": "^28.2.1",
         "ag-grid-react": "^28.2.1",
+        "axios": "^1.3.4",
         "history": "^4.7.2",
+        "moment": "^2.29.4",
         "react": "^18.2.0",
         "react-chartjs-2": "^5.1.0",
         "react-dom": "^18.2.0",
@@ -1814,9 +1817,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.20.13",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
-      "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+      "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
       "dependencies": {
         "regenerator-runtime": "^0.13.11"
       },
@@ -2147,12 +2150,11 @@
       }
     },
     "node_modules/@emotion/babel-plugin": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
-      "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
+      "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==",
       "dependencies": {
         "@babel/helper-module-imports": "^7.16.7",
-        "@babel/plugin-syntax-jsx": "^7.17.12",
         "@babel/runtime": "^7.18.3",
         "@emotion/hash": "^0.9.0",
         "@emotion/memoize": "^0.8.0",
@@ -2163,9 +2165,6 @@
         "find-root": "^1.1.0",
         "source-map": "^0.5.7",
         "stylis": "4.1.3"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
       }
     },
     "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
@@ -2218,12 +2217,12 @@
       "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
     },
     "node_modules/@emotion/react": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz",
-      "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz",
+      "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
-        "@emotion/babel-plugin": "^11.10.5",
+        "@emotion/babel-plugin": "^11.10.6",
         "@emotion/cache": "^11.10.5",
         "@emotion/serialize": "^1.1.1",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
@@ -2232,13 +2231,9 @@
         "hoist-non-react-statics": "^3.3.1"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0",
         "react": ">=16.8.0"
       },
       "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
         "@types/react": {
           "optional": true
         }
@@ -2262,26 +2257,22 @@
       "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
     },
     "node_modules/@emotion/styled": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.5.tgz",
-      "integrity": "sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz",
+      "integrity": "sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
-        "@emotion/babel-plugin": "^11.10.5",
+        "@emotion/babel-plugin": "^11.10.6",
         "@emotion/is-prop-valid": "^1.2.0",
         "@emotion/serialize": "^1.1.1",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
         "@emotion/utils": "^1.2.0"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0",
         "@emotion/react": "^11.0.0-rc.0",
         "react": ">=16.8.0"
       },
       "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
         "@types/react": {
           "optional": true
         }
@@ -2332,11 +2323,6 @@
         "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
-    },
     "node_modules/@eslint/eslintrc/node_modules/globals": {
       "version": "13.20.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@@ -2351,17 +2337,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/@eslint/eslintrc/node_modules/type-fest": {
       "version": "0.20.2",
       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -2418,6 +2393,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
     "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -2438,6 +2421,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -3161,15 +3156,15 @@
       "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
     },
     "node_modules/@mui/base": {
-      "version": "5.0.0-alpha.117",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.117.tgz",
-      "integrity": "sha512-3GlRSZdSrvDQ4k03dSV2rM+97JbNWimFOqGsE7n7Mi8WuBSYCgnPe56bQp3E5cShOrTn11dGH8FRCmVMcCEXqQ==",
+      "version": "5.0.0-alpha.123",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.123.tgz",
+      "integrity": "sha512-pxzcAfET3I6jvWqS4kijiLMn1OmdMw+mGmDa0SqmDZo3bXXdvLhpCCPqCkULG3UykhvFCOcU5HclOX3JCA+Zhg==",
       "dependencies": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@emotion/is-prop-valid": "^1.2.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
-        "@popperjs/core": "^2.11.6",
+        "@mui/utils": "^5.11.13",
+        "@popperjs/core": "^2.11.7",
         "clsx": "^1.2.1",
         "prop-types": "^15.8.1",
         "react-is": "^18.2.0"
@@ -3198,9 +3193,9 @@
       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.8.tgz",
-      "integrity": "sha512-n/uJRIwZAaJaROaOA4VzycxDo27cusnrRzfycnAkAP5gBndwOJQ1CXjd1Y7hJe5eorj/ukixC7IZD+qCClMCMg==",
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.15.tgz",
+      "integrity": "sha512-Q0e2oBsjHyIWWj1wLzl14btunvBYC0yl+px7zL9R69tF87uenj6q72ieS369BJ6jxYpJwvXfR6/f+TC+ZUsKKg==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui"
@@ -3232,16 +3227,16 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.8.tgz",
-      "integrity": "sha512-MpIVmtj9VJBhPHvPWoMkfCPpmVGXT4q43PtCJsdKIdc7W9/nG3Kpqw2oWyw+UxG5xG7eLhmfRFGPKvj4/WopEQ==",
-      "dependencies": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/base": "5.0.0-alpha.117",
-        "@mui/core-downloads-tracker": "^5.11.8",
-        "@mui/system": "^5.11.8",
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.15.tgz",
+      "integrity": "sha512-E5RbLq9/OvRKmGyeZawdnmFBCvhKkI/Zqgr0xFqW27TGwKLxObq/BreJc6Uu5Sbv8Fjj34vEAbRx6otfOyxn5w==",
+      "dependencies": {
+        "@babel/runtime": "^7.21.0",
+        "@mui/base": "5.0.0-alpha.123",
+        "@mui/core-downloads-tracker": "^5.11.15",
+        "@mui/system": "^5.11.15",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
+        "@mui/utils": "^5.11.13",
         "@types/react-transition-group": "^4.4.5",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
@@ -3281,12 +3276,12 @@
       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
     },
     "node_modules/@mui/private-theming": {
-      "version": "5.11.7",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz",
-      "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==",
+      "version": "5.11.13",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.13.tgz",
+      "integrity": "sha512-PJnYNKzW5LIx3R+Zsp6WZVPs6w5sEKJ7mgLNnUXuYB1zo5aX71FVLtV7geyPXRcaN2tsoRNK7h444ED0t7cIjA==",
       "dependencies": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/utils": "^5.11.7",
+        "@babel/runtime": "^7.21.0",
+        "@mui/utils": "^5.11.13",
         "prop-types": "^15.8.1"
       },
       "engines": {
@@ -3307,11 +3302,11 @@
       }
     },
     "node_modules/@mui/styled-engine": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.8.tgz",
-      "integrity": "sha512-iSpZp9AoeictsDi5xAQ4PGXu7mKtQyzMl7ZaWpHIGMFpsNnfY3NQNg+wkj/gpsAZ+Zg+IIyD+t+ig71Kr9fa0w==",
+      "version": "5.11.11",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.11.tgz",
+      "integrity": "sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==",
       "dependencies": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@emotion/cache": "^11.10.5",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
@@ -3338,15 +3333,15 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.8.tgz",
-      "integrity": "sha512-zhroUcxAw2x/dISBJKhGbD70DOYCwMFRo7o/LUYTiUfQkfmLhRfEf1bopWgY9nYstn7QOxOq9fA3aR3pHrUTbw==",
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.15.tgz",
+      "integrity": "sha512-vCatoWCTnAPquoNifHbqMCMnOElEbLosVUeW0FQDyjCq+8yMABD9E6iY0s14O7iq1wD+qqU7rFAuDIVvJ/AzzA==",
       "dependencies": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/private-theming": "^5.11.7",
-        "@mui/styled-engine": "^5.11.8",
+        "@babel/runtime": "^7.21.0",
+        "@mui/private-theming": "^5.11.13",
+        "@mui/styled-engine": "^5.11.11",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
+        "@mui/utils": "^5.11.13",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
@@ -3390,11 +3385,11 @@
       }
     },
     "node_modules/@mui/utils": {
-      "version": "5.11.7",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz",
-      "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==",
+      "version": "5.11.13",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz",
+      "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==",
       "dependencies": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@types/prop-types": "^15.7.5",
         "@types/react-is": "^16.7.1 || ^17.0.0",
         "prop-types": "^15.8.1",
@@ -3526,9 +3521,9 @@
       }
     },
     "node_modules/@popperjs/core": {
-      "version": "2.11.6",
-      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
-      "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+      "version": "2.11.7",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
+      "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/popperjs"
@@ -4084,6 +4079,23 @@
         "@testing-library/dom": ">=7.21.4"
       }
     },
+    "node_modules/@textea/json-viewer": {
+      "version": "2.16.2",
+      "resolved": "https://registry.npmjs.org/@textea/json-viewer/-/json-viewer-2.16.2.tgz",
+      "integrity": "sha512-ZPRNpcLJ+hOO6C8tzkQvltRSp7Ol73j8oJ1+w5mWVWn+JR1QqzZJJgBqnNZvs3S1h/jJAjwyeZUVlb0VtIJcvg==",
+      "dependencies": {
+        "@emotion/react": "^11.10.6",
+        "@emotion/styled": "^11.10.6",
+        "@mui/material": "^5.11.14",
+        "clsx": "^1.2.1",
+        "copy-to-clipboard": "^3.3.3",
+        "zustand": "^4.3.6"
+      },
+      "peerDependencies": {
+        "react": "^17 || ^18",
+        "react-dom": "^17 || ^18"
+      }
+    },
     "node_modules/@tootallnate/once": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -5326,12 +5338,9 @@
       "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
     },
     "node_modules/argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dependencies": {
-        "sprintf-js": "~1.0.2"
-      }
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
     "node_modules/aria-query": {
       "version": "5.1.3",
@@ -5515,6 +5524,29 @@
         "node": ">=4"
       }
     },
+    "node_modules/axios": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
+      "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
+      "dependencies": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/axios/node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/axobject-query": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
@@ -6412,6 +6444,14 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
     },
+    "node_modules/copy-to-clipboard": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+      "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+      "dependencies": {
+        "toggle-selection": "^1.0.6"
+      }
+    },
     "node_modules/core-js": {
       "version": "3.27.2",
       "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.2.tgz",
@@ -8111,11 +8151,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/eslint/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
-    },
     "node_modules/eslint/node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -8180,17 +8215,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/eslint/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/eslint/node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -12107,12 +12131,11 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
     },
     "node_modules/js-yaml": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dependencies": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "argparse": "^2.0.1"
       },
       "bin": {
         "js-yaml": "bin/js-yaml.js"
@@ -12654,6 +12677,14 @@
         "mkdirp": "bin/cmd.js"
       }
     },
+    "node_modules/moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -14593,6 +14624,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/psl": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -16346,6 +16382,14 @@
         "node": ">=4.0.0"
       }
     },
+    "node_modules/svgo/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
     "node_modules/svgo/node_modules/css-select": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
@@ -16391,6 +16435,18 @@
       "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
       "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
     },
+    "node_modules/svgo/node_modules/js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/svgo/node_modules/nth-check": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
@@ -16625,6 +16681,11 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/toggle-selection": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+      "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+    },
     "node_modules/toidentifier": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -17993,6 +18054,29 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zustand": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz",
+      "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==",
+      "dependencies": {
+        "use-sync-external-store": "1.2.0"
+      },
+      "engines": {
+        "node": ">=12.7.0"
+      },
+      "peerDependencies": {
+        "immer": ">=9.0",
+        "react": ">=16.8"
+      },
+      "peerDependenciesMeta": {
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
     }
   },
   "dependencies": {
@@ -19183,9 +19267,9 @@
       }
     },
     "@babel/runtime": {
-      "version": "7.20.13",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
-      "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+      "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
       "requires": {
         "regenerator-runtime": "^0.13.11"
       }
@@ -19359,12 +19443,11 @@
       "requires": {}
     },
     "@emotion/babel-plugin": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
-      "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
+      "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==",
       "requires": {
         "@babel/helper-module-imports": "^7.16.7",
-        "@babel/plugin-syntax-jsx": "^7.17.12",
         "@babel/runtime": "^7.18.3",
         "@emotion/hash": "^0.9.0",
         "@emotion/memoize": "^0.8.0",
@@ -19420,12 +19503,12 @@
       "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
     },
     "@emotion/react": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz",
-      "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz",
+      "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==",
       "requires": {
         "@babel/runtime": "^7.18.3",
-        "@emotion/babel-plugin": "^11.10.5",
+        "@emotion/babel-plugin": "^11.10.6",
         "@emotion/cache": "^11.10.5",
         "@emotion/serialize": "^1.1.1",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
@@ -19452,12 +19535,12 @@
       "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
     },
     "@emotion/styled": {
-      "version": "11.10.5",
-      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.5.tgz",
-      "integrity": "sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==",
+      "version": "11.10.6",
+      "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz",
+      "integrity": "sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==",
       "requires": {
         "@babel/runtime": "^7.18.3",
-        "@emotion/babel-plugin": "^11.10.5",
+        "@emotion/babel-plugin": "^11.10.6",
         "@emotion/is-prop-valid": "^1.2.0",
         "@emotion/serialize": "^1.1.1",
         "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
@@ -19501,11 +19584,6 @@
         "strip-json-comments": "^3.1.1"
       },
       "dependencies": {
-        "argparse": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-          "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
-        },
         "globals": {
           "version": "13.20.0",
           "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@@ -19514,14 +19592,6 @@
             "type-fest": "^0.20.2"
           }
         },
-        "js-yaml": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-          "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-          "requires": {
-            "argparse": "^2.0.1"
-          }
-        },
         "type-fest": {
           "version": "0.20.2",
           "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -19561,6 +19631,14 @@
         "resolve-from": "^5.0.0"
       },
       "dependencies": {
+        "argparse": {
+          "version": "1.0.10",
+          "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+          "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+          "requires": {
+            "sprintf-js": "~1.0.2"
+          }
+        },
         "camelcase": {
           "version": "5.3.1",
           "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -19575,6 +19653,15 @@
             "path-exists": "^4.0.0"
           }
         },
+        "js-yaml": {
+          "version": "3.14.1",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+          "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+          "requires": {
+            "argparse": "^1.0.7",
+            "esprima": "^4.0.0"
+          }
+        },
         "locate-path": {
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -20117,15 +20204,15 @@
       "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
     },
     "@mui/base": {
-      "version": "5.0.0-alpha.117",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.117.tgz",
-      "integrity": "sha512-3GlRSZdSrvDQ4k03dSV2rM+97JbNWimFOqGsE7n7Mi8WuBSYCgnPe56bQp3E5cShOrTn11dGH8FRCmVMcCEXqQ==",
+      "version": "5.0.0-alpha.123",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.123.tgz",
+      "integrity": "sha512-pxzcAfET3I6jvWqS4kijiLMn1OmdMw+mGmDa0SqmDZo3bXXdvLhpCCPqCkULG3UykhvFCOcU5HclOX3JCA+Zhg==",
       "requires": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@emotion/is-prop-valid": "^1.2.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
-        "@popperjs/core": "^2.11.6",
+        "@mui/utils": "^5.11.13",
+        "@popperjs/core": "^2.11.7",
         "clsx": "^1.2.1",
         "prop-types": "^15.8.1",
         "react-is": "^18.2.0"
@@ -20139,9 +20226,9 @@
       }
     },
     "@mui/core-downloads-tracker": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.8.tgz",
-      "integrity": "sha512-n/uJRIwZAaJaROaOA4VzycxDo27cusnrRzfycnAkAP5gBndwOJQ1CXjd1Y7hJe5eorj/ukixC7IZD+qCClMCMg=="
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.15.tgz",
+      "integrity": "sha512-Q0e2oBsjHyIWWj1wLzl14btunvBYC0yl+px7zL9R69tF87uenj6q72ieS369BJ6jxYpJwvXfR6/f+TC+ZUsKKg=="
     },
     "@mui/icons-material": {
       "version": "5.11.0",
@@ -20152,16 +20239,16 @@
       }
     },
     "@mui/material": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.8.tgz",
-      "integrity": "sha512-MpIVmtj9VJBhPHvPWoMkfCPpmVGXT4q43PtCJsdKIdc7W9/nG3Kpqw2oWyw+UxG5xG7eLhmfRFGPKvj4/WopEQ==",
-      "requires": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/base": "5.0.0-alpha.117",
-        "@mui/core-downloads-tracker": "^5.11.8",
-        "@mui/system": "^5.11.8",
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.15.tgz",
+      "integrity": "sha512-E5RbLq9/OvRKmGyeZawdnmFBCvhKkI/Zqgr0xFqW27TGwKLxObq/BreJc6Uu5Sbv8Fjj34vEAbRx6otfOyxn5w==",
+      "requires": {
+        "@babel/runtime": "^7.21.0",
+        "@mui/base": "5.0.0-alpha.123",
+        "@mui/core-downloads-tracker": "^5.11.15",
+        "@mui/system": "^5.11.15",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
+        "@mui/utils": "^5.11.13",
         "@types/react-transition-group": "^4.4.5",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
@@ -20178,36 +20265,36 @@
       }
     },
     "@mui/private-theming": {
-      "version": "5.11.7",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz",
-      "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==",
+      "version": "5.11.13",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.13.tgz",
+      "integrity": "sha512-PJnYNKzW5LIx3R+Zsp6WZVPs6w5sEKJ7mgLNnUXuYB1zo5aX71FVLtV7geyPXRcaN2tsoRNK7h444ED0t7cIjA==",
       "requires": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/utils": "^5.11.7",
+        "@babel/runtime": "^7.21.0",
+        "@mui/utils": "^5.11.13",
         "prop-types": "^15.8.1"
       }
     },
     "@mui/styled-engine": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.8.tgz",
-      "integrity": "sha512-iSpZp9AoeictsDi5xAQ4PGXu7mKtQyzMl7ZaWpHIGMFpsNnfY3NQNg+wkj/gpsAZ+Zg+IIyD+t+ig71Kr9fa0w==",
+      "version": "5.11.11",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.11.tgz",
+      "integrity": "sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==",
       "requires": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@emotion/cache": "^11.10.5",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
       }
     },
     "@mui/system": {
-      "version": "5.11.8",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.8.tgz",
-      "integrity": "sha512-zhroUcxAw2x/dISBJKhGbD70DOYCwMFRo7o/LUYTiUfQkfmLhRfEf1bopWgY9nYstn7QOxOq9fA3aR3pHrUTbw==",
+      "version": "5.11.15",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.15.tgz",
+      "integrity": "sha512-vCatoWCTnAPquoNifHbqMCMnOElEbLosVUeW0FQDyjCq+8yMABD9E6iY0s14O7iq1wD+qqU7rFAuDIVvJ/AzzA==",
       "requires": {
-        "@babel/runtime": "^7.20.7",
-        "@mui/private-theming": "^5.11.7",
-        "@mui/styled-engine": "^5.11.8",
+        "@babel/runtime": "^7.21.0",
+        "@mui/private-theming": "^5.11.13",
+        "@mui/styled-engine": "^5.11.11",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.7",
+        "@mui/utils": "^5.11.13",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
@@ -20220,11 +20307,11 @@
       "requires": {}
     },
     "@mui/utils": {
-      "version": "5.11.7",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz",
-      "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==",
+      "version": "5.11.13",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz",
+      "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==",
       "requires": {
-        "@babel/runtime": "^7.20.7",
+        "@babel/runtime": "^7.21.0",
         "@types/prop-types": "^15.7.5",
         "@types/react-is": "^16.7.1 || ^17.0.0",
         "prop-types": "^15.8.1",
@@ -20302,9 +20389,9 @@
       }
     },
     "@popperjs/core": {
-      "version": "2.11.6",
-      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
-      "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
+      "version": "2.11.7",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
+      "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
     },
     "@reduxjs/toolkit": {
       "version": "1.9.2",
@@ -20660,6 +20747,19 @@
         "@babel/runtime": "^7.12.5"
       }
     },
+    "@textea/json-viewer": {
+      "version": "2.16.2",
+      "resolved": "https://registry.npmjs.org/@textea/json-viewer/-/json-viewer-2.16.2.tgz",
+      "integrity": "sha512-ZPRNpcLJ+hOO6C8tzkQvltRSp7Ol73j8oJ1+w5mWVWn+JR1QqzZJJgBqnNZvs3S1h/jJAjwyeZUVlb0VtIJcvg==",
+      "requires": {
+        "@emotion/react": "^11.10.6",
+        "@emotion/styled": "^11.10.6",
+        "@mui/material": "^5.11.14",
+        "clsx": "^1.2.1",
+        "copy-to-clipboard": "^3.3.3",
+        "zustand": "^4.3.6"
+      }
+    },
     "@tootallnate/once": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -21665,12 +21765,9 @@
       "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
     },
     "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
     "aria-query": {
       "version": "5.1.3",
@@ -21796,6 +21893,28 @@
       "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz",
       "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg=="
     },
+    "axios": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
+      "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
+      "requires": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+          "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
+    },
     "axobject-query": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
@@ -22480,6 +22599,14 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
     },
+    "copy-to-clipboard": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+      "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+      "requires": {
+        "toggle-selection": "^1.0.6"
+      }
+    },
     "core-js": {
       "version": "3.27.2",
       "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.2.tgz",
@@ -23389,11 +23516,6 @@
             "color-convert": "^2.0.1"
           }
         },
-        "argparse": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-          "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
-        },
         "chalk": {
           "version": "4.1.2",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -23434,14 +23556,6 @@
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
         },
-        "js-yaml": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-          "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-          "requires": {
-            "argparse": "^2.0.1"
-          }
-        },
         "supports-color": {
           "version": "7.2.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -26584,12 +26698,11 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
     },
     "js-yaml": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "argparse": "^2.0.1"
       }
     },
     "jsdom": {
@@ -26990,6 +27103,11 @@
         "minimist": "^1.2.6"
       }
     },
+    "moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
+    },
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -28204,6 +28322,11 @@
         }
       }
     },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "psl": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -29473,6 +29596,14 @@
         "util.promisify": "~1.0.0"
       },
       "dependencies": {
+        "argparse": {
+          "version": "1.0.10",
+          "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+          "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+          "requires": {
+            "sprintf-js": "~1.0.2"
+          }
+        },
         "css-select": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
@@ -29514,6 +29645,15 @@
             }
           }
         },
+        "js-yaml": {
+          "version": "3.14.1",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+          "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+          "requires": {
+            "argparse": "^1.0.7",
+            "esprima": "^4.0.0"
+          }
+        },
         "nth-check": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
@@ -29686,6 +29826,11 @@
         "is-number": "^7.0.0"
       }
     },
+    "toggle-selection": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+      "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+    },
     "toidentifier": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -30720,6 +30865,14 @@
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
       "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+    },
+    "zustand": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz",
+      "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==",
+      "requires": {
+        "use-sync-external-store": "1.2.0"
+      }
     }
   }
 }
diff --git a/src/ml_board/frontend/Dashboard/package.json b/src/ml_board/frontend/Dashboard/package.json
index 2ff6e28a..07afde86 100644
--- a/src/ml_board/frontend/Dashboard/package.json
+++ b/src/ml_board/frontend/Dashboard/package.json
@@ -11,9 +11,12 @@
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^13.4.0",
     "@testing-library/user-event": "^13.5.0",
+    "@textea/json-viewer": "^2.16.2",
     "ag-grid-community": "^28.2.1",
     "ag-grid-react": "^28.2.1",
+    "axios": "^1.3.4",
     "history": "^4.7.2",
+    "moment": "^2.29.4",
     "react": "^18.2.0",
     "react-chartjs-2": "^5.1.0",
     "react-dom": "^18.2.0",
diff --git a/src/ml_board/frontend/Dashboard/src/app/ApiMaster.tsx b/src/ml_board/frontend/Dashboard/src/app/ApiMaster.tsx
new file mode 100644
index 00000000..99c0445c
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/app/ApiMaster.tsx
@@ -0,0 +1,33 @@
+export const defaultGridSearchConfigFileName = "grid_search_config";
+export const defaultExperimentConfigFileName = "experiment_config";
+
+const GRID_SEARCH_BASE_URL = "/grid_searches/<grid_search_id>";
+const CHECKPOINT_BASE_URL = "/checkpoints/<grid_search_id>/<experiment_id>/<checkpoint_id>";
+const CHECKPOINT_LIST_BASE_URL = "/checkpoint_list/<grid_search_id>/<experiment_id>";
+const MODEL_CARDS_BASE_URL = "/system-info/<grid_search_id>";
+
+let api = {
+    // following API to PUT config file for the whole grid search
+    gridsearch_config_file: GRID_SEARCH_BASE_URL + "/" + defaultGridSearchConfigFileName,
+
+    // following API to PUT config file for a selected experiment
+    experiment_config_file: GRID_SEARCH_BASE_URL + "/<experiment_id>/" + defaultExperimentConfigFileName,
+
+    // following API to get all the experiments of a grid search
+    experiments: GRID_SEARCH_BASE_URL + "/experiments",
+
+    // following API to GET data of all checkpoints of a selected experiment
+    checkpoint_list: CHECKPOINT_LIST_BASE_URL,
+
+    // following API to GET all data of a selected checkpoint
+    selected_checkpoint_list: CHECKPOINT_LIST_BASE_URL + "/<checkpoint_id>",
+    
+    // following APIs to GET, POST & DELETE data individually for checkpoints
+    // <checkpoint_resource> can be: model, optimizer, stateful_component, lr_scheduler
+    checkpoint_resource: CHECKPOINT_BASE_URL + "/<checkpoint_resource>",
+
+    // following API to GET system details like CPU & GPU for model card
+    model_card_sys_info: MODEL_CARDS_BASE_URL + "/<experiment_id>",
+}
+
+export default api;
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/app/App.tsx b/src/ml_board/frontend/Dashboard/src/app/App.tsx
index f76483a8..418628b8 100644
--- a/src/ml_board/frontend/Dashboard/src/app/App.tsx
+++ b/src/ml_board/frontend/Dashboard/src/app/App.tsx
@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import { Route, Routes, useLocation, useSearchParams } from 'react-router-dom';
 import { upsertCharts } from '../redux/charts/chartsSlice';
-import { incrementReceivedMsgCount, setLastPing, setSocketConnection, setThroughput } from '../redux/status/statusSlice';
+import { incrementReceivedMsgCount, setGridSearchId, setLastPing, setRestApiUrl, setSocketConnection, setThroughput } from '../redux/status/statusSlice';
 import { upsertManyRows } from '../redux/table/tableSlice';
 import { DataToRedux } from '../worker_socket/DataTypes';
 import { useAppDispatch } from './hooks';
@@ -123,6 +123,10 @@ export default function App() {
                 dispatch(setThroughput(data.status["throughput"]));
             } else if (data.status["isSocketConnected"] !== undefined) {
                 dispatch(setSocketConnection(data.status["isSocketConnected"]));
+                if (data.status["isSocketConnected"]) {
+                    dispatch(setGridSearchId(data.status["gridSearchId"]));
+                    dispatch(setRestApiUrl(data.status["restApiUrl"]));
+                }
                 setConnectionSnackBar({
                     isOpen: true,
                     connection: data.status["isSocketConnected"]
@@ -179,7 +183,7 @@ export default function App() {
             {
                 // Floating Action Button (FAB) added for filter popup
                 // Show filter - FAB only if valid url is there. Else hide the button (Just as mentioned above - for the case of TopBar). Also hide it when user is on Settings Page (As - not needed to do filter when viewing/inserting/updating configurations)
-                urls.includes(location.pathname.split("/")[1]) && location.pathname.split("/")[1] !== RoutesMapping["Settings"].url ?
+                (urls.includes(location.pathname.split("/")[1]) && location.pathname.split("/")[1] !== RoutesMapping["Settings"].url) && (location.pathname.split("/")[1] !== RoutesMapping["ExperimentPage"].url) ?
                     <div className={styles.fab}>
                         <Fab
                             variant="extended"
diff --git a/src/ml_board/frontend/Dashboard/src/app/RoutesMapping.tsx b/src/ml_board/frontend/Dashboard/src/app/RoutesMapping.tsx
index 3e1513cf..c8cfa841 100644
--- a/src/ml_board/frontend/Dashboard/src/app/RoutesMapping.tsx
+++ b/src/ml_board/frontend/Dashboard/src/app/RoutesMapping.tsx
@@ -1,10 +1,10 @@
 import Graphs from '../components/graphs/Graphs';
 import Dashboard from '../components/dashboard/Dashboard';
-import Settings from '../components/settings/Settings';
 import PageNotFound from '../components/error/PageNotFound';
 import DashboardIcon from '@mui/icons-material/Dashboard';
 import AutoGraphIcon from '@mui/icons-material/AutoGraph';
 import SettingsIcon from '@mui/icons-material/Settings';
+import ExperimentPage from '../components/experimentPage/ExperimentPage';
 
 interface RoutesMappingInterface {
     // Object's Key name will be the name shown in Menu List
@@ -29,6 +29,12 @@ export const RoutesMapping:RoutesMappingInterface = {
         showInMenu: true,
         menuIcon: <DashboardIcon/>
     },
+    ExperimentPage: {
+        url: "experiment",
+        component: <ExperimentPage/>,
+        showInMenu: false,
+        menuIcon: null
+    },
     Graphs: {
         url: "analysisboard",
         component: <Graphs />,
diff --git a/src/ml_board/frontend/Dashboard/src/components/configPopup/ConfigPopup.tsx b/src/ml_board/frontend/Dashboard/src/components/configPopup/ConfigPopup.tsx
index e5f1bdd4..3417aa4a 100644
--- a/src/ml_board/frontend/Dashboard/src/components/configPopup/ConfigPopup.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/configPopup/ConfigPopup.tsx
@@ -47,11 +47,13 @@ const ConfigPopup: React.FC<FuncProps> = (props) => {
         setConfigTextState({ ...configTextState, [key]: text });
     }
 
+    // regular expression in replace function replaces all the trailing backslashes and then stores the url in redux as during actual API call, we are appending the full api path with the main url
+
     function submitData() {
         let settingConfigs = {
             gridSearchId: configTextState.gridSearchId,
-            socketConnectionUrl: configTextState.socketConnectionUrl,
-            restApiUrl: configTextState.restApiUrl
+            socketConnectionUrl: configTextState.socketConnectionUrl.replace(/\/+$/, ''),
+            restApiUrl: configTextState.restApiUrl.replace(/\/+$/, '')
         }      
         setOpen(false);
         props.validateConfigs(true);
@@ -60,8 +62,8 @@ const ConfigPopup: React.FC<FuncProps> = (props) => {
 
     function validateData() {
         let gridSearchId = configTextState.gridSearchId;
-        let socketConnectionUrl = configTextState.socketConnectionUrl;
-        let restApiUrl = configTextState.restApiUrl;
+        let socketConnectionUrl = configTextState.socketConnectionUrl.replace(/\/+$/, '');
+        let restApiUrl = configTextState.restApiUrl.replace(/\/+$/, '');
         let isDataInValid = true;
 
         // basic validation check: if any values are typed by user, then enable submit button or else keep it disabled - so that empty values are not sent for socket connection request
diff --git a/src/ml_board/frontend/Dashboard/src/components/dashboard/Dashboard.tsx b/src/ml_board/frontend/Dashboard/src/components/dashboard/Dashboard.tsx
index c73f6d9d..be26acbd 100644
--- a/src/ml_board/frontend/Dashboard/src/components/dashboard/Dashboard.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/dashboard/Dashboard.tsx
@@ -2,7 +2,37 @@ import { useAppSelector } from "../../app/hooks";
 import { selectFilter } from "../../redux/status/statusSlice";
 import { selectAllRows } from "../../redux/table/tableSlice";
 import Table from "./table/Table";
+import { Toolbar } from '@mui/material';
 
+export interface TableRow {
+  // Job
+  job_id: string; // format <grid_search_id>-<job index>
+  job_type: string; // <CALC, TERMINATE>
+  job_status: string; // <INIT, RUNNING, DONE>
+  starting_time: number;
+  finishing_time: number;
+  error: string;
+  stacktrace: string;
+  device: string;
+  // Experiment
+  experiment_id: number;
+  model_status?: string;   // <TRAINING, EVALUATING>,
+  current_split?: string;
+  splits?: string[]; //e.g.: ["train", "val", "test"],
+  num_epochs?: number;
+  current_epoch?: number;
+  num_batches?: number;
+  current_batch?: number;
+  // special Experiment keys for "latest_split_metric"
+  F1?: string;
+  Precision?: string;
+  Recall?: string;
+  // calculations
+  epoch_progress?: number;
+  batch_progress?: number;
+  // TODO: maybe get color???
+  color?: string;
+}
 
 export default function Dashboard() {
   // filter them based on the regEx in the status slice
@@ -11,11 +41,20 @@ export default function Dashboard() {
   const rows = useAppSelector(selectAllRows);
   const colNames: string[] = [];
 
+  // Table should consist of these columns for minimized view. To get detailed view, user can click on the row and see the job + experiment details:
+  // const colNames = ["experiment_id", "job_status", "starting_time", "finishing_time",
+  // "model_status", "epoch_progress", "batch_progress"];
+
   // TODO: get them from the Redux store better, otherwise this checks the first row ONLY!!!
   // getting the columns' headers dynamically
   if (rows.length > 1) {
     colNames.push(...Object.keys(rows[0]).filter((colName: string) => re.test(colName)));
   }
 
-  return (<Table colNames={colNames} rows={rows} />);
+  return (
+    <div>
+      <Toolbar />
+      <Table colNames={colNames} rows={rows} />
+    </div>
+  );
 }
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/dashboard/table/Table.tsx b/src/ml_board/frontend/Dashboard/src/components/dashboard/table/Table.tsx
index a0c6ed04..edcec1e1 100644
--- a/src/ml_board/frontend/Dashboard/src/components/dashboard/table/Table.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/dashboard/table/Table.tsx
@@ -1,9 +1,9 @@
-import { Toolbar } from '@mui/material';
-import { CellClickedEvent } from 'ag-grid-community';
+import { RowDoubleClickedEvent } from 'ag-grid-community';
 import 'ag-grid-community/styles/ag-grid.css'; // Core grid CSS, always needed
 import 'ag-grid-community/styles/ag-theme-alpine.css'; // Optional theme CSS
 import { AgGridReact } from "ag-grid-react";
 import { useCallback, useMemo } from 'react';
+import { useNavigate } from 'react-router-dom';
 // styles
 import styles from './Table.module.css';
 
@@ -12,6 +12,8 @@ interface columnDefinition { field: string; }
 // TODO: Maybe merge Table and Dashboard?
 export default function Table({ colNames, rows }: { colNames: string[], rows: any[] }) {
 
+  const navigate = useNavigate();
+
   // change the array of strings to array of colum definitions
   const colDefs: columnDefinition[] = colNames.map(
     (colName: string) => ({
@@ -21,38 +23,30 @@ export default function Table({ colNames, rows }: { colNames: string[], rows: an
 
   const defaultColDef = useMemo(() => ({ resizable: true, sortable: true, filter: true }), []);
 
-  const onCellClicked = useCallback((event: CellClickedEvent) => { console.log(event) }, []);
+  const onRowDoubleClicked = useCallback((event: RowDoubleClickedEvent) => {
+    const selectedRowData = event.api.getSelectedRows()[0];
+    navigate({
+      pathname: '/experiment',
+      search: '?experiment_id=' + selectedRowData.experiment_id,
+    })
+  }, []);
+
+  // set background colour for every row based on the rowIndex, as it is the same as experiment_id. BUT this looks bad, should be using CSS classes?
+  // const getRowStyle = ({ rowIndex }: RowClassParams): RowStyle => { return { background: `hsl(${rowIndex * 137.5},75%,50%)` }; };
 
   return (
     <div className="ag-theme-alpine" id={styles.ag_grid_container_table}>
-      <Toolbar />
+      {/* NOTE: https://www.ag-grid.com/react-data-grid/row-styles/ */}
       <AgGridReact
         defaultColDef={defaultColDef}
-        // {/* provide column definitions */}
         columnDefs={colDefs}
-        // {/* specify auto group column definition */}
-        // autoGroupColumnDef={this.autoGroupColumnDef}
-        // {/* row data provided via props from the file store */}
         rowData={rows}
-        // // enable tree data
-        // treeData={true}
-        // // {/* return tree hierarchy from supplied data */}
-        // getDataPath={data => data.filePath}
-        // // {/* expand tree by default */}
-        // groupDefaultExpanded={-1}
-        // // {/* fit grid columns */}
         onGridReady={params => params.api.sizeColumnsToFit()}
-        // // {/* provide context menu callback */}
-        // getContextMenuItems={this.getContextMenuItems}
-        // // {/* provide row drag end callback */}
-        // onRowDragEnd={this.onRowDragEnd}
-        // // {/* return id required for tree data and immutable data */}
         getRowId={(params: any) => params.data.job_id}
-        // // {/* specify our FileCellRenderer component */}
-        // components={this.components}
+        rowSelection={"multiple"}
+        onRowDoubleClicked={onRowDoubleClicked}
+        rowStyle={{ cursor: "pointer" }}
         animateRows={true} // Optional - set to 'true' to have rows animate when sorted
-        rowSelection="multiple"
-        onCellClicked={onCellClicked}
       >
       </AgGridReact>
     </div>
diff --git a/src/ml_board/frontend/Dashboard/src/components/error/PageNotFound.tsx b/src/ml_board/frontend/Dashboard/src/components/error/PageNotFound.tsx
index 0fbe7542..737a610b 100644
--- a/src/ml_board/frontend/Dashboard/src/components/error/PageNotFound.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/error/PageNotFound.tsx
@@ -1,5 +1,3 @@
-import Box from '@mui/material/Box';
-
 // Error 404 Page to show if the entered URL is not valid
 function PageNotFound() {
     return(
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.module.css b/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.module.css
new file mode 100644
index 00000000..595270c9
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.module.css
@@ -0,0 +1,26 @@
+.textfield_cardcontent_checkpoint_config {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+}
+
+.checkpoint_data_resources {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.checkpoint_data_resources_main_card {
+    margin-bottom: 10px;
+    border-radius: 15px;
+}
+
+.error_text {
+    font-family: sans-serif;
+    color: red;
+    font-weight: bold;
+    text-align: center;
+    font-size: 15px;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.tsx
new file mode 100644
index 00000000..7aa14d54
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/CheckpointConfigurations/CheckpointConfigurations.tsx
@@ -0,0 +1,192 @@
+import { Grid, Card, CardContent, IconButton, Box, LinearProgress, FormControl, FormHelperText, InputLabel, MenuItem, Select } from '@mui/material';
+import { Download } from '@mui/icons-material';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
+import api from '../../../app/ApiMaster';
+import { useAppSelector } from "../../../app/hooks";
+import { getGridSearchId, getRestApiUrl } from '../../../redux/status/statusSlice';
+import styles from './CheckpointConfigurations.module.css';
+
+interface CheckpointDataInterface {
+    "experiment_id": string,
+    "epoch": string,
+    "checkpoints": Array<string>
+}
+
+export default function CheckpointConfigurations({experimentIdProp} : {experimentIdProp: string}) {
+
+    const [selectedCheckpointIndex, setSelectedCheckpointIndex] = useState(0);
+    const [checkpointResourceNames, setCheckpointResourceNames] = useState(Array<string>);
+    const [checkpointData, setCheckpointData] = useState(Array<CheckpointDataInterface>);
+    const [error, setError] = useState("");
+    const [errorInGettingResource, setErrorInGettingResource] = useState("");
+    const [isLoading, setIsLoading] = useState(false);
+    const [isLoadingResource, setIsLoadingResource] = useState(false);
+
+    const grid_search_id = useAppSelector(getGridSearchId);
+    const rest_api_url = useAppSelector(getRestApiUrl);
+
+    useEffect(() => {
+        if(experimentIdProp) {
+            getAllCheckpoints();
+        }
+    },[experimentIdProp]);
+
+    function getAllCheckpoints() {
+        let all_checkpoints = api.checkpoint_list.replace("<grid_search_id>", grid_search_id);
+        all_checkpoints = all_checkpoints.replace("<experiment_id>", experimentIdProp);
+
+        axios.get(rest_api_url + all_checkpoints).then((response) => {
+            console.log("Got response from checkpoint_list API: ", response);
+            if (response.status === 200) {
+                let resp_data = response.data;
+                setCheckpointData(resp_data);
+                // selecting all the resource names to display for the first default index 
+                setCheckpointResourceNames(resp_data[0].checkpoints);
+            }
+            else {
+                setError("Error occured / No checkpoints available");
+            }
+            setIsLoading(false);
+        })
+        .catch((error) => {
+            console.log("Error in checkpoint_list: ", error);
+            setIsLoading(false);
+            setError("Error occured / No checkpoints available");
+        });
+    }
+
+    function getCheckpointResource(resourceName: string) {
+        let checkpoint_resource = api.checkpoint_resource.replace("<grid_search_id>", grid_search_id);
+        checkpoint_resource = checkpoint_resource.replace("<experiment_id>", experimentIdProp);
+        checkpoint_resource = checkpoint_resource.replace("<checkpoint_id>", checkpointData[selectedCheckpointIndex].epoch.toString());
+        checkpoint_resource = checkpoint_resource.replace("<checkpoint_resource>", resourceName);
+
+        setErrorInGettingResource("");
+        setIsLoadingResource(true);
+
+        axios.get(rest_api_url + checkpoint_resource).then((response) => {
+            console.log("Got response from checkpoint_resource API: ", response);
+            if (response.status === 200) {
+                downloadFile(resourceName, new Blob([response.data]));
+            }
+            else {
+                setErrorInGettingResource("Oops! an error occurred in getting & downloading checkpoint resource data")
+            }
+            setIsLoadingResource(false);
+        })
+        .catch((error) => {
+            console.log("Error in checkpoint_resource API: ", error);
+            setIsLoadingResource(false);
+            setErrorInGettingResource("Oops! an error occurred in getting & downloading checkpoint resource data")
+        });
+    }
+
+    function downloadFile(resourceName: string, fileData: Blob) {
+        const url = window.URL.createObjectURL(fileData!);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download = resourceName+"_checkpoint_"+checkpointData[selectedCheckpointIndex].epoch.trim().toString()+"_experiemnt_"+experimentIdProp+".pickle";
+        document.body.appendChild(a);
+        a.click();
+        document.body.removeChild(a);
+    }
+
+    return(
+        <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+            <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                <Box className={styles.textfield_cardcontent_checkpoint_config}>
+                    <FormControl fullWidth={true} error={error.length > 0}>
+                        <InputLabel id="demo-simple-select-helper-label">Select Checkpoint Id</InputLabel>
+                        <Select
+                            labelId="demo-simple-select-helper-label"
+                            id="demo-simple-select-helper"
+                            value={selectedCheckpointIndex}
+                            label="Select Checkpoint Id"
+                            onChange={(e)=>{
+                                let index = Number(e.target.value);
+                                setSelectedCheckpointIndex(index);
+                                setCheckpointResourceNames(checkpointData[index].checkpoints);
+                            }}
+                            MenuProps={{ style: { maxHeight: "250px" } }} // have to keep style here. keeping it in css file is not working for this
+                        >
+                            {
+                                checkpointData.length > 0 ?
+                                checkpointData.map((chkpt_obj, index) => 
+                                    <MenuItem key={index} value={index.toString()}>
+                                        {chkpt_obj.epoch}
+                                    </MenuItem>
+                                )
+                                :
+                                null
+                            }
+                        </Select>
+                        {
+                            error.length > 0 ?
+                            <FormHelperText>
+                                {error}
+                            </FormHelperText>
+                            :
+                            null
+                        }
+                    </FormControl>
+                </Box>
+            </Grid>
+            {
+                isLoading ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Box>
+                        <LinearProgress />
+                    </Box>
+                </Grid>
+                :
+                null
+            }
+            {
+                !isLoading && error.length === 0 && checkpointResourceNames.length > 0 ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    {
+                        checkpointResourceNames.map((resourceName, index) => {
+                            return(
+                                <Card className={styles.checkpoint_data_resources_main_card} key={index}>
+                                    <CardContent className={styles.checkpoint_data_resources}>
+                                        <div>
+                                            {resourceName}
+                                        </div>
+                                        <IconButton 
+                                            size="small"
+                                            onClick={()=>getCheckpointResource(resourceName)}
+                                            disabled={isLoadingResource}
+                                        >
+                                            <Download /> 
+                                        </IconButton>
+                                    </CardContent>
+                                </Card>
+                            );
+                        })
+                    }
+                    {
+                        isLoadingResource ?
+                        <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                            <Box>
+                                <LinearProgress />
+                            </Box>
+                        </Grid>
+                        :
+                        null
+                    }
+                    {
+                        errorInGettingResource.length > 0 ?
+                        <div className={styles.error_text}>
+                            {errorInGettingResource}
+                        </div>
+                        :
+                        null
+                    }
+                </Grid>
+                :
+                null
+            }
+        </Grid>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.module.css b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.module.css
new file mode 100644
index 00000000..0ddb85d5
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.module.css
@@ -0,0 +1,16 @@
+.textfield_cardcontent_experiment_config {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.heading_style {
+    font-family: sans-serif;
+}
+
+.error_text {
+    font-family: sans-serif;
+    color: red;
+    font-size: 20px;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.tsx
new file mode 100644
index 00000000..ac2cc0df
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentConfigurations/ExperimentConfigurations.tsx
@@ -0,0 +1,125 @@
+import { Grid, Card, CardContent, TextField, IconButton, Switch, FormControlLabel, Box, LinearProgress } from '@mui/material';
+import { Send, Download } from '@mui/icons-material';
+import { JsonViewer } from '@textea/json-viewer';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
+import api, { defaultExperimentConfigFileName } from '../../../app/ApiMaster';
+import { useAppSelector } from "../../../app/hooks";
+import { getGridSearchId, getRestApiUrl } from '../../../redux/status/statusSlice';
+import styles from './ExperimentConfigurations.module.css';
+
+export default function ExperimentConfigurations({experimentIdProp} : {experimentIdProp: string}) {
+    
+    const [experimentConfigFileObject, setExperimentConfigFileObject] = useState({});
+    const [error, setError] = useState("");
+    const [isLoading, setIsLoading] = useState(false);
+    const [showHideData, setShowHideData] = useState(false);
+    const grid_search_id = useAppSelector(getGridSearchId);
+    const rest_api_url = useAppSelector(getRestApiUrl);
+
+    useEffect(() => {
+        if(experimentIdProp) {
+            let experiment_config_file = api.experiment_config_file.replace("<grid_search_id>", grid_search_id);
+            experiment_config_file = experiment_config_file.replace("<experiment_id>", experimentIdProp);
+            
+            setError("");
+            setShowHideData(false);
+            setIsLoading(true);
+    
+            axios.get(rest_api_url + experiment_config_file).then((response) => {
+                console.log("Got response from experiment_config API: ", response);
+                if (response.status === 200) {
+                    setExperimentConfigFileObject(response.data);
+                    setShowHideData(true);
+                }
+                else {
+                    setError("Oops! an error occurred");
+                }
+                setIsLoading(false);
+            })
+            .catch((error) => {
+                console.log("Error in experiment_config_file: ", error);
+                setIsLoading(false);
+                setError("Oops! an error occurred");
+            });
+        }
+    },[experimentIdProp]);
+
+    function downloadFile() {
+        if(experimentIdProp) {
+            const dataStr = JSON.stringify(experimentConfigFileObject, null, 2);
+            const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
+            const linkElement = document.createElement("a");
+            linkElement.setAttribute('href', dataUri);
+            linkElement.setAttribute('download', defaultExperimentConfigFileName.trim()+"_experiment_"+experimentIdProp+".json");
+            linkElement.click();
+        }
+    }
+
+    return(
+        <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+            <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                <Box className={styles.textfield_cardcontent_experiment_config}>
+                    <h2 className={styles.heading_style}>Experiment Configuration File</h2>
+                    <div>
+                        {
+                            !isLoading && error.length === 0 && Object.keys(experimentConfigFileObject).length > 0 ?
+                            <IconButton 
+                                size="large"
+                                onClick={()=>downloadFile()}
+                            >
+                                <Download /> 
+                            </IconButton>
+                            :
+                            null
+                        }
+                        {
+                            !isLoading && error.length === 0 && Object.keys(experimentConfigFileObject).length > 0 ?
+                            <FormControlLabel
+                                control={
+                                    <Switch
+                                        checked={showHideData}
+                                        color="primary"
+                                        onChange={()=>setShowHideData(!showHideData)}
+                                    />
+                                }
+                                label={"View/Hide"}
+                            />
+                            :
+                            null
+                        }
+                    </div>
+                </Box>
+            </Grid>
+            {
+                isLoading ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Box>
+                        <LinearProgress />
+                    </Box>
+                </Grid>
+                :
+                null
+            }
+            {
+                !isLoading && error.length === 0 && Object.keys(experimentConfigFileObject).length > 0 && showHideData ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Card>
+                        <CardContent>
+                            <JsonViewer value={experimentConfigFileObject}/>
+                        </CardContent>
+                    </Card>
+                </Grid>
+                :
+                !isLoading && error.length !== 0 ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Box className={styles.error_text}>
+                        Error Occured In Getting Experiment Configuration File!
+                    </Box>
+                </Grid>
+                :
+                null
+            }
+        </Grid>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/CardDetails.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/CardDetails.tsx
new file mode 100644
index 00000000..027b3dd8
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/CardDetails.tsx
@@ -0,0 +1,28 @@
+import styles from '../ExperimentPage.module.css';
+import { Card, CardContent } from '@mui/material';
+import { AnyKeyValuePairsInterface } from '../ExperimentPage';
+
+export function CardDetails({cardTitle, contentObj} : {cardTitle:string, contentObj: AnyKeyValuePairsInterface}) {
+
+    return(
+        <Card className={styles.card}>
+            <CardContent>
+                <div className={styles.card_content_typography}>
+                    {cardTitle}
+                </div>
+                {
+                    Object.keys(contentObj).map((content) => 
+                        <div className={styles.cardcontent} key={content}>
+                            <div className={styles.cardcontent_key}>
+                                {content}
+                            </div>
+                            <div className={styles.cardcontent_value}>
+                                {contentObj[content]}
+                            </div>
+                        </div>
+                    )
+                }
+            </CardContent>
+        </Card>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/ExperimentDetails.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/ExperimentDetails.tsx
new file mode 100644
index 00000000..dbc4b9cf
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentDetails/ExperimentDetails.tsx
@@ -0,0 +1,128 @@
+import { Card, CardContent, Grid } from '@mui/material';
+import moment from 'moment';
+import { useState } from 'react';
+import { AnyKeyValuePairsInterface } from '../ExperimentPage';
+import styles from '../ExperimentPage.module.css';
+import { CardDetails } from './CardDetails';
+
+interface return_obj_interface {
+    [key: string]: string
+}
+
+export function ExperimentDetails({ selectedExperiment }: { selectedExperiment: AnyKeyValuePairsInterface }) {
+
+    const [showMore, setShowMore] = useState(false);
+
+    const conditional_render_exp_overview = () => ({
+        "Experiment ID": selectedExperiment.experiment_id,
+        "Job Type": selectedExperiment.job_type ?? "--",
+        "Device": selectedExperiment.device ?? "--"
+    });
+
+    function conditional_render_exp_state() {
+        const return_obj: return_obj_interface = {};
+
+        if(selectedExperiment.hasOwnProperty("error") && selectedExperiment.error !== null) {
+            return_obj["Job Status"] = "FAILED";
+        }
+        else if(selectedExperiment.job_status === "DONE") {
+            return_obj["Job Status"] = "COMPLETED";
+        }
+        else if(selectedExperiment.job_status === "INIT") {
+            return_obj["Job Status"] = "INITIALIZING";
+        }
+        else if(selectedExperiment.job_status) {
+            return_obj["Job Status"] = selectedExperiment.job_status;
+        }
+        else {
+            return_obj["Job Status"] = "--";
+        }
+
+        return_obj["Model Status"] = selectedExperiment.model_status ?? "--";
+
+        return return_obj;
+    }
+
+    function conditional_render_exp_date_time() {
+        const return_obj: return_obj_interface = {};
+
+        if(selectedExperiment.starting_time && selectedExperiment.starting_time !== -1) {
+            return_obj["Start"] = moment(new Date(selectedExperiment.starting_time * 1000)).format("YYYY-MM-DD hh:MM:SS");
+        }
+        else {
+            return_obj["Start"] = "--";
+        }
+
+        if(selectedExperiment.finishing_time && selectedExperiment.finishing_time !== -1) {
+            return_obj["End"] = moment(new Date(selectedExperiment.finishing_time * 1000)).format("YYYY-MM-DD hh:MM:SS");
+        }
+        else {
+            return_obj["End"] = "--";
+        }
+
+        return return_obj;
+    }
+
+    return (
+        <>
+            <Grid container rowSpacing={1} spacing={{ xs: 1, md: 2 }}>
+                <Grid item={true} xs={12} sm={12} md={4}>
+                    <CardDetails
+                        cardTitle='Overview'
+                        contentObj={conditional_render_exp_overview()}
+                    />
+                </Grid>
+                <Grid item={true} xs={12} sm={12} md={4}>
+                    <CardDetails
+                        cardTitle='Times'
+                        contentObj={conditional_render_exp_date_time()}
+                    />
+                </Grid>
+                <Grid item={true} xs={12} sm={12} md={4}>
+                    <CardDetails
+                        cardTitle='State'
+                        contentObj={conditional_render_exp_state()}
+                    />
+                </Grid>
+            </Grid>
+            {
+                selectedExperiment.error && <div className={styles.error_card}>
+                    <Card className={styles.card}>
+                        <CardContent>
+                            <div className={styles.card_content_typography_error} >
+                                Error
+                            </div>
+                            <div className={styles.cardcontent_key}>
+                                {selectedExperiment.error}
+                            </div>
+                            {
+                                selectedExperiment.stacktrace && <div>
+                                    <br />
+                                    <div className={styles.card_content_typography} >
+                                        Stacktrace
+                                    </div>
+                                    <div className={styles.cardcontent_key}>
+                                        {
+                                            showMore ?
+                                                selectedExperiment.stacktrace
+                                                :
+                                                selectedExperiment.stacktrace.substring(0, 100) + "..."
+                                        }
+                                        <span
+                                            onClick={() => setShowMore(!showMore)}
+                                            className={styles.show_more_text}
+                                        >
+                                            {
+                                                showMore ? "show less" : "show more"
+                                            }
+                                        </span>
+                                    </div>
+                                </div>
+                            }
+                        </CardContent>
+                    </Card>
+                </div>
+            }
+        </>
+    );
+}
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.module.css b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.module.css
new file mode 100644
index 00000000..85c25baf
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.module.css
@@ -0,0 +1,152 @@
+.cardview {
+    display: flex;
+    flex-direction: row;
+    gap: 10px;
+}
+
+.cardcontent {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    margin-top: 5px;
+    margin-bottom: 10px;
+}
+
+.cardcontent_typography {
+    display: flex;
+    flex-direction: row;
+    gap: 10px;
+}
+
+.accrodian_typography {
+    display: flex;
+    flex-direction: row;
+    margin-left: 15px;
+    margin-bottom: 15px;
+}
+
+.main {
+    padding-bottom: 100px;
+}
+
+/* .main_heading {
+    padding: 20px;
+    font-size: 25px;
+    font-weight: bold;
+    margin: auto;
+    text-align: center;
+    font-family: sans-serif;
+} */
+
+.accordian_heading {
+    font-weight: bold !important;
+    font-size: 20px;
+    font-family: sans-serif;
+}
+
+.card {
+    width: 100%;
+    height: 100%;
+}
+
+.card_download_btn {
+    width: 100%;
+}
+
+.error_card {
+    margin-top: 10px;
+}
+
+.expand_more_icon {
+    font-size: 0.9rem !important;
+}
+
+.show_more_text {
+    cursor: pointer;
+    color: #36A2EB;
+    font-family: sans-serif;
+}
+
+.donought_container {
+    display: flex;
+    justify-content: center;
+}
+
+.card_content_typography {
+    color: gray;
+    margin-bottom: 10px;
+    font-family: sans-serif;
+}
+
+.card_content_typography_error {
+    font-weight: bold !important;
+    color: red;
+    font-family: sans-serif;
+}
+
+.cardcontent_key {
+    font-family: sans-serif;
+}
+
+.cardcontent_textfiled {
+    background-color: #36A2EB;
+    margin-top: 5px;
+    margin-bottom: 10px;
+}
+
+.textfiled_cardcontent {
+    display: flex;
+    flex-direction: row;
+    margin-top: 5px;
+    margin-bottom: 10px;
+}
+
+.cardcontent_value {
+    font-weight: bold;
+    color: #36A2EB;
+    font-family: sans-serif;
+}
+
+.grid_card { 
+    box-shadow: 0px 1px 1px 2px rgba(97, 95, 95, 0.23) !important;
+    margin-bottom: 20px;
+    padding: 5px;
+    display: flex;
+}
+
+.graphs_chart_container {
+    position: relative;
+    margin: auto;
+    height: 80vh;
+    width: 100%;
+}
+
+.loading_bar_wrap {
+    width: 100%;
+}
+
+.switch_container {
+    display: block;
+    margin-left: 20px;
+}
+
+.error_text_no_data_1 {
+    font-family: sans-serif;
+    color: red;
+    font-weight: bold;
+    font-size: 25px;
+}
+
+.error_text_no_data_2 {
+    font-family: sans-serif;
+    font-size: 20px;
+    margin-top: 10px;
+}
+
+.extra_text_container {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    margin-top: 30px;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.tsx
new file mode 100644
index 00000000..6f5308eb
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentPage.tsx
@@ -0,0 +1,160 @@
+import { Toolbar, styled, AccordionProps, AccordionSummaryProps, Card, CardContent, Box, Grid } from '@mui/material';
+import { useEffect, useState } from 'react';
+import { useSearchParams } from 'react-router-dom';
+import { useAppSelector } from "../../app/hooks";
+import { selectRowById } from '../../redux/table/tableSlice';
+import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
+import MuiAccordion from '@mui/material/Accordion';
+import MuiAccordionSummary from '@mui/material/AccordionSummary';
+import MuiAccordionDetails from '@mui/material/AccordionDetails';
+import styles from './ExperimentPage.module.css';
+import styles_graphs from "../graphs/Graphs.module.css";
+import Graph from "../graphs/Graph";
+import CheckpointConfigurations from './CheckpointConfigurations/CheckpointConfigurations';
+import ExperimentConfigurations from './ExperimentConfigurations/ExperimentConfigurations';
+import ModelCards from './ModelCards/ModelCards';
+import { isConnected } from "../../redux/status/statusSlice";
+import { ExperimentDetails } from './ExperimentDetails/ExperimentDetails';
+import { ExperimentProgress } from './ExperimentProgress/ExperimentProgress';
+import { selectChartsByExperimentId } from '../../redux/charts/chartsSlice';
+
+export interface AnyKeyValuePairsInterface {
+    [key: string]: any
+}
+var anyObj: AnyKeyValuePairsInterface = {}
+
+const Accordion = styled((props: AccordionProps) => (
+    <MuiAccordion disableGutters elevation={0} square {...props} />))(() => ({
+    '&:not(:last-child)': {
+        borderBottom: 0,
+    },
+    '&:before': {
+        display: 'none',
+    }
+}));
+  
+const AccordionSummary = styled((props: AccordionSummaryProps) => (
+    <MuiAccordionSummary
+      expandIcon={<ArrowForwardIosSharpIcon className={styles.expand_more_icon} />}
+      {...props}
+    />
+  ))(({ theme }) => ({
+    flexDirection: 'row-reverse',
+    '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': {
+      transform: 'rotate(90deg)',
+    },
+    '& .MuiAccordionSummary-content': {
+      marginLeft: theme.spacing(1),
+    },
+}));
+  
+const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
+    paddingTop: theme.spacing(0),
+}));
+
+function ExperimentPage() {
+    const [searchParams, setSearchParams] = useSearchParams();
+    let experiment_id = searchParams.get("experiment_id") as string;
+    const [selectedExperiment, setSelectedExperiment] = useState(anyObj)
+    const filteredExp = useAppSelector(state => selectRowById(state, experiment_id));
+    const selectedExpGraphs = useAppSelector(state => selectChartsByExperimentId(state, experiment_id))
+    const isSocketConnected = useAppSelector(isConnected);
+
+    useEffect(() => {
+        console.log("filteredExp: ",filteredExp);
+        if (filteredExp) {
+            setSelectedExperiment(filteredExp);
+        }
+    },[filteredExp]);
+
+    return(
+        <div className={styles.main}>
+            <Toolbar />
+            {
+                filteredExp && isSocketConnected ?
+                <>
+                    <Accordion defaultExpanded={true}>
+                        <AccordionSummary>
+                            <div className={styles.accordian_heading}>Details</div>
+                        </AccordionSummary>
+                        <AccordionDetails className={styles.accordian_details}>
+                            <ExperimentDetails selectedExperiment={selectedExperiment}/>
+                        </AccordionDetails>
+                    </Accordion>
+                    <Accordion defaultExpanded={true}>
+                        <AccordionSummary aria-controls="panel2d-content" id="panel2d-header">
+                            <div className={styles.accordian_heading}>Progress</div>
+                        </AccordionSummary>
+                        <AccordionDetails>
+                            <ExperimentProgress selectedExperiment={selectedExperiment}/>
+                        </AccordionDetails>
+                    </Accordion>
+                    <Accordion defaultExpanded={true}>
+                        <AccordionSummary aria-controls="panel2d-content" id="panel2d-header">
+                            <div className={styles.accordian_heading}>Model Cards</div>
+                        </AccordionSummary>
+                        <AccordionDetails>
+                            <ModelCards experimentIdProp={experiment_id.toString()}/>
+                        </AccordionDetails>
+                    </Accordion>
+                    {
+                        filteredExp && filteredExp.hasOwnProperty("experiment_id") ?
+                        <Accordion defaultExpanded={true}>
+                            <AccordionSummary aria-controls="panel2d-content" id="panel2d-header">
+                                <div className={styles.accordian_heading}>Configurations</div>
+                            </AccordionSummary>
+                            <AccordionDetails>
+                                <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+                                    <Grid item={true} xs={12} sm={12} md={3}>
+                                        <CheckpointConfigurations experimentIdProp={filteredExp!.experiment_id.toString()}/>
+                                    </Grid>
+                                    <Grid item={true} xs={12} sm={12} md={9}>
+                                        <ExperimentConfigurations experimentIdProp={filteredExp!.experiment_id.toString()}/>
+                                    </Grid>
+                                </Grid>
+                            </AccordionDetails>
+                        </Accordion>
+                        :
+                        null
+                    }
+                    <Accordion defaultExpanded={true}>
+                        <AccordionSummary>
+                            <div className={styles.accordian_heading}>Losses & Metrics</div>
+                        </AccordionSummary>
+                        <AccordionDetails>
+                            {
+                                Object.keys(selectedExpGraphs).length > 0 ?
+                                <Grid container rowSpacing={1} spacing={{ xs: 2, md: 3 }} className={styles_graphs.grid_container}>
+                                    {
+                                        Object.keys(selectedExpGraphs).map((chart_id) => 
+                                            <Graph 
+                                                key={chart_id.toString()} 
+                                                chart_id={chart_id.toString()} 
+                                                exp_id={selectedExpGraphs[chart_id].exp_id.toString()}
+                                                exp_data={selectedExpGraphs[chart_id].data}
+                                            />
+                                        )
+                                    }
+                                </Grid>
+                                :
+                                <Box className={styles_graphs.no_data_to_viz}>
+                                    --- No Data To Visualize ---
+                                </Box>
+                            }
+                        </AccordionDetails>
+                    </Accordion>
+                </>
+                :
+                isSocketConnected ?
+                <div className={styles.extra_text_container}>
+                    <div className={styles.error_text_no_data_1}> No Data Available </div>
+                    <div className={styles.error_text_no_data_2}> (Please wait, experiment details might be loading...) </div>
+                </div>
+                :
+                null
+            }
+        </div>
+    )
+}
+
+export default ExperimentPage;
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentProgress/ExperimentProgress.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentProgress/ExperimentProgress.tsx
new file mode 100644
index 00000000..f8df9d0a
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ExperimentProgress/ExperimentProgress.tsx
@@ -0,0 +1,102 @@
+import styles from '../ExperimentPage.module.css';
+import { Card, CardContent, Grid } from '@mui/material';
+import HalfDonoughtGraph from '../HalfDonoughtGraph/HalfDonoughtGraph';
+import { AnyKeyValuePairsInterface } from '../ExperimentPage';
+
+export function ExperimentProgress({selectedExperiment} : {selectedExperiment: AnyKeyValuePairsInterface }) {
+
+    return (
+        <Grid container rowSpacing={1} spacing={{ xs: 1, md: 2 }}>
+            <Grid item={true} xs={12} sm={12} md={6}>
+                <Card className={styles.card}>
+                    <CardContent>
+                        <div className={styles.card_content_typography} >
+                            <div className={styles.cardcontent_typography}>
+                                <div>
+                                    Epoch Details: 
+                                </div>
+                                {
+                                    selectedExperiment.hasOwnProperty("current_epoch") && selectedExperiment.hasOwnProperty("num_epochs") ?
+                                    <div>
+                                        {selectedExperiment.current_epoch} / {selectedExperiment.num_epochs}
+                                    </div>
+                                    :
+                                    <div>--</div>
+                                }
+                            </div>
+                        </div>
+                        <div className={styles.donought_container}>
+                            {
+                                selectedExperiment.hasOwnProperty("current_epoch") ?
+                                <HalfDonoughtGraph 
+                                    graph_data={
+                                        [
+                                            selectedExperiment.epoch_progress,
+                                            (1-selectedExperiment.epoch_progress)
+                                        ]
+                                    }
+                                    label_data={
+                                        [
+                                            selectedExperiment.epoch_progress*100+"%",
+                                            (1-selectedExperiment.epoch_progress)*100+"%"
+                                        ]
+                                    }
+                                />
+                                :
+                                <HalfDonoughtGraph 
+                                    graph_data={[0,100]}
+                                    label_data={["--","--"]}
+                                />
+                            }
+                        </div>
+                    </CardContent>
+                </Card>
+            </Grid>
+            <Grid item={true} xs={12} sm={12} md={6}>
+                <Card className={styles.card}>
+                    <CardContent>
+                        <div className={styles.card_content_typography} >
+                            <div className={styles.cardcontent_typography}>
+                                <div>
+                                    Batch Details: 
+                                </div>
+                                {
+                                    selectedExperiment.hasOwnProperty("current_batch") && selectedExperiment.hasOwnProperty("num_batches") ?
+                                    <div>
+                                        {selectedExperiment.current_batch} / {selectedExperiment.num_batches}
+                                    </div>
+                                    :
+                                    <div>--</div>
+                                }
+                            </div>
+                        </div>
+                        <div className={styles.donought_container}>
+                            {
+                                selectedExperiment.hasOwnProperty("batch_progress") && selectedExperiment.batch_progress ?
+                                <HalfDonoughtGraph
+                                    graph_data={
+                                        [
+                                            selectedExperiment.batch_progress,
+                                            1-selectedExperiment.batch_progress
+                                        ]
+                                    }
+                                    label_data={
+                                        [
+                                            selectedExperiment.batch_progress*100+"%",
+                                            (1-selectedExperiment.batch_progress)*100+"%"
+                                        ]
+                                    }
+                                />
+                                :
+                                <HalfDonoughtGraph 
+                                    graph_data={[0,100]}
+                                    label_data={["--","--"]}
+                                />
+                            }
+                        </div>
+                    </CardContent>
+                </Card>
+            </Grid>
+        </Grid>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.module.css b/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.module.css
new file mode 100644
index 00000000..e5d8315a
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.module.css
@@ -0,0 +1,4 @@
+.donought {
+    max-height: 250px !important;
+    max-width: 250px !important;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.tsx
new file mode 100644
index 00000000..b5189d5d
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/HalfDonoughtGraph/HalfDonoughtGraph.tsx
@@ -0,0 +1,74 @@
+
+import { Doughnut } from 'react-chartjs-2';
+import styles from './HalfDonoughtGraph.module.css';
+import { Chart as ChartJS, Filler, ArcElement, Tooltip, Legend } from 'chart.js';
+ChartJS.register(
+    ArcElement,
+    Tooltip,
+    Legend,
+    Filler
+);
+
+interface HalfDonoughtGraphDataProps {
+    graph_data: Array<number>;
+    label_data: Array<string>;
+}
+
+const HalfDonoughtGraph: React.FC<HalfDonoughtGraphDataProps> = (props) => {
+    
+    return (
+        <Doughnut
+            className={styles.donought}
+            data={{
+                labels: props.label_data,
+                datasets: [
+                    {
+                        data: props.graph_data,
+                        backgroundColor: [
+                            "#36A2EB",
+                            "transparent"
+                        ],
+                        borderColor: "#D1D6DC",
+                        borderWidth: 1,
+                    },
+                ],
+            }}
+            options={{
+                animation: {
+                    duration: 0,
+                    easing: 'linear'
+                },
+                plugins: {
+                    legend: {
+                        display: false
+                    },
+                    tooltip: {
+                        enabled: false
+                    }
+                },
+                rotation: -90,
+                circumference: 180,
+                cutout: "70%",
+                maintainAspectRatio: true,
+                responsive: true
+            }}
+            plugins={[{
+                id: "textCenter",
+                beforeDraw: (chart) => {
+                    const {ctx, data} = chart;
+                    let text:any = "--"; // have to use 'any' type here as charts -> data.labels have type 'unknown[]'. So taking data.lables[0] is also unknown.
+                    if (data.labels) {
+                        text = data.labels[0];
+                    }
+                    ctx.font = 'bolder 40px sans-serif';
+                    ctx.fillStyle = '#36A2EB';
+                    ctx.textAlign = 'center';
+                    ctx.fillText(text, chart.getDatasetMeta(0).data[0].x, chart.getDatasetMeta(0).data[0].y);
+                    ctx.save();  
+                }
+            }]}
+        />        
+    );
+}
+
+export default HalfDonoughtGraph;
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardCudaList.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardCudaList.tsx
new file mode 100644
index 00000000..c6c0d321
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardCudaList.tsx
@@ -0,0 +1,112 @@
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell, { tableCellClasses } from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import Paper from '@mui/material/Paper';
+import { cudaDeviceListInterface } from './ModelCards';
+import { Box, Card, CardContent, TableFooter, TablePagination } from '@mui/material';
+import styles from './ModelCards.module.css';
+import { useState } from 'react';
+import { styled } from '@mui/material/styles';
+
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+    [`&.${tableCellClasses.head}`]: {
+      backgroundColor: theme.palette.common.black,
+      color: theme.palette.common.white,
+    }
+}));
+
+const StyledTableRow = styled(TableRow)(({ theme }) => ({
+    '&:nth-of-type(odd)': {
+      backgroundColor: theme.palette.action.hover,
+    },
+    // hide last border
+    '&:last-child td, &:last-child th': {
+      border: 0,
+    },
+}));
+
+export default function ModelCardCudaList({cardTitle, cudaDeviceList} : {cardTitle: string, cudaDeviceList: Array<cudaDeviceListInterface>}) {
+
+    const [page, setPage] = useState(0);
+    const [rowsPerPage, setRowsPerPage] = useState(5);
+    const handleChangePage = (event: unknown, newPage: number) => {
+        setPage(newPage);
+    };
+    const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
+        setRowsPerPage(parseInt(event.target.value, 10));
+        setPage(0);
+    };
+
+    return(
+        <Box>
+            <div className={styles.model_card_content_typography}>
+                {cardTitle}
+            </div>
+            {
+                cudaDeviceList.length > 0 ?
+                <TableContainer component={Paper}>
+                    <Table size="small" aria-label="a dense table">
+                        <TableHead>
+                            <TableRow className={styles.table_header_bg}>
+                                <StyledTableCell>
+                                    GPU Name
+                                </StyledTableCell>
+                                <StyledTableCell>
+                                    Processor Count Of GPU
+                                </StyledTableCell>
+                                <StyledTableCell>
+                                    GPU Memory&nbsp;(GB)
+                                </StyledTableCell>
+                            </TableRow>
+                        </TableHead>
+                        <TableBody>
+                            {
+                                (rowsPerPage > 0 ? cudaDeviceList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
+                                :
+                                cudaDeviceList
+                                ).map((row, index) => (
+                                    <StyledTableRow key={index}>
+                                        <TableCell>
+                                            {row.name}
+                                        </TableCell>
+                                        <TableCell>
+                                            {row.multi_proc_count}
+                                        </TableCell>
+                                        <TableCell>
+                                            {row.total_memory}
+                                        </TableCell>
+                                    </StyledTableRow>
+                                ))
+                            }
+                        </TableBody>
+                        <TableFooter>
+                            <TableRow>
+                                <TablePagination
+                                    rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
+                                    count={cudaDeviceList.length}
+                                    rowsPerPage={rowsPerPage}
+                                    page={page}
+                                    onPageChange={handleChangePage}
+                                    onRowsPerPageChange={handleChangeRowsPerPage}
+                                />
+                            </TableRow>
+                        </TableFooter>
+                    </Table>
+                </TableContainer>
+                :
+                <Card>
+                    <CardContent>
+                        <div className={styles.cardcontent_model_cards}>
+                            <div className={styles.loading_text}>
+                                No Data Available
+                            </div>
+                        </div>
+                    </CardContent>
+                </Card>
+            }
+        </Box>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardPythonPackagesList.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardPythonPackagesList.tsx
new file mode 100644
index 00000000..6d55b9ee
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCardPythonPackagesList.tsx
@@ -0,0 +1,102 @@
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell, { tableCellClasses } from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import Paper from '@mui/material/Paper';
+import { Box, Card, CardContent, TableFooter, TablePagination } from '@mui/material';
+import styles from './ModelCards.module.css';
+import { useState } from 'react';
+import { styled } from '@mui/material/styles';
+import { pythonPackagesListInterface } from './ModelCards';
+
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+    [`&.${tableCellClasses.head}`]: {
+      backgroundColor: theme.palette.common.black,
+      color: theme.palette.common.white,
+    }
+}));
+
+const StyledTableRow = styled(TableRow)(({ theme }) => ({
+    '&:nth-of-type(odd)': {
+      backgroundColor: theme.palette.action.hover,
+    },
+    // hide last border
+    '&:last-child td, &:last-child th': {
+      border: 0,
+    },
+}));
+
+export default function ModelCardCudaList({cardTitle, pythonPackagesList} : {cardTitle: string, pythonPackagesList: Array<pythonPackagesListInterface>}) {
+
+    const [page, setPage] = useState(0);
+    const [rowsPerPage, setRowsPerPage] = useState(5);
+    const handleChangePage = (event: unknown, newPage: number) => {
+        setPage(newPage);
+    };
+    const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
+        setRowsPerPage(parseInt(event.target.value, 10));
+        setPage(0);
+    };
+
+    return(
+        <Box>
+            <div className={styles.model_card_content_typography}>
+                {cardTitle}
+            </div>
+            {
+                pythonPackagesList.length > 0 ?
+                <TableContainer component={Paper}>
+                    <Table size="small" aria-label="a dense table">
+                        <TableHead>
+                            <TableRow>
+                                <StyledTableCell>Package Name</StyledTableCell>
+                                <StyledTableCell>Package Version</StyledTableCell>
+                            </TableRow>
+                        </TableHead>
+                        <TableBody>
+                            {
+                                (rowsPerPage > 0 ? pythonPackagesList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
+                                :
+                                pythonPackagesList
+                                ).map((row, index) => (
+                                    <StyledTableRow key={index}>
+                                        <TableCell>
+                                            {row.name}
+                                        </TableCell>
+                                        <TableCell>
+                                            {row.version}
+                                        </TableCell>
+                                    </StyledTableRow>
+                                ))
+                            }
+                        </TableBody>
+                        <TableFooter>
+                            <TableRow>
+                                <TablePagination
+                                    rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
+                                    count={pythonPackagesList.length}
+                                    rowsPerPage={rowsPerPage}
+                                    page={page}
+                                    onPageChange={handleChangePage}
+                                    onRowsPerPageChange={handleChangeRowsPerPage}
+                                />
+                            </TableRow>
+                        </TableFooter>
+                    </Table>
+                </TableContainer>
+                :
+                <Card>
+                    <CardContent>
+                        <div className={styles.cardcontent_model_cards}>
+                            <div className={styles.loading_text}>
+                                No Data Available
+                            </div>
+                        </div>
+                    </CardContent>
+                </Card>
+            }
+        </Box>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.module.css b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.module.css
new file mode 100644
index 00000000..d0c3ad14
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.module.css
@@ -0,0 +1,35 @@
+.cardcontent_model_cards {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+}
+
+.error_text {
+    font-family: sans-serif;
+    color: red;
+    font-weight: bold;
+    font-size: 25px;
+}
+
+.loading_text {
+    font-family: sans-serif;
+    font-size: 20px;
+    margin-top: 10px;
+}
+
+.model_card_content_typography {
+    color: gray;
+    margin-bottom: 10px;
+    font-family: sans-serif;
+}
+
+.table_header_bg {
+    background-color: rgb(147, 147, 147);
+    color: white;
+}
+
+.table_header_text {
+    color: white;
+    font-weight: bold;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.tsx b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.tsx
new file mode 100644
index 00000000..9f2585d1
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/experimentPage/ModelCards/ModelCards.tsx
@@ -0,0 +1,124 @@
+import { Grid, Card, CardContent } from '@mui/material';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
+import api from '../../../app/ApiMaster';
+import { useAppSelector } from "../../../app/hooks";
+import { getGridSearchId, getRestApiUrl } from '../../../redux/status/statusSlice';
+import styles from './ModelCards.module.css';
+import { AnyKeyValuePairsInterface } from '../ExperimentPage';
+import { CardDetails } from '../ExperimentDetails/CardDetails';
+import ModelCardCudaList from './ModelCardCudaList';
+import ModelCardPythonPackagesList from './ModelCardPythonPackagesList';
+
+export interface pythonPackagesListInterface {
+    "name": string,
+    "version": string
+}
+
+export interface cudaDeviceListInterface {
+    "name": string,
+    "multi_proc_count": string,
+    "total_memory": string
+}
+let sysInfoAnyKeyObj:AnyKeyValuePairsInterface = {};
+
+export default function ModelCards({experimentIdProp} : {experimentIdProp: string}) {
+
+    const grid_search_id = useAppSelector(getGridSearchId);
+    const rest_api_url = useAppSelector(getRestApiUrl);
+    const [error, setError] = useState("");
+    const [isLoading, setIsLoading] = useState(false);
+    const[sysInfoBasicData, setSysInfoBasicData] = useState(sysInfoAnyKeyObj);
+    const[sysInfoCudaDevicesData, setSysInfoCudaDevicesData] = useState(Array<cudaDeviceListInterface>);
+    const[sysInfoPythonPackages, setSysInfoPythonPackages] = useState([]);
+
+
+    useEffect(() => {
+        if(experimentIdProp) {
+            getSysInfo();
+        }
+    },[experimentIdProp]);
+
+    function getSysInfo() {
+        let model_card_sys_info = api.model_card_sys_info.replace("<grid_search_id>", grid_search_id);
+        model_card_sys_info = model_card_sys_info.replace("<experiment_id>", experimentIdProp);
+
+        setError("");
+        setIsLoading(true);
+
+        axios.get(rest_api_url + model_card_sys_info).then((response) => {
+            console.log("Got response from model_card_sys_info API: ", response);
+            if (response.status === 200) {
+                let resp_data = response.data;
+                Object.keys(resp_data).map((sysInfoKeyName) => {
+                    if(sysInfoKeyName === "cuda_device_list") {
+                        setSysInfoCudaDevicesData(resp_data[sysInfoKeyName]);
+                    }
+                    else if(sysInfoKeyName === "python-packages") {
+                        setSysInfoPythonPackages(resp_data[sysInfoKeyName])
+                    }
+                    else {
+                        sysInfoBasicData[sysInfoKeyName] = resp_data[sysInfoKeyName];
+                    }
+                });
+                setSysInfoBasicData(sysInfoBasicData);
+            }
+            else {
+                setError("Error occured / No system info available");
+            }
+            setIsLoading(false);
+        })
+        .catch((error) => {
+            console.log("Error in model_card_sys_info: ", error);
+            setIsLoading(false);
+            setError("Error occured / No system info available");
+        });
+    }
+
+    return(
+        <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+            <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+            {
+                isLoading ?
+                <Card className={styles.cardcontent_model_cards}>
+                    <CardContent>
+                        <div className={styles.loading_text}> 
+                            Please wait, loading model cards...
+                        </div>
+                    </CardContent>
+                </Card>
+                :
+                error.length > 0 ?
+                <Card className={styles.cardcontent_model_cards}>
+                    <CardContent>
+                        <div className={styles.error_text}> 
+                            {error}
+                        </div>
+                    </CardContent>
+                </Card>
+                :
+                <Grid container rowSpacing={1} spacing={{ xs: 1, md: 2 }}>
+                    <Grid item={true} xs={12} sm={12} md={4}>
+                        <CardDetails
+                            cardTitle="System Info"
+                            contentObj={sysInfoBasicData}
+                        />
+                    </Grid>
+                    <Grid item={true} xs={12} sm={12} md={4}>
+                        <ModelCardCudaList 
+                            cardTitle="Cuda Devices List" 
+                            cudaDeviceList={sysInfoCudaDevicesData}
+                        />
+                    </Grid>
+                    <Grid item={true} xs={12} sm={12} md={4}>
+                        <ModelCardPythonPackagesList 
+                            cardTitle="Python Packages List" 
+                            pythonPackagesList={sysInfoPythonPackages}
+                        />
+                    </Grid>
+                </Grid>
+            }
+            </Grid>
+        </Grid>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/graphs/Graph.tsx b/src/ml_board/frontend/Dashboard/src/components/graphs/Graph.tsx
index 4404a81a..36a79f24 100644
--- a/src/ml_board/frontend/Dashboard/src/components/graphs/Graph.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/graphs/Graph.tsx
@@ -3,28 +3,43 @@ import { ChartData, ChartOptions } from "chart.js";
 import { Line } from "react-chartjs-2";
 import { useAppSelector } from "../../app/hooks";
 import { selectChartLabelsById, selectExperimentsPerChartById } from "../../redux/charts/chartsSlice";
-import { selectColorMap } from "../../redux/status/statusSlice";
 // styles
 import styles from "./Graphs.module.css";
 
+const selectColor = (index: number): string => `hsl(${index * 137.5},75%,50%)`;
+
 // https://www.chartjs.org/docs/latest/general/data-structures.html
 
-export default function Graph({ chart_id }: { chart_id: string }) {
+export default function Graph({ chart_id, exp_id, exp_data }: { chart_id: string, exp_id?:string, exp_data?: Array<number> }) {
 
     const chartLabels = useAppSelector(state => selectChartLabelsById(state, chart_id));
     const experimentsDict = useAppSelector(state => selectExperimentsPerChartById(state, chart_id));
-    const colors = useAppSelector(selectColorMap);
 
     // prepare data (Warning Looping!)
     const data: ChartData<"line"> = {
-        labels: chartLabels, // the X-axis:Array<number>
-        datasets: !experimentsDict ? [] :
-            // loop over the experiments in the ChartF
+        // labels = the X-axis:Array<number>
+        labels: 
+            exp_id && exp_data ?
+            Array.from({ length: exp_data.length }, (_, i) => i)
+            :
+            chartLabels, 
+        datasets: 
+            exp_id && exp_data ?
+            [{
+                label: "experiment_" + exp_id, // exp_name
+                data: exp_data, // exp_values:Array<number>, Y-axis, same size as X-axis
+                backgroundColor: selectColor(Number(exp_id)),
+                borderColor: selectColor(Number(exp_id)),
+            }]
+            :
+            !experimentsDict ? 
+            [] 
+            :
             Object.values(experimentsDict).map(exp => ({
                 label: "experiment_" + exp!.exp_id, // exp_name
                 data: exp!.data, // exp_values:Array<number>, Y-axis, same size as X-axis
-                backgroundColor: colors[exp!.exp_id],
-                borderColor: colors[exp!.exp_id],
+                backgroundColor: selectColor(exp!.exp_id),
+                borderColor: selectColor(exp!.exp_id),
             })),
     };
 
@@ -40,14 +55,14 @@ export default function Graph({ chart_id }: { chart_id: string }) {
                 pointRadius:0,
                 // showLine: false,
                 // // Automatic data decimation during draw happens if these 3 values are left as default!
-                // tension:0,
+                // tension:0, // 0 for straight lines otherwise curvy lines
                 // stepped:false,
                 // borderDash:[],
             }
         },
         plugins: {
             title: {
-                text: chart_id,
+                text: chart_id.toLowerCase().split("_").join(" "),
                 display: true,
                 color: 'black',
                 font: {
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.module.css b/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.module.css
new file mode 100644
index 00000000..46a91563
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.module.css
@@ -0,0 +1,14 @@
+.textfield_cardcontent_grid_search_config {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.yaml_content_overflow {
+    overflow: auto
+}
+
+.heading_style {
+    font-family: sans-serif;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.tsx b/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.tsx
new file mode 100644
index 00000000..855cb56b
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/GridSearchConfigurations/GridSearchConfigurations.tsx
@@ -0,0 +1,115 @@
+import { Grid, Card, CardContent, IconButton, Switch, FormControlLabel, Box, LinearProgress } from '@mui/material';
+import { Download } from '@mui/icons-material';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
+import api, { defaultGridSearchConfigFileName } from '../../../app/ApiMaster';
+import { useAppSelector } from "../../../app/hooks";
+import { getGridSearchId, getRestApiUrl } from '../../../redux/status/statusSlice';
+import styles from './GridSearchConfigurations.module.css';
+
+export default function GridSearchConfigurations() {
+    
+    const [yamlString, setYamlString] = useState("");
+    const [error, setError] = useState("");
+    const [isLoading, setIsLoading] = useState(false);
+    const [showHideData, setShowHideData] = useState(false);
+    const grid_search_id = useAppSelector(getGridSearchId);
+    const rest_api_url = useAppSelector(getRestApiUrl);
+
+    useEffect(() => {
+        let grid_search_config_url = api.gridsearch_config_file.replace("<grid_search_id>", grid_search_id);
+
+        setError("");
+        setShowHideData(false);
+        setIsLoading(true);
+        
+        axios.get(rest_api_url+grid_search_config_url).then((response) => {
+            console.log("Got response from grid_search_config_file API: ", response);
+            if(response.status === 200) {
+                setYamlString(response.data.trim());
+                setShowHideData(true);
+            }
+            else {
+                setError("Oops! an error occurred");
+            }
+            setIsLoading(false);
+        })
+        .catch((error) => {
+            console.log("Error in grid_search_config_file: ", error);
+            setIsLoading(false);
+            setError("Oops! an error occurred");
+        });
+
+    },[]);
+
+    function downloadFile() {
+        const blob = new Blob([yamlString], { type: 'text/yaml' });
+        const url = window.URL.createObjectURL(blob!);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download = defaultGridSearchConfigFileName+".yml";
+        document.body.appendChild(a);
+        a.click();
+        document.body.removeChild(a);
+    }
+
+    return(
+        <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+            <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                <Box className={styles.textfield_cardcontent_grid_search_config}>
+                    <h2 className={styles.heading_style}>Grid Search Configuration</h2>
+                    <div>
+                    {
+                        !isLoading && error.length === 0 && yamlString.length > 0 ?
+                        <IconButton 
+                            size="large"
+                            onClick={()=>downloadFile()}
+                        >
+                            <Download /> 
+                        </IconButton>
+                        :
+                        null
+                    }
+                    {
+                        !isLoading && error.length === 0 && yamlString.length > 0 ?
+                        <FormControlLabel
+                            control={
+                                <Switch
+                                    checked={showHideData}
+                                    color="primary"
+                                    onChange={()=>setShowHideData(!showHideData)}
+                                />
+                            }
+                            label={"View/Hide"}
+                        />
+                        :
+                        null
+                    }
+                    </div>
+                </Box>
+            </Grid>
+            {
+                isLoading ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Box>
+                        <LinearProgress />
+                    </Box>
+                </Grid>
+                :
+                null
+            }
+            {
+                !isLoading && error.length === 0 && Object.keys(yamlString).length > 0 && showHideData ?
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Card>
+                        <CardContent className={styles.yaml_content_overflow}>
+                            <pre>{yamlString}</pre>
+                        </CardContent>
+                    </Card>
+                </Grid>
+                :
+                null
+            }
+        </Grid>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.module.css b/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.module.css
new file mode 100644
index 00000000..d69d2c93
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.module.css
@@ -0,0 +1,10 @@
+.heading_style {
+    font-family: sans-serif;
+}
+
+.textfield_cardcontent_select_experiment {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.tsx b/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.tsx
new file mode 100644
index 00000000..c680448a
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/SelectExperimentDropdown/SelectExperimentDropdown.tsx
@@ -0,0 +1,129 @@
+import { getGridSearchId, getRestApiUrl } from '../../../redux/status/statusSlice';
+import api from '../../../app/ApiMaster';
+import axios from 'axios';
+import { useEffect, useState } from 'react';
+import { useAppSelector } from "../../../app/hooks";
+import { Grid, Box, LinearProgress, Button } from '@mui/material';
+import FormHelperText from '@mui/material/FormHelperText';
+import FormControl from '@mui/material/FormControl';
+import Select from '@mui/material/Select';
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import SendIcon from '@mui/icons-material/Send';
+import styles from './SelectExperimentDropdown.module.css';
+import { useNavigate } from 'react-router-dom';
+import { AnyKeyValuePairsInterface } from '../../experimentPage/ExperimentPage';
+
+export default function SelectExperimentDropdown() {
+    
+    const [experimentIdsInDropdown, setExperimentIdsInDropdown] = useState(Array<string>);
+    const [selectedExperiment, setSelectedExperiment] = useState("");
+    const [error, setError] = useState("");
+    const [isLoading, setIsLoading] = useState(false);
+    const grid_search_id = useAppSelector(getGridSearchId);
+    const rest_api_url = useAppSelector(getRestApiUrl);
+    const navigate = useNavigate();
+
+    useEffect(() => {
+
+        setError("");
+        setIsLoading(true);
+
+        let experiments_url = api.experiments.replace("<grid_search_id>", grid_search_id);
+        axios.get(rest_api_url + experiments_url).then((response) => {
+            console.log("Got response from experiments API: ", response);
+            if(response.status === 200) {
+                if (response.data.length > 0) {
+                    let experimentIdsInDropdown:Array<string> = [];
+                    response.data.map((exp_data:AnyKeyValuePairsInterface ) => {
+                        experimentIdsInDropdown.push(exp_data.experiment_id.toString());
+                    });
+                    experimentIdsInDropdown.sort();
+                    setExperimentIdsInDropdown(experimentIdsInDropdown);
+                    setSelectedExperiment(experimentIdsInDropdown[0]);
+                }
+                else {
+                    setError("No Experiments Found!");
+                }
+            }
+            else {
+                setError("Oops! an error occurred");
+            }
+            setIsLoading(false);
+        })
+        .catch((error) => {
+            console.log("Error in experiments API: ", error);
+            setIsLoading(false);
+            setError("Oops! an error occurred");
+        });
+    },[]);
+
+    function goToSelectedExperiment() {
+        if (selectedExperiment !== "") {
+            navigate({
+                pathname: '/experiment',
+                search: '?experiment_id='+selectedExperiment,
+            });
+        }
+    }
+
+    function setSelectedExpValue(value: string) {
+        setSelectedExperiment(value);
+    }
+
+    return(
+        <div>
+            <h2 className={styles.heading_style}>Experiment Details</h2>
+            <Grid container spacing={{ xs: 2, sm: 2, md: 2, lg: 2 }}>
+                <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                    <Box className={styles.textfield_cardcontent_select_experiment}>
+                        <FormControl fullWidth={true} error={error.length > 0}>
+                            <InputLabel id="demo-simple-select-helper-label">Experiment Id</InputLabel>
+                            <Select
+                                labelId="demo-simple-select-helper-label"
+                                id="demo-simple-select-helper"
+                                value={selectedExperiment}
+                                label="Experiment Id"
+                                onChange={(e)=>setSelectedExpValue(e.target.value)}
+                                MenuProps={{ style: { maxHeight: "250px" } }} // have to keep style here. keeping it in css file is not working for this
+                            >
+                                {
+                                    experimentIdsInDropdown.map((exp_id)=> 
+                                        <MenuItem key={exp_id} value={exp_id}>{exp_id}</MenuItem>
+                                    )
+                                }
+                            </Select>
+                            {
+                                error.length > 0 ?
+                                <FormHelperText>
+                                    {error}
+                                </FormHelperText>
+                                :
+                                null
+                            }
+                        </FormControl>
+                        <Button 
+                            variant="contained" 
+                            size="large" 
+                            endIcon={<SendIcon />}
+                            onClick={()=>goToSelectedExperiment()}
+                            disabled={isLoading || error.length > 0}
+                        >
+                            
+                        </Button>
+                    </Box>
+                </Grid>
+                {
+                    isLoading ?
+                    <Grid item={true} xs={12} sm={12} md={12} lg={12}>
+                        <Box>
+                            <LinearProgress />
+                        </Box>
+                    </Grid>
+                    :
+                    null
+                }
+            </Grid>
+        </div>
+    );
+}
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/Settings.module.css b/src/ml_board/frontend/Dashboard/src/components/settings/Settings.module.css
index 1b828db8..dc38e952 100644
--- a/src/ml_board/frontend/Dashboard/src/components/settings/Settings.module.css
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/Settings.module.css
@@ -2,10 +2,14 @@
     display: flex;
     flex-direction: column;
     align-items: center;
-    padding-top: 5%;
+    padding-top: 2%;
 }
 
-.settings_text_box {
+.settings_text_box_full_width {
+    width: 100%;
+}
+
+.settings_text_box_less_width {
     width: 60%;
 }
 
@@ -14,6 +18,31 @@
     flex-direction: row;
     align-items: center;
     justify-content: center;
-    width: 60%;
-    gap: 40px
+    width: 100%;
+    gap: 40px;
+    margin-top: 20px;
+    margin-bottom: 40px;
+}
+
+.grid_container_basic_fields {
+    padding-left: 30px;
+    padding-right: 30px;
+}
+
+.grid_item_basic_fields {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 20px;
+}
+
+.grid_search_config_contianer {
+    padding-left: 30px;
+    padding-right: 30px;
+    display: flex;
+    flex-direction: column;
+}
+
+.heading_style {
+    font-family: sans-serif;
 }
\ No newline at end of file
diff --git a/src/ml_board/frontend/Dashboard/src/components/settings/Settings.tsx b/src/ml_board/frontend/Dashboard/src/components/settings/Settings.tsx
index 1e0cbe6f..0595b9e4 100644
--- a/src/ml_board/frontend/Dashboard/src/components/settings/Settings.tsx
+++ b/src/ml_board/frontend/Dashboard/src/components/settings/Settings.tsx
@@ -1,14 +1,19 @@
-import { Box, Button, TextField, Toolbar } from "@mui/material";
+import { Box, Button, TextField, Toolbar, Grid } from "@mui/material";
 import { useCallback, useEffect, useState } from 'react';
 import DeleteIcon from '@mui/icons-material/Delete';
 import SendIcon from '@mui/icons-material/Send';
 import { settingConfigsInterface } from "../../app/App";
+import { isConnected } from "../../redux/status/statusSlice";
+import { useAppSelector } from "../../app/hooks";
+import GridSearchConfigurations from "./GridSearchConfigurations/GridSearchConfigurations";
+import SelectExperimentDropdown from "./SelectExperimentDropdown/SelectExperimentDropdown";
 // styles
 import styles from "./Settings.module.css";
 
 export const defaultGridSearchIdHelperText = "eg: 2022-11-06--17-59-10";
 export const defaultSocketConnectionUrlHelperText = "eg: http://127.0.0.1:5002";
 export const defaultRestApiUrlHelperText = "eg: http://127.0.0.1:5001";
+
 export interface FuncProps {
     validateConfigs(value:boolean): void;
     setConfigData(configData:settingConfigsInterface): void;
@@ -17,6 +22,8 @@ export interface FuncProps {
 // Settings file and Config popup file (except render part) might look exactly the same for now. But Settigns will have more functionality later. So we will add more things in Settings which will make it different from popup file.
 const Settings: React.FC<FuncProps> = (props) => {
 
+    const isSocketConnected = useAppSelector(isConnected);
+
     const [configTextState, setConfigTextState] = useState({
         gridSearchId: "",
         socketConnectionUrl: "",
@@ -78,61 +85,79 @@ const Settings: React.FC<FuncProps> = (props) => {
         <Box>
             <Toolbar/>
             <Box className={styles.settings_main_box}>
-                <TextField
-                    id="outlined-multiline-flexible"
-                    label="Grid Search-id"
-                    placeholder="Enter grid search-id here!..."
-                    value={configTextState.gridSearchId}
-                    onChange={(e)=>changeText("gridSearchId", e.target.value)}
-                    className={styles.settings_text_box}
-                    helperText={
-                        defaultGridSearchIdHelperText
-                    }
-                />
-                <br/>
-                <TextField
-                    id="outlined-multiline-flexible"
-                    label="Socket Connection URL"
-                    placeholder="Enter Socker Connection URL here!..."
-                    value={configTextState.socketConnectionUrl}
-                    onChange={(e)=>changeText("socketConnectionUrl", e.target.value)}
-                    className={styles.settings_text_box}
-                    helperText={
-                        defaultSocketConnectionUrlHelperText
-                    }
-                />
-                <br/>
-                <TextField
-                    id="outlined-multiline-flexible"
-                    label="Rest API URL"
-                    placeholder="Enter rest API URL here!..."
-                    value={configTextState.restApiUrl}
-                    onChange={(e)=>changeText("restApiUrl", e.target.value)}
-                    className={styles.settings_text_box}
-                    helperText={
-                        defaultRestApiUrlHelperText
-                    }
-                />
-                <br/>
-                <Box className={styles.setting_form_btns_box}>
-                    <Button 
-                        variant="outlined" 
-                        size="large"
-                        startIcon={<DeleteIcon />}
-                        onClick={()=>clearAllText()}
-                    >
-                        Clear
-                    </Button>
-                    <Button 
-                        variant="contained" 
-                        size="large" 
-                        endIcon={<SendIcon />}
-                        onClick={()=>submitData()}
-                        disabled={validateData()}
-                    >
-                        Save
-                    </Button>
+                <Grid container rowSpacing={1} rowGap={3} spacing={{ xs: 1, md: 2, lg: 2 }} className={styles.grid_container_basic_fields}>
+                    <Grid item={true} xs={12} sm={12} md={12} lg={isSocketConnected?4:12} className={styles.grid_item_basic_fields}>
+                        <TextField
+                            id="outlined-multiline-flexible"
+                            label="Grid Search-id"
+                            placeholder="Enter grid search-id here!..."
+                            value={configTextState.gridSearchId}
+                            onChange={(e)=>changeText("gridSearchId", e.target.value)}
+                            className={isSocketConnected?styles.settings_text_box_full_width:styles.settings_text_box_less_width}
+                            helperText={defaultGridSearchIdHelperText}
+                            fullWidth={isSocketConnected?true:false}
+                        />
+                    </Grid>
+                    <Grid item={true} xs={12} sm={12} md={12} lg={isSocketConnected?4:12} className={styles.grid_item_basic_fields}>
+                        <TextField
+                            id="outlined-multiline-flexible"
+                            label="Socket Connection URL"
+                            placeholder="Enter Socker Connection URL here!..."
+                            value={configTextState.socketConnectionUrl}
+                            onChange={(e)=>changeText("socketConnectionUrl", e.target.value)}
+                            className={isSocketConnected?styles.settings_text_box_full_width:styles.settings_text_box_less_width}
+                            helperText={defaultSocketConnectionUrlHelperText}
+                            fullWidth={isSocketConnected?true:false}
+                        />
+                    </Grid>
+                    <Grid item={true} xs={12} sm={12} md={12} lg={isSocketConnected?4:12} className={styles.grid_item_basic_fields}>
+                        <TextField
+                            id="outlined-multiline-flexible"
+                            label="Rest API URL"
+                            placeholder="Enter rest API URL here!..."
+                            value={configTextState.restApiUrl}
+                            onChange={(e)=>changeText("restApiUrl", e.target.value)}
+                            className={isSocketConnected?styles.settings_text_box_full_width:styles.settings_text_box_less_width}
+                            helperText={defaultRestApiUrlHelperText}
+                            fullWidth={isSocketConnected?true:false}
+                        />
+                    </Grid>
+                </Grid>
+            </Box>
+            {
+                isSocketConnected ?
+                <Box className={styles.grid_search_config_contianer}>
+                    <SelectExperimentDropdown />
+                </Box>
+                :
+                null
+            }
+            {
+                isSocketConnected ?
+                <Box className={styles.grid_search_config_contianer}>
+                    <GridSearchConfigurations />
                 </Box>
+                :
+                null
+            }
+            <Box className={styles.setting_form_btns_box}>
+                <Button 
+                    variant="outlined" 
+                    size="large"
+                    startIcon={<DeleteIcon />}
+                    onClick={()=>clearAllText()}
+                >
+                    Clear
+                </Button>
+                <Button 
+                    variant="contained" 
+                    size="large" 
+                    endIcon={<SendIcon />}
+                    onClick={()=>submitData()}
+                    disabled={validateData()}
+                >
+                    Save
+                </Button>
             </Box>
         </Box>
     )
diff --git a/src/ml_board/frontend/Dashboard/src/components/topbar-with-drawer/TopBarWithDrawer.module.css b/src/ml_board/frontend/Dashboard/src/components/topbar-with-drawer/TopBarWithDrawer.module.css
index a3a304ed..7f6b4baf 100644
--- a/src/ml_board/frontend/Dashboard/src/components/topbar-with-drawer/TopBarWithDrawer.module.css
+++ b/src/ml_board/frontend/Dashboard/src/components/topbar-with-drawer/TopBarWithDrawer.module.css
@@ -27,6 +27,7 @@
 }
 .app_bar_right_corner_logo { 
     color: black !important;
+    margin-right: -20px !important;
 }
 .logo_inside_menu { 
     padding-top: 10px;
diff --git a/src/ml_board/frontend/Dashboard/src/redux/charts/chartsSlice.ts b/src/ml_board/frontend/Dashboard/src/redux/charts/chartsSlice.ts
index 2082fbb7..c4680bbe 100644
--- a/src/ml_board/frontend/Dashboard/src/redux/charts/chartsSlice.ts
+++ b/src/ml_board/frontend/Dashboard/src/redux/charts/chartsSlice.ts
@@ -33,13 +33,13 @@ export const chartsSlice = createSlice({
             // TODO: avoid this big loop !!!
             for (const chartUpdate of payload) {
                 const { chart_id, exp_id, epoch, score } = chartUpdate;
-                if (state.ids.includes(chart_id)) {
+                if (chart_id in state.entities) { //if (state.entities[chart_id] !== undefined)
                     const chart = state.entities[chart_id] as Chart;
                     // update X-axis
-                    if (!chart.x_axis.includes(epoch))
+                    if (!(epoch in chart.x_axis)) // if (chart.x_axis[epoch] != epoch)
                         chart.x_axis.push(epoch)
-                    // if experiment exist push the score otherwise add it as new
-                    if (chart.experiments.ids.includes(exp_id)) {
+                    // if experiment exists, push the score, otherwise add it as new
+                    if (exp_id in chart.experiments.entities) { //if (chart.experiments.entities[exp_id] !== undefined)
                         (chart.experiments.entities[exp_id] as Experiment).data.push(score);
                     } else {
                         chart.experiments = experimentsAdapter.addOne(chart.experiments, {
@@ -69,7 +69,16 @@ export const { upsertCharts } = chartsSlice.actions;
 // TODO: memoize these selectors
 export const selectChartLabelsById = (state: RootState, chart_id: string) => state.charts.entities[chart_id]?.x_axis ?? [];
 export const selectExperimentsPerChartById = (state: RootState, chart_id: string) => state.charts.entities[chart_id]?.experiments.entities ?? {};
-
+export const selectChartsByExperimentId = (state: RootState, exp_id: string) =>{
+    const allExp: {[key: string]: any} = {};
+    for (const chart_id in state.charts.entities) {
+        const experiments = state.charts.entities[chart_id]?.experiments.entities ?? {};
+        if(experiments[exp_id]) {
+            allExp[chart_id] = experiments[exp_id];
+        }
+    }
+    return allExp;
+}
 
 // create a set of memoized selectors
 export const {
diff --git a/src/ml_board/frontend/Dashboard/src/redux/status/statusSlice.ts b/src/ml_board/frontend/Dashboard/src/redux/status/statusSlice.ts
index 0854755b..cb9eaf01 100644
--- a/src/ml_board/frontend/Dashboard/src/redux/status/statusSlice.ts
+++ b/src/ml_board/frontend/Dashboard/src/redux/status/statusSlice.ts
@@ -1,5 +1,4 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { upsertCharts } from '../charts/chartsSlice';
 import { RootState } from '../store';
 
 // TODO: ASK MAX, renaming to GlobalConfigSlice
@@ -11,8 +10,8 @@ export interface StatusState {
   received_msg_count: number;
   throughput: number;
   grid_search_id: string;
-  color_map: { [expID: string]: string }; // expID mapped to a color
   metric_loss: Array<string>;
+  rest_api_url: string;
 }
 
 const initialState: StatusState = {
@@ -23,8 +22,8 @@ const initialState: StatusState = {
   received_msg_count: 0,
   throughput: 0,
   grid_search_id: "",
-  color_map: {},
-  metric_loss: [] //TODO: redundant??
+  metric_loss: [], //TODO: redundant??
+  rest_api_url: ""
 };
 
 export const statusSlice = createSlice({
@@ -52,33 +51,39 @@ export const statusSlice = createSlice({
     setGridSearchId: (state, action: PayloadAction<string>) => {
       state.grid_search_id = action.payload;
     },
+    setRestApiUrl: (state, action: PayloadAction<string>) => {
+      state.rest_api_url = action.payload;
+    },
     // ASK MAX: To be used if we want to save the Metric or Losses key here for the table
     // I stopped from doing so because then function is going to be called every evaluation_result message!!!
     upsertMetricOrLoss: (state, { payload }: PayloadAction<string[]>) => {
       state.metric_loss.push(...payload);
     }
-  }, extraReducers(builder) {
-    builder.addCase(upsertCharts, (state, { payload }) => {
-      // NOTE: very important to notice here that +4 increment
-      // because in the testing file every evaluation_result held 4 values for the same experiment
-      // this might not be true with other data
-      // TODO:
-      for (let i = 0; i < payload.length; i+=4) {
-        if (!state.color_map.hasOwnProperty(payload[i].exp_id)) {
-          // one can only hope that it doesn't produce a blue blue blue blue blue... pattern :')
-          state.color_map[payload[i].exp_id] = `#${Math.floor(Math.random() * 16777215).toString(16)}`;
-        }
-      }
-    })
-  },
+  }, 
+  // extraReducers(builder) {
+  //   builder.addCase(upsertCharts, (state, { payload }) => {
+  //     // NOTE: very important to notice here that +4 increment
+  //     // because in the testing file every evaluation_result held 4 values for the same experiment
+  //     // this might not be true with other data
+  //     for (let i = 0; i < payload.length; i+=4) {
+  //       if (!state.color_map.hasOwnProperty(payload[i].exp_id)) {
+  //         // one can only hope that it doesn't produce a blue blue blue blue blue... pattern :')
+  //         state.color_map[payload[i].exp_id] = `#${Math.floor(Math.random() * 16777215).toString(16)}`;
+  //       }
+  //     }
+  //   })
+  // },
 });
 
-export const { changeFilter, changeTab, setSocketConnection, setLastPing, incrementReceivedMsgCount, setThroughput } = statusSlice.actions;
+export const { changeFilter, changeTab, setSocketConnection, setLastPing, incrementReceivedMsgCount, setThroughput, setGridSearchId, setRestApiUrl } = statusSlice.actions;
+
 export const selectFilter = (state: RootState) => state.status.currentFilter;
 export const selectTab = (state: RootState) => state.status.idTab;
 export const isConnected = (state: RootState) => state.status.wsConnected;
 export const getLastPing = (state: RootState) => state.status.ping;
 export const getReceivevMsgCount = (state: RootState) => state.status.received_msg_count;
 export const getThroughput = (state: RootState) => state.status.throughput;
-export const selectColorMap = (state: RootState) => state.status.color_map;
+export const getGridSearchId = (state: RootState) => state.status.grid_search_id;
+export const getRestApiUrl = (state: RootState) => state.status.rest_api_url;
+
 export default statusSlice.reducer;
diff --git a/src/ml_board/frontend/Dashboard/src/redux/table/tableSlice.ts b/src/ml_board/frontend/Dashboard/src/redux/table/tableSlice.ts
index cde5ea3a..11c8ee9c 100644
--- a/src/ml_board/frontend/Dashboard/src/redux/table/tableSlice.ts
+++ b/src/ml_board/frontend/Dashboard/src/redux/table/tableSlice.ts
@@ -38,13 +38,6 @@ const rowsAdapter = createEntityAdapter<Row>({
     sortComparer: ({ experiment_id: id1 }: Row, { experiment_id: id2 }: Row) => id1 - id2
 });
 
-// interface RowsState {
-//     // The unique IDs of each Row.
-//     ids: [];
-//     // A lookup table mapping Rows' IDs to the corresponding Row objects
-//     entities: {};
-// }
-
 const initialState: EntityState<Row> = rowsAdapter.getInitialState({});
 
 export const tableSlice = createSlice({
diff --git a/src/ml_board/frontend/Dashboard/src/worker_socket/DataTypes.ts b/src/ml_board/frontend/Dashboard/src/worker_socket/DataTypes.ts
index e28416da..10f789d0 100644
--- a/src/ml_board/frontend/Dashboard/src/worker_socket/DataTypes.ts
+++ b/src/ml_board/frontend/Dashboard/src/worker_socket/DataTypes.ts
@@ -14,6 +14,11 @@ export interface DataFromSocket extends JSON {
     }
 }
 
+export interface BufferedDataFromSocket extends JSON {
+    event_id: string,
+    data: Array<DataFromSocket>
+}
+
 export interface DataToRedux {
     tableData: Row[],
     chartsUpdates: ChartUpdate[],
diff --git a/src/ml_board/frontend/Dashboard/src/worker_socket/MainThreadCallbacks.ts b/src/ml_board/frontend/Dashboard/src/worker_socket/MainThreadCallbacks.ts
index b89a8fcc..6ebd220f 100644
--- a/src/ml_board/frontend/Dashboard/src/worker_socket/MainThreadCallbacks.ts
+++ b/src/ml_board/frontend/Dashboard/src/worker_socket/MainThreadCallbacks.ts
@@ -1,4 +1,4 @@
-import { DataFromSocket, DataToRedux } from "./DataTypes";
+import { BufferedDataFromSocket, DataFromSocket, DataToRedux } from "./DataTypes";
 import { MLGYM_EVENT } from "./EventsTypes";
 import handleEvaluationResultData from "./event_handlers/EvaluationResultHandler";
 import handleExperimentStatusData from "./event_handlers/ExperimentStatusHandler";
@@ -17,6 +17,7 @@ const MapEventToProcess: { [event: string]: (output: DataToRedux, input: JSON) =
     },
     [MLGYM_EVENT.EXPERIMENT_CONFIG]: (dataToRedux: DataToRedux, data: JSON): void => console.log("Exp config found"),
     [MLGYM_EVENT.EXPERIMENT_STATUS]: (dataToRedux: DataToRedux, data: JSON): void => { dataToRedux.tableData.push(handleExperimentStatusData(data)) },
+    // [MLGYM_EVENT.BATCHED_EVENTS]: (dataToRedux: DataToRedux, data: JSON): void => { dataToRedux.tableData.push(handleJobStatusData(data)) },
 };
 
 
@@ -28,11 +29,25 @@ export const updateMainThreadCallback = (bufferedSocketData: Array<JSON>) => {
         chartsUpdates: []
     };
     // loop over all incoming data from socket
-    for (const data of bufferedSocketData) {
-        // parse data from socket then extract event_type and payload
-        const { data: { event_type, payload } } = data as DataFromSocket;
-        // process the payload and load it into the DataToRedux object
-        MapEventToProcess[event_type as keyof typeof MapEventToProcess](bufferedDataToRedux, payload);
+    for (const bfrdata of bufferedSocketData) {
+        let data_from_socket = bfrdata as DataFromSocket | BufferedDataFromSocket;
+        // handling batched events coming from socket itself
+        if(data_from_socket.event_id === "batched_events") {
+            const buffered_data =  data_from_socket as BufferedDataFromSocket;
+            // treating batched events as normal buffered events from web workers (like the else part of this function)
+            for(const iterated_buffer_data of buffered_data["data"]) {
+                // parse data from socket then extract event_type and payload
+                const { data: { event_type, payload } } = iterated_buffer_data as DataFromSocket;
+                // process the payload and load it into the DataToRedux object
+                MapEventToProcess[event_type as keyof typeof MapEventToProcess](bufferedDataToRedux, payload);
+            }
+        }
+        else {
+            // parse data from socket then extract event_type and payload
+            const { data: { event_type, payload } } = data_from_socket as DataFromSocket;
+            // process the payload and load it into the DataToRedux object
+            MapEventToProcess[event_type as keyof typeof MapEventToProcess](bufferedDataToRedux, payload);
+        }
     }
     // sending Data to the Main thread to store it in Redux
     postMessage(bufferedDataToRedux);
@@ -43,8 +58,8 @@ export const pingMainThreadCallback = (ping: number) => {
     postMessage({ status: { ping } } as DataToRedux);
 };
 
-export const connectionMainThreadCallback = (isSocketConnected: boolean) => {
-    postMessage({ status: { isSocketConnected } } as DataToRedux);
+export const connectionMainThreadCallback = (isSocketConnected: boolean, gridSearchId?: string, restApiUrl?: string) => {
+    postMessage({ status: { isSocketConnected, gridSearchId, restApiUrl } } as DataToRedux);
 };
 
 export const msgCounterIncMainThreadCallback = () => {
diff --git a/src/ml_board/frontend/Dashboard/src/worker_socket/WorkerSocket.ts b/src/ml_board/frontend/Dashboard/src/worker_socket/WorkerSocket.ts
index ad3352c9..78a6b12e 100644
--- a/src/ml_board/frontend/Dashboard/src/worker_socket/WorkerSocket.ts
+++ b/src/ml_board/frontend/Dashboard/src/worker_socket/WorkerSocket.ts
@@ -2,7 +2,6 @@ import socketIO, { Socket } from 'socket.io-client';
 import { settingConfigsInterface } from '../app/App';
 import { connectionMainThreadCallback, msgCounterIncMainThreadCallback, pingMainThreadCallback, throughputMainThreadCallback, updateMainThreadCallback } from './MainThreadCallbacks';
 
-
 // ========================= variables ============================//
 let socket: Socket; // 'let' to initialize on funciton call
 // Ping to measure Round Trip Time (RTT)
@@ -20,9 +19,9 @@ let buffering_interval: NodeJS.Timer;
 
 // =~=~=~=~=~=~=~=~=~=~=~=~=~= ~WebSocket~ =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=//
 const initSocket = (settingConfigs: settingConfigsInterface) => {
+    console.log("WebSocket initializing...");
     socket = socketIO(settingConfigs.socketConnectionUrl, { autoConnect: true });
-    console.log("WebSocket initialized");
-    socket.on('connect', () => onConnect(socket, settingConfigs.gridSearchId));
+    socket.on('connect', () => onConnect(socket, settingConfigs.gridSearchId, settingConfigs.restApiUrl));
     socket.on('disconnect', onDisconnect);
     socket.on('connect_error', onError);
     socket.on('mlgym_event', process_mlgym_event);
@@ -31,14 +30,14 @@ const initSocket = (settingConfigs: settingConfigsInterface) => {
 
 
 // ========================= connection events ============================//
-const onConnect = (socket: Socket, runId: string) => {
+const onConnect = (socket: Socket, gridSearchId: string, restApiUrl: string) => {
     //NOTE: for testing with the dummy_server.py set runId = "mlgym_event_subscribers";
     // Max added bug report here: https://github.com/mlgym/mlgym/issues/134
-    socket.emit('join', { rooms: [runId] });
+    socket.emit('join', { rooms: [gridSearchId] });
     // start periodic server pining
     pinging_interval = setInterval(send_ping_to_websocket_server, period * 1000, socket);
     // flag main thread that connection is on
-    connectionMainThreadCallback(true);
+    connectionMainThreadCallback(true, gridSearchId, restApiUrl);
     // start periodic buffer flushing
     buffering_interval = setInterval(() => { bufferQueue.length > 0 && flushBufferingWindow() }, BUFFER_WINDOW_LIMIT_IN_SECONDS * 1000);
 };
@@ -47,7 +46,6 @@ const onDisconnect = (reason: Socket.DisconnectReason) => stop(reason);
 
 const onError = (err: Error) => stop(err);
 
-
 // ========================= data driven events ============================//
 const process_mlgym_event = (msg: JSON) => {
     // TODO: maybe here instead of just pushing, insert based on the "event_id" or "creation_ts"
diff --git a/src/ml_board/frontend/Dashboard/src/worker_socket/event_handlers/evaluationResultDataHandler.tsx b/src/ml_board/frontend/Dashboard/src/worker_socket/event_handlers/evaluationResultDataHandler.tsx
new file mode 100644
index 00000000..10447ba9
--- /dev/null
+++ b/src/ml_board/frontend/Dashboard/src/worker_socket/event_handlers/evaluationResultDataHandler.tsx
@@ -0,0 +1,243 @@
+export interface evalResultCustomData {
+    grid_search_id: string | null, //TODO: move it to the statusSlice?
+    experiments: {
+        [key: string]: {
+            data: {
+                labels: Array<number>,
+                datasets: Array<{
+                    exp_id: number,
+                    label: string,
+                    data: Array<number>,
+                    fill: Boolean,
+                    backgroundColor: string,
+                    borderColor: string,
+                    tension: number, // to give smoothness to the line curves
+                    pointRadius: Number, // radius of the label tag
+                    pointHitRadius: Number, // on hover:: change of data point radius size
+                    pointHoverRadius: Number // radius of mouse to show the label values when mouse is near a datapoint
+                }>
+            },
+            options: {
+                animation: {
+                    duration: number,
+                    easing: string
+                }
+                responsive: Boolean,
+                maintainAspectRatio: Boolean,
+                plugins: {
+                    title: {
+                        display: Boolean,
+                        text: string,
+                        color: string,
+                        font: {
+                            weight: string,
+                            size: Number
+                        }
+                    },
+                    legend: {
+                        display: true,
+                        labels: {
+                            usePointStyle: boolean,
+                            pointStyle: string
+                        }
+                    }
+                }
+            },
+            ids_to_track_and_find_exp_id: Array<number>
+        }
+    },
+    colors_mapped_to_exp_id: {
+        [key: number]: string
+    }
+}
+
+export interface EvaluationResultPayload {
+    epoch: number,
+    grid_search_id: string,
+    experiment_id: number,
+    metric_scores: Array<{
+        metric: string,
+        split: string,
+        score: number
+    }>,
+    loss_scores: Array<{
+        loss: string,
+        split: string,
+        score: number
+    }>
+}
+
+export default function handleEvaluationResultData(evalResultCustomData: evalResultCustomData, evalResultSocketData: EvaluationResultPayload) {
+    let exp = undefined;
+    if (evalResultCustomData.grid_search_id !== null) {
+        exp = evalResultCustomData.experiments;
+    }
+    else {
+        evalResultCustomData.grid_search_id = evalResultSocketData.grid_search_id;
+        exp = {}
+    }
+
+    if (evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id] === undefined) {
+        let random_color = getRandomColor();
+        evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id] = random_color;
+    }
+
+    for (let i = 0; i < evalResultSocketData.loss_scores.length; i++) {
+        let d = evalResultSocketData.loss_scores[i]
+        if (exp[d.split + "_" + d.loss] === undefined) {
+            exp[d.split + "_" + d.loss] = {
+                data: {
+                    labels: [],
+                    datasets: []
+                },
+                options: {
+                    animation: {
+                        duration: 300,
+                        easing: 'linear'
+                    },
+                    responsive: true,
+                    maintainAspectRatio: false,
+                    plugins: {
+                        title: {
+                            display: true,
+                            text: (d.split + " " + d.loss.split("_").join(" ")).toLowerCase(),
+                            color: 'black',
+                            font: {
+                                weight: 'bold',
+                                size: 20
+                            }
+                        },
+                        legend: {
+                            display: true,
+                            labels: {
+                                usePointStyle: true,
+                                pointStyle: 'circle'
+                            }
+                        }
+                    }
+                },
+                ids_to_track_and_find_exp_id: []
+            }
+        }
+
+        let prevIndex = null;
+        if (exp[d.split + "_" + d.loss].ids_to_track_and_find_exp_id.includes(evalResultSocketData.experiment_id)) {
+            prevIndex = exp[d.split + "_" + d.loss].ids_to_track_and_find_exp_id.indexOf(evalResultSocketData.experiment_id)
+        }
+        else {
+            exp[d.split + "_" + d.loss].ids_to_track_and_find_exp_id.push(evalResultSocketData.experiment_id);
+        }
+
+        if (!exp[d.split + "_" + d.loss].data.labels.includes(evalResultSocketData.epoch)) {
+            exp[d.split + "_" + d.loss].data.labels.push(evalResultSocketData.epoch);
+        }
+
+        if (prevIndex !== null) {
+            exp[d.split + "_" + d.loss].data.datasets[prevIndex].data = [...exp[d.split + "_" + d.loss].data.datasets[prevIndex].data, d.score]
+        }
+        else {
+            exp[d.split + "_" + d.loss].data.datasets.push({
+                exp_id: evalResultSocketData.experiment_id,
+                label: "experiment_" + evalResultSocketData.experiment_id.toString(),
+                data: [d.score],
+                fill: false,
+                backgroundColor: evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id],
+                borderColor: evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id],
+                tension: 0,
+                pointRadius: 3,
+                pointHitRadius: 20,
+                pointHoverRadius: 12
+            });
+        }
+        exp[d.split + "_" + d.loss].data.datasets.sort((a, b) => (a.exp_id > b.exp_id) ? 1 : -1)
+        exp[d.split + "_" + d.loss].ids_to_track_and_find_exp_id.sort((a, b) => (a > b) ? 1 : -1)
+    }
+
+    for (let i = 0; i < evalResultSocketData.metric_scores.length; i++) {
+        let d = evalResultSocketData.metric_scores[i]
+        if (exp[d.split + "_" + d.metric] === undefined) {
+            exp[d.split + "_" + d.metric] = {
+                data: {
+                    labels: [],
+                    datasets: []
+                },
+                options: {
+                    animation: {
+                        duration: 300,
+                        easing: 'linear'
+                    },
+                    responsive: true,
+                    maintainAspectRatio: false,
+                    plugins: {
+                        title: {
+                            display: true,
+                            text: (d.split + " " + d.metric.split("_").join(" ")).toLowerCase(),
+                            color: 'black',
+                            font: {
+                                weight: 'bold',
+                                size: 20
+                            }
+                        },
+                        legend: {
+                           display: true,
+                           labels: {
+                                usePointStyle: true,
+                                pointStyle: 'circle'
+                            }
+                        }
+                    }
+                },
+                ids_to_track_and_find_exp_id: []
+            }
+        }
+
+        let prevIndex = null;
+        if (exp[d.split + "_" + d.metric].ids_to_track_and_find_exp_id.includes(evalResultSocketData.experiment_id)) {
+            prevIndex = exp[d.split + "_" + d.metric].ids_to_track_and_find_exp_id.indexOf(evalResultSocketData.experiment_id)
+        }
+        else {
+            exp[d.split + "_" + d.metric].ids_to_track_and_find_exp_id.push(evalResultSocketData.experiment_id);
+        }
+
+        if (!exp[d.split + "_" + d.metric].data.labels.includes(evalResultSocketData.epoch)) {
+            exp[d.split + "_" + d.metric].data.labels.push(evalResultSocketData.epoch);
+        }
+
+        if (prevIndex !== null) {
+            exp[d.split + "_" + d.metric].data.datasets[prevIndex].data = [...exp[d.split + "_" + d.metric].data.datasets[prevIndex].data, d.score]
+        }
+        else {
+            exp[d.split + "_" + d.metric].data.datasets.push({
+                exp_id: evalResultSocketData.experiment_id,
+                label: "experiment_" + evalResultSocketData.experiment_id.toString(),
+                data: [d.score],
+                fill: false,
+                backgroundColor: evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id],
+                borderColor: evalResultCustomData.colors_mapped_to_exp_id[evalResultSocketData.experiment_id],
+                tension: 0,
+                pointRadius: 3,
+                pointHitRadius: 20,
+                pointHoverRadius: 12
+            });
+        }
+
+        exp[d.split + "_" + d.metric].data.datasets.sort((a, b) => (a.exp_id > b.exp_id) ? 1 : -1)
+        exp[d.split + "_" + d.metric].ids_to_track_and_find_exp_id.sort((a, b) => (a > b) ? 1 : -1)
+    }
+
+    evalResultCustomData.experiments = exp;
+    // console.log("In Handle Exp evalResultCustomData = ",evalResultCustomData);
+
+    return evalResultCustomData;
+
+}
+
+// TODO: should be moved to the statusSlice maybe ?
+export function getRandomColor() {
+    let letters = '0123456789ABCDEF'.split('');
+    let color = '#';
+    for (let i = 0; i < 6; i++) {
+        color += letters[Math.floor(Math.random() * 16)];
+    }
+    return color;
+}
\ No newline at end of file
diff --git a/src/ml_gym/gym/evaluators/accelerate_evaluator.py b/src/ml_gym/gym/evaluators/accelerate_evaluator.py
index f69936a5..143c3586 100644
--- a/src/ml_gym/gym/evaluators/accelerate_evaluator.py
+++ b/src/ml_gym/gym/evaluators/accelerate_evaluator.py
@@ -33,7 +33,7 @@ def evaluate(self, model: NNModel, accelerator: Accelerator, epoch_result_callba
         :returns:
             evaluation_batch_results (List[EvaluationBatchResult]): Evaluation results of batches trained on.
         """
-        model = model.eval()
+        model.eval()
 
         # returns a EvaluationBatchResult for each split
         evaluation_batch_results = self.eval_component.evaluate(model=model, accelerator=accelerator,
diff --git a/src/ml_gym/gym/gym.py b/src/ml_gym/gym/gym.py
index 14dbe33e..f646683e 100644
--- a/src/ml_gym/gym/gym.py
+++ b/src/ml_gym/gym/gym.py
@@ -28,6 +28,7 @@ def __init__(self, logger_collection_constructable: MLgymStatusLoggerCollectionC
         """
         self.job_status_logger = JobStatusLogger(logger_collection_constructable.construct())
 
+
     @abstractmethod
     def run(self, blueprints: List[BluePrint]):
         """
@@ -199,6 +200,7 @@ def _run_standard_gym_job(blueprint: BluePrint, device: torch.device, num_epochs
                                                            logger_collection_constructable=logger_collection_constructable,
                                                            gs_restful_api_client_constructable=gs_restful_api_client_constructable,
                                                            num_batches_per_epoch=num_batches_per_epoch)
+
         return gym_job.execute(device=device)
 
     @staticmethod
diff --git a/src/ml_gym/gym/gym_jobs/accelerate_gym_job.py b/src/ml_gym/gym/gym_jobs/accelerate_gym_job.py
index 1a5c41c2..e9580c23 100644
--- a/src/ml_gym/gym/gym_jobs/accelerate_gym_job.py
+++ b/src/ml_gym/gym/gym_jobs/accelerate_gym_job.py
@@ -71,24 +71,28 @@ def _execute_train(self):
         self.optimizer.register_model_params(model_params=dict(self.model.named_parameters()))
         self.lr_scheduler.register_optimizer(optimizer=self.optimizer)
 
-        self.model, self.optimizer, self.trainer, self.evaluator, self.lr_scheduler, train_loader = self.accelerator.prepare(self.model,
-                                                                                                                             self.optimizer,
-                                                                                                                             self.trainer,
-                                                                                                                             self.evaluator,
-                                                                                                                             self.lr_scheduler,
-                                                                                                                             self.trainer.train_loader)
+        eval_loader_keys, eval_loaders_list = zip(*self.evaluator.eval_component.dataset_loaders.items())
+
+        self.model, self.optimizer, self.lr_scheduler, train_loader, *eval_acc_loaders_list = self.accelerator.prepare(self.model,
+                                                                                                                       self.optimizer,
+                                                                                                                       self.lr_scheduler,
+                                                                                                                       self.trainer.train_loader,
+                                                                                                                       *eval_loaders_list)
+
+        eval_loaders = {k: v for k, v in zip(eval_loader_keys, eval_acc_loaders_list)}
+
         self.trainer.train_loader = DatasetLoaderFactory.get_data_loader_shard_wrapper(data_loader_shard=train_loader,
                                                                                        dataset_name=self.trainer.train_loader.dataset_name,
                                                                                        dataset_tag=self.trainer.train_loader.dataset_tag)
 
         self.evaluator.eval_component.dataset_loaders = {key: DatasetLoaderFactory.get_data_loader_shard_wrapper(
-            data_loader_shard=self.accelerator.prepare(data_loader),
+            data_loader_shard=eval_loaders[key],
             dataset_name=data_loader.dataset_name,
             dataset_tag=data_loader.dataset_tag) for key, data_loader in self.evaluator.eval_component.dataset_loaders.items()}
 
         partial_batch_done_callback = partial(self.batch_processed_callback, experiment_status_logger=self._experiment_status_logger)
         def evaluation_step_routine(current_epoch: int): return self._evaluation_step(current_epoch=current_epoch)
-        partial_train_epoch_done_callback = partial(self.train_epoch_done_callback, evaluation_step_routine=evaluation_step_routine, 
+        partial_train_epoch_done_callback = partial(self.train_epoch_done_callback, evaluation_step_routine=evaluation_step_routine,
                                                     accelerator=self.accelerator)
 
         model = self.trainer.train(num_epochs=self.num_epochs, model=self.model, optimizer=self.optimizer,
diff --git a/src/ml_gym/gym/gym_jobs/gym_job.py b/src/ml_gym/gym/gym_jobs/gym_job.py
index fc9b9289..3ffbe6eb 100644
--- a/src/ml_gym/gym/gym_jobs/gym_job.py
+++ b/src/ml_gym/gym/gym_jobs/gym_job.py
@@ -1,5 +1,8 @@
 from abc import abstractmethod
+import os
+import pickle
 from typing import Callable, List
+from ml_board.backend.restful_api.data_models import CheckpointResource
 from ml_gym.batching.batch import EvaluationBatchResult
 from ml_gym.checkpointing.checkpointing import CheckpointingIF, CheckpointingInstruction
 from ml_gym.early_stopping.early_stopping_strategies import EarlyStoppingIF
@@ -16,6 +19,8 @@
 from ml_gym.persistency.logging import ExperimentStatusLogger
 import torch
 from accelerate import Accelerator
+import shutil
+import tempfile
 
 
 class AbstractGymJob(StatefulComponent):
@@ -63,7 +68,7 @@ def __init__(self, experiment_status_logger: ExperimentStatusLogger, gs_api_clie
     def execute(self, device: torch.device):
         raise NotImplementedError
 
-    def run_checkpointing(self, checkpoint_instruction: CheckpointingInstruction, current_epoch: int):
+    def run_checkpointing(self, checkpoint_instruction: CheckpointingInstruction, current_epoch: int, accelerator: Accelerator = None):
         """
         Create and delete checkpoints for each epoch in experiments.
 
@@ -71,22 +76,38 @@ def run_checkpointing(self, checkpoint_instruction: CheckpointingInstruction, cu
            - checkpoint_instruction (CheckpointingInstruction): CheckpointingInstruction object.
            - current_epoch (int): Current epoch number for cerating checkpoints.
         """
-
         if checkpoint_instruction.save_current:
-            payload_dict = {
-                "epoch": current_epoch,
-                "model": self.model.state_dict(),
-                "optimizer": self.optimizer.state_dict(),
-                "lr_scheduler": self.lr_scheduler.state_dict(),
-                "stateful_components": self.get_state(),
-            }
 
-            self.gs_api_client.add_checkpoint_resource(
-                grid_search_id=self.grid_search_id, experiment_id=self.experiment_id, payload=payload_dict
-            )
+            if accelerator is not None:
+                global_rank = accelerator.process_index
+                # TODO replace with an in-memory file system solution
+                with tempfile.TemporaryDirectory() as tmpdirname:
+                    root_dir = os.path.join(tmpdirname, f"epoch_{current_epoch}/rank_{global_rank}")
+                    checkpoint_file_name = f"checkpoint_rank_{global_rank}"
+                    checkpoint_path = os.path.join(root_dir, f"rank_{global_rank}/")
+                    accelerator.save_state(output_dir=checkpoint_path)
+                    shutil.make_archive(base_name=os.path.join(root_dir, checkpoint_file_name), format='zip', root_dir=checkpoint_path)
+                    with open(os.path.join(root_dir, f"{checkpoint_file_name}.zip"), 'rb') as fd:
+                        self.gs_api_client.add_checkpoint_resource(grid_search_id=self.grid_search_id, experiment_id=self.experiment_id,
+                                                                   epoch=current_epoch, payload_stream=fd,
+                                                                   custom_file_name=f"{checkpoint_file_name}.zip")
+
+            else:
+                payload_dict = {
+                    CheckpointResource.model: pickle.dumps(self.model.state_dict()),
+                    CheckpointResource.optimizer: pickle.dumps(self.optimizer.state_dict()),
+                    CheckpointResource.lr_scheduler: pickle.dumps(self.lr_scheduler.state_dict()),
+                    CheckpointResource.stateful_components: pickle.dumps(self.get_state())
+                }
+
+                for checkpoint_resource_key, checkpoint_resource_stream in payload_dict.items():
+                    self.gs_api_client.add_checkpoint_resource(grid_search_id=self.grid_search_id, experiment_id=self.experiment_id,
+                                                               epoch=current_epoch, payload_stream=checkpoint_resource_stream,
+                                                               custom_file_name=f"{checkpoint_resource_key}.pickle")
 
-        for epoch in checkpoint_instruction.checkpoints_to_delete:
-            self.gs_api_client.delete_checkpoints(grid_search_id=self.grid_search_id, experiment_id=self.experiment_id, epoch=epoch)
+        if accelerator is None or accelerator is not None and accelerator.is_main_process:
+            for epoch in checkpoint_instruction.checkpoints_to_delete:
+                self.gs_api_client.delete_checkpoints(grid_search_id=self.grid_search_id, experiment_id=self.experiment_id, epoch=epoch)
 
     @staticmethod
     def batch_processed_callback(status: str, experiment_status_logger: ExperimentStatusLogger, num_batches: int,
@@ -141,11 +162,11 @@ def train_epoch_done_callback(self, num_epochs: int, current_epoch: int, model:
         evaluation_results = evaluation_step_routine(current_epoch=current_epoch)
         if current_epoch > 0:
             self.lr_scheduler.step()
-        if accelerator is None or accelerator is not None and accelerator.is_main_process:
-            checkpointing_instruction = self.checkpointing_strategy.get_model_checkpoint_instruction(num_epochs=num_epochs,
-                                                                                                     current_epoch=current_epoch,
-                                                                                                     evaluation_result=evaluation_results)
-            self.run_checkpointing(checkpointing_instruction, current_epoch=current_epoch)
+        checkpointing_instruction = self.checkpointing_strategy.get_model_checkpoint_instruction(num_epochs=num_epochs,
+                                                                                                 current_epoch=current_epoch,
+                                                                                                 evaluation_result=evaluation_results)
+        self.run_checkpointing(checkpointing_instruction, current_epoch=current_epoch, accelerator=accelerator)
+
         if self.early_stopping_strategy.is_stopping_criterion_fulfilled(current_epoch=current_epoch,
                                                                         evaluation_results=evaluation_results):
             raise EarlyStoppingCriterionFulfilledError
diff --git a/src/ml_gym/gym/trainers/accelerate_trainer.py b/src/ml_gym/gym/trainers/accelerate_trainer.py
index e941ce67..f7aa0d51 100644
--- a/src/ml_gym/gym/trainers/accelerate_trainer.py
+++ b/src/ml_gym/gym/trainers/accelerate_trainer.py
@@ -80,6 +80,8 @@ def train(self, model: NNModel, optimizer: OptimizerAdapter, dataloader: Dataset
             model (NNModel): Torch Neural Network module.
         """
 
+        model.train()
+
         if num_batches_per_epoch is None:
             num_batches_per_epoch = len(dataloader)
 
@@ -100,7 +102,7 @@ def train(self, model: NNModel, optimizer: OptimizerAdapter, dataloader: Dataset
                                         current_epoch=current_epoch)
             if (batch_id + 1) % num_batches_per_epoch == 0:  # when epoch done
                 epoch_done_callback_fun(num_epochs=num_epochs, current_epoch=current_epoch, model=model, accelerator=accelerator)
-
+                model.train()
         return model
 
     def calc_loss(self, model: NNModel, batch: DatasetBatch) -> torch.Tensor:
@@ -146,8 +148,6 @@ def train(self, num_epochs: int, model: NNModel, optimizer: OptimizerAdapter,
             model (NNModel): Torch Neural Network module.
         """
 
-        model = model.train()
-
         # accelerate_train_loader = accelerator.prepare(self.train_loader)
 
         model = self.train_component.train(model=model, optimizer=optimizer, dataloader=self.train_loader, accelerator=accelerator,
diff --git a/src/ml_gym/gym/trainers/standard_trainer.py b/src/ml_gym/gym/trainers/standard_trainer.py
index 8d42529e..4f57b2d4 100644
--- a/src/ml_gym/gym/trainers/standard_trainer.py
+++ b/src/ml_gym/gym/trainers/standard_trainer.py
@@ -60,6 +60,7 @@ def train(self, model: NNModel, optimizer: OptimizerAdapter, dataloader: Dataset
         :returns:
             model (NNModel): Torch Neural Network module.
         """
+        model.train()
 
         if num_batches_per_epoch is None:
             num_batches_per_epoch = len(dataloader)
@@ -83,6 +84,7 @@ def train(self, model: NNModel, optimizer: OptimizerAdapter, dataloader: Dataset
 
             if (batch_id + 1) % num_batches_per_epoch == 0:  # when epoch done
                 epoch_done_callback_fun(num_epochs=num_epochs, current_epoch=current_epoch, model=model)
+                model.train()
 
         return model
 
@@ -129,7 +131,6 @@ def train(self, num_epochs: int, model: NNModel, optimizer: OptimizerAdapter, de
             model (NNModel): Torch Neural Network module.
         """
 
-        model = model.train()
 
         model = self.train_component.train(model=model, optimizer=optimizer, dataloader=self.train_loader, device=device,
                                            batch_done_callback_fun=batch_done_callback_fun, epoch_done_callback_fun=epoch_done_callback,
diff --git a/src/ml_gym/persistency/io.py b/src/ml_gym/persistency/io.py
index 2f96f87a..9985caa9 100644
--- a/src/ml_gym/persistency/io.py
+++ b/src/ml_gym/persistency/io.py
@@ -1,8 +1,6 @@
 from abc import abstractmethod
-import base64
 from dataclasses import dataclass
 from enum import Enum
-import pickle
 from typing import Dict, List
 import requests
 from http import HTTPStatus
@@ -27,10 +25,10 @@ def add_config_string(self, grid_search_id: str, config_name: str, config: Dict,
     def get_validation_config(self, grid_search_id: str):
         raise NotImplementedError
 
-    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, checkpoint_id: int, checkpoint_resource: CheckpointResource):
+    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, checkpoint_id: int, checkpoint_resource: str):
         raise NotImplementedError
 
-    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, payload: Dict):
+    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, payload: Dict, custom_file_name: str):
         raise NotImplementedError
 
     def delete_checkpoints(self, grid_search_id: str, experiment_id: str, epoch: int):
@@ -90,18 +88,17 @@ def _get_binary_resource(url) -> bytes:
         else:
             return response.content
 
-    def _post_binary_resource(url: str, payload: bytes) -> Dict:
+    def _post_binary_resource(url: str, file_name: str, payload_stream: bytes) -> Dict:
         """
         ``HTTP POST Call`` Send bytes data.
 
         :params:
              url (str): HTTP request URL
+             file_name (str): Name of the file to be posted
              payload (bytes); pickle dump to be sent
         """
-        data = base64.b64encode(payload)
-        response = requests.post(
-            url=url, data={"data": data, "msg": "insert checkpoint", "type": "multipart/form-data"}, files={"file": data}
-        )
+        response = requests.post(url=url, files={"checkpoint_file": (file_name, payload_stream)})
+
         if response.status_code != HTTPStatus.OK:
             raise NetworkError(f"Server responded with error code {response.status_code}")
 
@@ -113,7 +110,9 @@ def _del_binary_resource(url: str) -> Dict:
              url (str): HTTP request URL
         """
         response = requests.delete(url=url)
-        if response.status_code != HTTPStatus.OK:
+        if response.status_code == HTTPStatus.NOT_FOUND:
+            print(f"Resource {url} not found")
+        elif response.status_code != HTTPStatus.OK:
             raise NetworkError(f"Server responded with error code {response.status_code}")
 
     def _put_raw_text_file_resource(url: str, payload: Dict) -> Dict:
@@ -145,9 +144,8 @@ def get_config(self, grid_search_id: str, config_name: str) -> str:
     #     url = f"{self.endpoint}/grid_searches/{grid_search_id}/gs_config"
     #     return GridSearchRestfulAPIClient._put_raw_text_file_resource(url, grid_search_config)
 
-    def add_config_string(
-        self, grid_search_id: str, config_name: str, config: str, file_format: FileFormat, experiment_id: int = None
-    ) -> Dict:
+    def add_config_string(self, grid_search_id: str, config_name: str, config: str, file_format: FileFormat,
+                          experiment_id: int = None) -> Dict:
         """
         ``HTTP PUT Call Request`` Add configuration
           given the grid search ID over HTTP call.
@@ -207,7 +205,7 @@ def get_full_checkpoint(self, grid_search_id: str, experiment_id: str, checkpoin
         url = f"{self.endpoint}/checkpoints/{grid_search_id}/{experiment_id}/{checkpoint_id}"
         return GridSearchRestfulAPIClient._get_json_resource(url)
 
-    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, checkpoint_id: int, checkpoint_resource: CheckpointResource):
+    def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, checkpoint_id: int, checkpoint_resource: str):
         """
         ``HTTP GET Call Request`` Fetch checkpoint resource
           given the grid search ID, experiment ID & checkpoint ID over HTTP call.
@@ -216,14 +214,15 @@ def get_checkpoint_resource(self, grid_search_id: str, experiment_id: str, check
              grid_search_id (str): Grid Search ID
              experiment_id (str): Experiment ID
              checkpoint_id (int): Checkpoint ID
-             checkpoint_resource (CheckpointResource) : CheckpointResource type
+             checkpoint_resource (str): Filename of the checkpoint resource
 
         :returns: bytes pickel data
         """
         url = f"{self.endpoint}/checkpoints/{grid_search_id}/{experiment_id}/{checkpoint_id}/{checkpoint_resource}"
         return GridSearchRestfulAPIClient._get_binary_resource(url)
 
-    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, payload: Dict):
+    def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, epoch: int,
+                                payload_stream: bytes, custom_file_name: str):
         """
         ``HTTP POST Call Request`` Send all checkpoint resource pickle files
           given the epoch, experiment ID & grid search ID over HTTP call.
@@ -231,20 +230,13 @@ def add_checkpoint_resource(self, grid_search_id: str, experiment_id: str, paylo
         :params:
              grid_search_id (str): Grid Search ID
              experiment_id (str): Experiment ID
-             payload (Dict): Dictionary containing CheckpointResource bytes array
+             epoch (int): Current epoch
+             payload_stream (bytes): Bytes stream containing CheckpointResource 
+             custom_file_name (str): File name of the checkpoint resource
         """
 
-        chekpoint_resources = [
-            CheckpointResource.model,
-            CheckpointResource.lr_scheduler,
-            CheckpointResource.optimizer,
-            CheckpointResource.stateful_components,
-        ]
-
-        for checkpoint in chekpoint_resources:
-            url = f"{self.endpoint}/checkpoints/{grid_search_id}/{experiment_id}/{payload['epoch']}/{checkpoint}"
-            payload_pick = pickle.dumps(payload[f"{checkpoint}"])
-            GridSearchRestfulAPIClient._post_binary_resource(url, payload_pick)
+        url = f"{self.endpoint}/checkpoints/{grid_search_id}/{experiment_id}/{epoch}"
+        GridSearchRestfulAPIClient._post_binary_resource(url, file_name=custom_file_name, payload_stream=payload_stream)
 
     def delete_checkpoints(self, grid_search_id: str, experiment_id: str, epoch: int):
         """
@@ -266,8 +258,6 @@ def get_experiment_statuses(self, grid_search_id: str) -> List[ExperimentStatus]
 
         :params:
              grid_search_id (str): Grid Search ID
-             experiment_id (str): Experiment ID
-             epoch (int): Epoch
 
         :returns: experiment status List
         """
diff --git a/src/ml_gym/persistency/logging.py b/src/ml_gym/persistency/logging.py
index d4951719..a76c3da6 100644
--- a/src/ml_gym/persistency/logging.py
+++ b/src/ml_gym/persistency/logging.py
@@ -6,7 +6,7 @@
 from ml_gym.io.websocket_client import ClientFactory, BufferedClient
 import time
 import torch
-import pickle
+import io
 import math
 
 
@@ -192,7 +192,7 @@ def log_evaluation_results(self, eval_result: "EvaluationBatchResult", epoch: in
         payload["loss_scores"] = loss_scores
         message["payload"] = payload
         self._logger.log_raw_message(raw_log_message=message)
-
+        
     def disconnect(self):
         self._logger.disconnect()
 
diff --git a/src/ml_gym/util/devices.py b/src/ml_gym/util/devices.py
index feb089e6..7d22a434 100644
--- a/src/ml_gym/util/devices.py
+++ b/src/ml_gym/util/devices.py
@@ -6,7 +6,7 @@
 
 
 def get_devices(gpu_device_ids: List[int] = None) -> List[torch.device]:
-    if gpu_device_ids is None:
+    if gpu_device_ids is None or not gpu_device_ids:
         print("WARNING: No cuda devices specified. Falling back to CPUs.")   # TODO Introduce proper logging...
         return [torch.device("cpu")]
     if torch.cuda.is_available():  # if we got some GPUs
@@ -14,7 +14,3 @@ def get_devices(gpu_device_ids: List[int] = None) -> List[torch.device]:
     else:
         print("WARNING: No cuda devices available. Falling back to CPUs.")   # TODO Introduce proper logging...
         return [torch.device("cpu")]
-
-
-if __name__ == "__main__":
-    print(get_devices())
diff --git a/src/ml_gym/util/util.py b/src/ml_gym/util/util.py
index dc965f74..65f4c3eb 100644
--- a/src/ml_gym/util/util.py
+++ b/src/ml_gym/util/util.py
@@ -40,12 +40,11 @@ def create_system_info() -> Dict:
             info["processor"] = platform.processor()
             info["ram"] = str(round(psutil.virtual_memory().total / (1024.0**3))) + " GB"
             info["python-version"] = platform.python_version()
-            info["python-packages"] = [p.project_name for p in pkg_resources.working_set]
+            info["python-packages"] = [{"name":p.project_name, "version": p.version} for p in pkg_resources.working_set]
             if torch.cuda.is_available():
                 info["CUDNN_version"] = torch.backends.cudnn.version()
                 info["num_cuda_device"] = torch.cuda.device_count()
                 dev_list = []
-                infor = torch.cuda.get_device_properties(0)
                 for i in range(torch.cuda.device_count()):
                     dev_list.append(
                         {
diff --git a/src/setup.py b/src/setup.py
index 9bb7698a..99d4f5b2 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -35,7 +35,8 @@
         "flask-socketio",
         "datasets",
         "transformers",
-        "accelerate"
+        "accelerate",
+        "fs"
     ],
     python_requires=">=3.7",
     include_package_data=True,


Test patch:
diff --git a/example/cnn_zero_stage_3/ds_test.py b/example/cnn_zero_stage_3/ds_test.py
new file mode 100644
index 00000000..8d0e687b
--- /dev/null
+++ b/example/cnn_zero_stage_3/ds_test.py
@@ -0,0 +1,11 @@
+from accelerate import Accelerator
+from accelerate.state import AcceleratorState
+
+
+def main():
+    accelerator = Accelerator()
+    accelerator.print(f"{AcceleratorState()}")
+
+
+if __name__ == "__main__":
+    main()