diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7769df441a6ad701ee8d885ef6ea3bf107877962..07f7e28258ed40bb22033a0be5cda763a140f986 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -42,6 +42,7 @@ variables:
     CI_DEBUG_TRACE: "false"
     TEAM_ID: "df"
     DF_API_PREFIX: "dfapi"
+    DISPLAY_MESSAGE: "false"
   script:
     - >
       helm upgrade --install $CI_PROJECT_NAME-$CI_ENVIRONMENT_NAME ./deploy/ --namespace=${KUBE_NAMESPACE}
@@ -69,6 +70,7 @@ variables:
       --set nuxt.image.repository="$CI_REGISTRY_IMAGE/$NUXT_IMG_NAME"
       --set nuxt.image.tag="$CI_COMMIT_SHORT_SHA"
       --set nuxt.dfApiPrefix="/${DF_API_PREFIX}"
+      --set nuxt.displayMessage="${DISPLAY_MESSAGE}"
       --set env="$ENV"
       --values deploy/values.yaml
       --values deploy/values.${ENV:-development}.yaml
@@ -141,6 +143,7 @@ deploy:dev:
     KUBE_NAMESPACE: "defense-finder-dev"
     PUBLIC_URL: "defense-finder.dev.pasteur.cloud"
     ENV: "development"
+    DISPLAY_MESSAGE: "true"
     CI_DEBUG_TRACE: "true"
   environment:
     name: k8sdev-01
@@ -160,6 +163,8 @@ deploy:prod:
     PUBLIC_URL: "defensefinder.mdmlab.fr"
     ENV: "production"
     CI_DEBUG_TRACE: "false"
+    DISPLAY_MESSAGE: "true"
+
   environment:
     name: k8sprod-02
     url: "https://defense-finder.pasteur.cloud"
diff --git a/backend/analysis/migrations/0009_analysis_from_nt.py b/backend/analysis/migrations/0009_analysis_from_nt.py
new file mode 100644
index 0000000000000000000000000000000000000000..dad4e252d57dfa9cbbe6c303670bdc322e51e3a2
--- /dev/null
+++ b/backend/analysis/migrations/0009_analysis_from_nt.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.3 on 2024-06-28 08:09
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('analysis', '0008_analysis_stderr'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='analysis',
+            name='from_nt',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/backend/analysis/migrations/0010_analysis_input_name_alter_analysis_name.py b/backend/analysis/migrations/0010_analysis_input_name_alter_analysis_name.py
new file mode 100644
index 0000000000000000000000000000000000000000..a765467b77f9abbd4b1a8ab8027f963a60d9c653
--- /dev/null
+++ b/backend/analysis/migrations/0010_analysis_input_name_alter_analysis_name.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.2.3 on 2024-07-01 13:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('analysis', '0009_analysis_from_nt'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='analysis',
+            name='input_name',
+            field=models.CharField(default='to_change', max_length=256),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='analysis',
+            name='name',
+            field=models.CharField(max_length=256),
+        ),
+    ]
diff --git a/backend/analysis/migrations/0011_set_input_name.py b/backend/analysis/migrations/0011_set_input_name.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b0bed73ef2d468baa9a3811375b328df7c2249a
--- /dev/null
+++ b/backend/analysis/migrations/0011_set_input_name.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.3 on 2024-07-01 13:37
+
+from django.db import migrations, models
+
+
+def copy_field(apps, schema):
+    analysis = apps.get_model("analysis", "analysis")
+    for analys in analysis.objects.all():
+        analys.input_name = analys.name
+        analys.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("analysis", "0010_analysis_input_name_alter_analysis_name"),
+    ]
+
+    operations = [
+        migrations.RunPython(copy_field),
+    ]
diff --git a/backend/analysis/models.py b/backend/analysis/models.py
index 3793948d213a390958c31a64f926e33ea4aaf1c2..a3c831b6a38b304c271aa0b13d56fccbdca04692 100644
--- a/backend/analysis/models.py
+++ b/backend/analysis/models.py
@@ -82,6 +82,7 @@ class AnalysisWorkflow(Workflow):
         try:
             analysis = Analysis(
                 name=history_name,
+                input_name=history_name,
                 galaxy_id=galaxy_inv.id,
                 galaxy_state=galaxy_inv.state,
                 analysis_workflow=self,
@@ -113,21 +114,25 @@ class AnalysisWorkflow(Workflow):
 
         datamap = dict()
         for i, file_path in enumerate(input_files):
-            upload_response = history.upload_file(file_path)
+            upload_response = history.upload_file(file_path, file_name=history_name)
             datamap[i] = {"id": upload_response.id, "src": "hda"}
             self.analysis_owner.obj_gi.gi.histories.update_dataset(
-                history.galaxy_id, upload_response.id, tags=["sequences"]
+                history.galaxy_id,
+                upload_response.id,
+                tags=["sequences"],
             )
         return history, datamap, params
 
 
 class Analysis(Invocation):
-    name = models.CharField(max_length=50)
+    name = models.CharField(max_length=256)
+    input_name = models.CharField(max_length=256)
     analysis_workflow = models.ForeignKey(AnalysisWorkflow, on_delete=models.CASCADE)
     analysis_history = models.ForeignKey(AnalysisHistory, on_delete=models.CASCADE)
     params = models.JSONField()
     datamap = models.JSONField()
     stderr = models.TextField(blank=True)
+    from_nt = models.BooleanField(default=False)
 
     class Meta:
         ordering = ["-create_time"]
@@ -281,7 +286,8 @@ class Analysis(Invocation):
                 self.save()
 
     def read_fasta_file(self, file_path, isFromNt=False):
-
+        self.from_nt = isFromNt
+        self.save()
         # if is from Nt, need to sum prot length.
         # In order to get proteins that belongs to same contig
         # just remove (_\d+) to the id
@@ -289,24 +295,24 @@ class Analysis(Invocation):
         sequences = []
         if file_path is not None:
             with open(file_path) as handle:
-                current_contig = None
-                offset = 0
-                last_prot_end = 0
+                # current_contig = None
+                # offset = 0
+                # last_prot_end = 0
                 for record in SeqIO.parse(handle, "fasta"):
 
                     prot = {"id": record.id, "length": len(record), "strand": None}
                     # print(len(record))
-                    if isFromNt:
-                        contig = "-".join(prot["id"].split("_")[0:-1])
-                        if current_contig is None or contig != current_contig:
-                            # print(contig)
-
-                            current_contig = contig
-                            if current_contig is not None:
-                                new_offset = int(last_prot_end)
-                                offset = new_offset
-                                if last_prot_end > 99999999:
-                                    return sequences
+                    # if isFromNt:
+                    #     contig = "-".join(prot["id"].split("_")[0:-1])
+                    #     if current_contig is None or contig != current_contig:
+                    #         # print(contig)
+
+                    #         current_contig = contig
+                    #         if current_contig is not None:
+                    #             # new_offset = int(last_prot_end)
+                    #             # offset = new_offset
+                    #             if last_prot_end > 99999999:
+                    #                 return sequences
 
                     description_list = record.description.split(" # ")
 
@@ -319,14 +325,14 @@ class Analysis(Invocation):
                             prot["strand"] = strand
                         else:
                             strand = None
-                        if isFromNt:
-                            prot["start"] = offset + start
-                            prot["end"] = offset + end
-                            last_prot_end = int(prot["end"])
-
-                        else:
-                            prot["start"] = start
-                            prot["end"] = end
+                        # if isFromNt:
+                        #     prot["start"] = offset + start
+                        #     prot["end"] = offset + end
+                        #     last_prot_end = int(prot["end"])
+
+                        # else:
+                        prot["start"] = start
+                        prot["end"] = end
                     sequences.append(prot)
         return sequences
 
diff --git a/deploy/charts/nuxt/templates/deployment.yaml b/deploy/charts/nuxt/templates/deployment.yaml
index 38819ab1b156572aa4d92a0df66981b5c272dbc1..c94c04465144711c2dc5c766deb5090ef001b2a3 100644
--- a/deploy/charts/nuxt/templates/deployment.yaml
+++ b/deploy/charts/nuxt/templates/deployment.yaml
@@ -46,6 +46,8 @@ spec:
               value: {{ .Values.dfApiPrefix }}
             - name: NUXT_PUBLIC_DF_API_PREFIX
               value: {{ .Values.dfApiPrefix }}
+            - name: NUXT_PUBLIC_DISPLAY_MESSAGE
+              value: {{ .Values.displayMessage | quote}}
           livenessProbe:
             httpGet:
               path: /api/probe
diff --git a/deploy/charts/nuxt/values.yaml b/deploy/charts/nuxt/values.yaml
index c9726c2525d41957764dd8beb7d65328077d4d14..9e24642211e4661ce365212a7c779f5b5cda4673 100644
--- a/deploy/charts/nuxt/values.yaml
+++ b/deploy/charts/nuxt/values.yaml
@@ -85,4 +85,5 @@ tolerations: []
 affinity: {}
 
 dfApiPrefix: "/dfapi"
-backendHostFromServer: ""
\ No newline at end of file
+backendHostFromServer: ""
+displayMessage: "false"
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index a977aef72a61581f246027a8774be1d77f769da6..c9a566ba609b327c282146071d1b3471d30fb6f6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -51,6 +51,7 @@ services:
       NUXT_SERVER_SIDE_API_BASE_URL: "http://defense-finder-api:8000/"
       NUXT_DF_API_PREFIX: "/dfapi"
       NUXT_PUBLIC_DF_API_PREFIX: "/dfapi"
+      NUXT_PUBLIC_DISPLAY_MESSAGE: "true"
 
     labels:
       - "traefik.enable=true"
diff --git a/frontend/components/AnalysisList.vue b/frontend/components/AnalysisList.vue
index ed529ac364a00dd182ad07fdfd36f7af078475b4..3149eb6da998362193fe386bfdb3606173f7daab 100644
--- a/frontend/components/AnalysisList.vue
+++ b/frontend/components/AnalysisList.vue
@@ -68,7 +68,7 @@ async function goToAnalysis(analysisId: number) {
   const analysisPendingsVal = toValue(analysisPendings)
   try {
     analysisPendingsVal[analysisId] = true
-    await navigateTo(`/analyses/${analysisId}/systems`)
+    await navigateTo(`/analyses/${analysisId}`)
   } catch (error) {
     throw createError("Unable to get analysis data")
   } 
diff --git a/frontend/components/AnalysisResultDataTable.vue b/frontend/components/AnalysisResultDataTable.vue
index ca92d327e4cccfc7f2d88c841390c4043d9ef745..423921ae0602bd1dd304fd077ffec303b3277dc2 100644
--- a/frontend/components/AnalysisResultDataTable.vue
+++ b/frontend/components/AnalysisResultDataTable.vue
@@ -5,6 +5,9 @@ import CollapsibleChips from './CollapsibleChips.vue';
 interface Props {
     items: any[]
     headers: Object[]
+    contig: string
+    groupBy?: { key: string, order: string }[] | boolean
+
 }
 
 const props = defineProps<Props>()
@@ -31,31 +34,76 @@ function namesToCollapsibleChips(names: string[]) {
 }
 
 
+const dataTableProps = computed(() => {
+    let tableProps: {
+        headers: Object[]
+        items: any[]
+        'item-value': string
+        search: string
+        'group-by'?: { key: string, order: string }[] | boolean
+    } = {
+        headers: toValue(props.headers),
+        items: toValue(computedItems),
+        "item-value": "name",
+        search: toValue(search)
+    }
+    if (props.groupBy && typeof props.groupBy !== 'boolean') {
+        tableProps = { ...tableProps, 'group-by': props.groupBy }
+    }
+    return tableProps
+
+})
+
+
 </script>
 <template>
-    <v-card v-if="items" class="mt-5" flat>
+    <v-card v-if="items" flat color="transparent">
         <v-card-title>
             <v-text-field v-model="search" append-icon="mdi-magnify" label="Search" single-line
                 hide-details></v-text-field></v-card-title>
         <v-card-text>
-            <v-data-table v-model:items-per-page="itemsPerPage" :headers="headers" :items="computedItems" item-value="name"
-                :search="search">
+            <v-data-table v-model:items-per-page="itemsPerPage" v-bind="dataTableProps">
                 <template #[`item.type`]="{ item }">
-                    <v-chip :href="item?.href ? item.href : undefined" target="_blank" color="info">
+                    <v-chip :href="item?.href ? item.href : undefined" target="_blank">
                         {{ item.type }}
                     </v-chip>
                 </template>
                 <template #[`item.hit_id`]="{ item }">
-                    <v-chip color="info" @click="setSelectedProtein(item.hit_id)">
+                    <v-chip color="info" @click="setSelectedProtein(item.hit_id, item.replicon)">
                         {{ item.hit_id }}
                     </v-chip>
                 </template>
-
+                <template #[`item.sys_id`]="{ item }">
+                    <v-chip color="info" @click="setSelectedProtein(item.protein_in_syst[0], item.replicon)">
+                        {{ item.sys_id }}
+                    </v-chip>
+                </template>
                 <template #[`item.protein_in_syst`]="{ item }">
-                    <CollapsibleChips :items="namesToCollapsibleChips(item.protein_in_syst)">
+                    <CollapsibleChips :contig="item.replicon" :items="namesToCollapsibleChips(item.protein_in_syst)">
                     </CollapsibleChips>
                 </template>
 
+                <template #header.data-table-group>
+                    <div>Replicon/contig</div>
+                </template>
+
+
+                <!-- workaround to make it expanded default (NOT WORKING) -->
+                <!-- <template #group-header="{ item, columns, toggleGroup, isGroupOpen }">
+                    <template :ref="(el) => {
+                        if (!isGroupOpen(item))
+                            toggleGroup(item);
+                    }"></template>
+                    <tr>
+                        <td :colspan="columns.length">
+                            <v-btn :icon="isGroupOpen(item) ? '$expand' : '$next'" size="small" variant="text"
+                                @click="toggleGroup(item)" />
+                            {{ item.value}} ({{
+                                item.items.length }})
+                        </td>
+                    </tr>
+                </template> -->
+
             </v-data-table>
 
 
diff --git a/frontend/components/CollapsibleChips.vue b/frontend/components/CollapsibleChips.vue
index bc2c95c55320d73c1cc7bc07808506b926562900..9f4053b9c710a66f35954becbfb66cbaa9915cad 100644
--- a/frontend/components/CollapsibleChips.vue
+++ b/frontend/components/CollapsibleChips.vue
@@ -4,16 +4,20 @@
 interface item {
     title: string;
     href?: string | undefined
+
 }
 
 export interface Props {
     items: item[];
     itemsToDisplay?: number;
+    contig?: string | undefined
 }
 
+
 const props = withDefaults(defineProps<Props>(), {
     items: () => [],
     itemsToDisplay: 1,
+    contig: undefined
 
 });
 
@@ -29,17 +33,20 @@ const show = ref(false);
     <span v-if="show" class="d-flex flex-wrap align-center justify-start">
         <template v-if="items.length > itemsToDisplay">
             <template v-for="item in items" :key="item.title">
-                <v-chip color="info" class="mr-1 my-1 align-self-center" size="small" @click="setSelectedProtein(item.title)">
+                <v-chip color="info" class="mr-1 my-1 align-self-center" size="small"
+                    @click="setSelectedProtein(item.title, props.contig)">
                     {{ item.title }}
                 </v-chip>
             </template>
         </template>
-        <v-btn v-if="itemsToDisplay < items.length" variant="text" :icon="'mdi-chevron-up'" @click="show = !show"></v-btn>
+        <v-btn v-if="itemsToDisplay < items.length" variant="text" :icon="'mdi-chevron-up'"
+            @click="show = !show"></v-btn>
     </span>
     <span v-else class="d-flex flex-wrap align-center justify-start">
         <template v-for="(item, index) in items" :key="item.title">
-            <v-chip v-if="index < itemsToDisplay || itemsToDisplay < 0 || items.length - itemsToDisplay === 1" color="info"
-                class="mr-1 my-1 align-self-center" size="small" @click="setSelectedProtein(item.title)">
+            <v-chip v-if="index < itemsToDisplay || itemsToDisplay < 0 || items.length - itemsToDisplay === 1"
+                color="info" class="mr-1 my-1 align-self-center" size="small"
+                @click="console.log(props.contig); setSelectedProtein(item.title, props.contig)">
                 {{ item.title }}
             </v-chip>
             <template v-if="index === itemsToDisplay && items.length - itemsToDisplay > 1">
diff --git a/frontend/components/Main.vue b/frontend/components/Main.vue
index 7664303fed1bccbe510be6b3844aa28feefb1c4e..439555df1dcbd58ae1e58d0579ac6ffb3f5d7470 100644
--- a/frontend/components/Main.vue
+++ b/frontend/components/Main.vue
@@ -42,6 +42,10 @@ function onScroll() {
             <v-col cols="auto">
               <v-card flat color="transparent" :min-width="computedMinWidth"
                 :max-width="props.fluid ? undefined : maxWidth">
+                <v-alert v-if="runtimeConfig.public.displayMessage" type="warning" variant="tonal" border="start" prominent>
+                  Following an update on July 3, 2024, some analyses conducted before this date may display unusual
+                  results. If this occurs, simply rerun the analysis to resolve the issue
+                </v-alert>
                 <slot />
               </v-card>
             </v-col>
diff --git a/frontend/components/SequenceBrowser.vue b/frontend/components/SequenceBrowser.vue
new file mode 100644
index 0000000000000000000000000000000000000000..bf85dc98915ab8eb68c2ca4a0d7d066f25f1f61b
--- /dev/null
+++ b/frontend/components/SequenceBrowser.vue
@@ -0,0 +1,372 @@
+<script setup lang="ts">
+import * as d3 from "d3";
+
+import { useElementSize, useThrottleFn } from '@vueuse/core'
+import type { Protein } from "~/types";
+
+interface Props {
+    sequences: { name: string, defenseSystemHits: string, hmmerHits: string, proteins: Protein[] },
+    fromNt: boolean
+    analysisName: string
+    canClose: boolean
+}
+const props = defineProps<Props>()
+
+const height = ref(300)
+const margin = ref({
+    marginLeft: 10,
+    marginRight: 10,
+    marginBottom: 10,
+    marginTop: 0,
+})
+
+const { selectedProtein } = useSelectedProtein()
+const { serialize } = useSerialize()
+const { rasterize } = useRasterize()
+const { download } = useDownloadBlob()
+const proteinIndex = computed(() => {
+    return new Map(props.sequences.proteins.map((d, i, arr) => {
+        return [d.id, { ...d }]
+    }))
+
+})
+
+
+
+const emit = defineEmits(['close'])
+// const tab = ref("systems")
+const gbContainer = ref<ComponentPublicInstance | null>(null)
+const svgRef = ref<ComponentPublicInstance | null>(null)
+const figureRef = ref<ComponentPublicInstance | null>(null)
+
+
+const gbContainerWidth = ref(useElementSize(gbContainer, { width: 500, height: 0 }, { box: 'border-box' }).width)
+type ColorScale = (value: string | number) => string
+const color = computed<ColorScale>(() => {
+    return d3.scaleOrdinal(["default", undefined, "df", "hmmer"], d3.schemeTableau10)
+})
+const computedWidth = computed(() => {
+    return gbContainerWidth.value
+})
+const displayHmmerHits = ref<boolean>(true)
+
+
+const innerWidth = computed(() => {
+    const { marginLeft, marginRight } = toValue(margin)
+    return computedWidth.value - marginLeft - marginRight
+})
+const innerHeigth = computed(() => {
+    const { marginBottom } = toValue(margin)
+
+    return height.value - marginBottom
+})
+
+
+
+const minRange = ref(0)
+const maxRange = ref(innerWidth.value)
+const domain = ref([0, 10000])
+// const range = ref()
+const xScale = ref(d3.scaleLinear()
+    .domain(domain.value)
+    .range([minRange.value, maxRange.value])
+);
+const yScale = ref(d3.scaleLinear()
+    .domain([-1, 1])
+    .range([0, innerHeigth.value]));
+
+
+const computedProteins = computed(() => {
+    const { marginLeft, marginBottom } = toValue(margin)
+
+    const newData = props.sequences.proteins.filter(gene => {
+        const { start, end } = gene
+        const [scaleStart, scaleEnd] = xScale.value.domain()
+
+        return start <= scaleEnd && end >= scaleStart
+
+    }).map(gene => {
+        const width = xScale.value(gene.end) - xScale.value(gene.start)
+        const height = yScale.value(-0.75)
+        const x = xScale.value(gene.start) + marginLeft
+        const y = yScale.value(-0.75) - marginBottom
+
+        return {
+            ...gene,
+            width,
+            height,
+            x,
+            y
+        }
+    })
+    return newData
+})
+
+const computedXUnit = computed(() => {
+    if (props.sequences.proteins) {
+        if (props.sequences.proteins[0].start === null && props.sequences.proteins[0].end === null) {
+            return "aa"
+        }
+        else { return "bp" }
+    }
+})
+
+function drawGene({ width, height, strand }: { width: number, height: number, strand: number }) {
+    const context = d3.path()
+    const halfHeight = height / 2
+    const isWidthLonger = halfHeight < width
+    if (strand < 0) {
+        context.moveTo(0, halfHeight)
+        if (isWidthLonger) context.lineTo(halfHeight, 0)
+        context.lineTo(width, 0)
+        context.lineTo(width, height)
+        if (isWidthLonger) context.lineTo(halfHeight, height)
+        context.closePath()
+    } else if (strand > 0) {
+        context.moveTo(0, 0)
+        if (isWidthLonger) context.lineTo(width - halfHeight, 0)
+        context.lineTo(width, halfHeight)
+        if (isWidthLonger) context.lineTo(width - halfHeight, height)
+        context.lineTo(0, height)
+        context.closePath()
+    }
+    else {
+        context.moveTo(0, 0)
+        context.lineTo(width, 0)
+        context.lineTo(width, height)
+        context.lineTo(0, height)
+        context.closePath()
+    }
+    return context
+}
+function positionText(selection) {
+    selection.each(function (d) {
+        const textWidth = this.clientWidth
+        if (d.width < 10) {
+            d3.select(this)
+                .text('')
+        }
+        // else {
+
+        const halfW = d.width / 2
+        const halfTw = textWidth / 2
+        const k = d.height / 8
+        const x = d.strand > 0 ? halfW - halfTw - k : halfW - halfTw + k
+        d3.select(this)
+            .attr("transform", `translate(${halfW},45) rotate(45)`)
+        // }
+    })
+}
+function proteinText(item) {
+    if (item.isHmmerHit && displayHmmerHits.value || item.isDefenseSystem) {
+        return `${item.gene_name} / ${item.name}`
+    }
+
+}
+
+function proteinTitle(item) {
+    let title = `name=${item.name} | length=${item.length}`
+
+    if (item.isHmmerHit && !item.isDefenseSystem) {
+        title = `gene name=${item.gene_name} | ${title}`
+    }
+    if (item.isDefenseSystem) {
+        title = `system=${item.defenseSystem} | ${title}`
+    }
+
+    return title
+}
+
+const maxLabelSize = ref(38)
+
+function proteinTextTrunc(item) {
+    const text = proteinText(item)
+    return (text && text.length > maxLabelSize.value) ? text.slice(0, maxLabelSize.value - 1) + '...' : text;
+}
+
+function drawGenes(genesSelection) {
+    genesSelection
+        .selectAll("g.gene") // get all "existing" lines in svg
+        .data(toValue(computedProteins)) // sync them with our data
+        .join(
+            enter => {
+                const g = enter.append("g")
+                    .classed("gene", true);
+                g.append("path").classed("gene", true)
+                g.append("text")
+                    // .attr("fill", "white")
+                    .classed("gene-label", true)
+                    .attr("fill", "currentColor")
+                    .attr("dominant-baseline", "middle")
+                g.append("title")
+            },
+            update => {
+                update
+                    .attr("transform", d => `translate(${d.x},${d.y})`)
+                update.select("path.gene")
+                    .attr("d", d => drawGene(d).toString())
+                    .attr("fill", d => {
+
+                        if (d.isDefenseSystem) return toValue(color)("df")
+                        if (displayHmmerHits.value && d.isHmmerHit) return toValue(color)("hmmer")
+                        return toValue(color)("default")
+                    })
+                update.select("text.gene-label").text(proteinTextTrunc).call(positionText)
+                update.select("title").text(proteinTitle)
+            },
+            exit => exit.remove()
+        )
+
+}
+function draw() {
+    const { marginLeft } = toValue(margin)
+
+    const throttledZoomed = useThrottleFn((event) => {
+        zoomed(event)
+    }, 50)
+
+
+    function zoomed(event) {
+        const { transform } = event
+        const zx = transform.rescaleX(xScale.value);
+        domain.value = zx.domain()
+        minRange.value = zx.range()[0]
+        maxRange.value = zx.range()[1]
+    }
+
+
+    const svg = d3.select(svgRef.value);
+    const zoom = d3.zoom()
+        .scaleExtent([0.5, 32])
+        .on("zoom", throttledZoomed);
+    const xAxis = d3.axisBottom(xScale.value)
+    let gx = svg.select("g.xaxis")
+    if (gx.empty()) {
+        gx = svg.append("g").classed("xaxis", true)
+
+    }
+    gx
+        .attr("transform", `translate(${marginLeft},${height.value - 18})`)
+        .call(xAxis)
+
+    let gxTitle = gx.select("text.x-axis-title")
+    if (gxTitle.empty()) {
+        gxTitle = gx.append("text")
+            .classed("x-axis-title", true)
+            .attr("text-anchor", "end")
+            .attr("fill", "currentColor")
+            .html(() => `${computedXUnit.value} &rarr;`)
+    }
+    gxTitle
+        .attr("x", innerWidth.value)
+        .attr("y", - 10)
+
+    let gGenes = svg.select("g.genes")
+    if (gGenes.empty()) {
+        gGenes = svg.append("g").classed("genes", true)
+    }
+    gGenes.call(drawGenes, xScale, yScale)
+    svg.call(zoom).call(zoom.transform, d3.zoomIdentity);
+
+
+
+}
+
+function downloadSvg(component: ComponentPublicInstance | null, filename: string) {
+
+    const componentVal = toValue(component)
+    if (componentVal) {
+        const blob = serialize(componentVal)
+        if (blob !== undefined) {
+            download(blob, filename)
+        }
+    }
+}
+
+async function downloadPng(component: ComponentPublicInstance | null, filename: string) {
+    const blob = await rasterize(toValue(component), filename)?.then((blob) => {
+        download(blob, filename)
+    })
+}
+
+onMounted(() => {
+    draw()
+    const selectedProt = toValue(selectedProtein)
+    const toValProtIndex = toValue(proteinIndex)
+    const contig = props.sequences.name
+    if (selectedProt?.name && toValProtIndex.size > 0 && toValProtIndex.has(selectedProt.name) && selectedProt?.contig && selectedProt.contig === contig) {
+        const prot = toValProtIndex.get(selectedProt.name)
+
+        domain.value = [prot?.start ?? 0, prot?.end ?? 400]
+    }
+})
+watchEffect(() => {
+    xScale.value = d3.scaleLinear()
+        .domain(domain.value) // input values...
+        .range([minRange.value, innerWidth.value])
+    draw()
+})
+
+
+watch(selectedProtein, () => {
+
+
+    const selectedProt = toValue(selectedProtein)
+    const toValProtIndex = toValue(proteinIndex)
+    const contig = props.sequences.name
+    if (selectedProt?.name && toValProtIndex.size > 0 && toValProtIndex.has(selectedProt.name) && selectedProt?.contig && selectedProt.contig === contig) {
+        const prot = toValProtIndex.get(selectedProt.name)
+
+        domain.value = [prot?.start ?? 0, prot?.end ?? 400]
+    }
+})
+
+</script>
+
+
+<template>
+    <div>
+        <v-card flat color="transparent" ref="gbContainer">
+            <v-toolbar flat>
+
+                <v-toolbar-title> {{ props.sequences.name }}</v-toolbar-title>
+                <v-switch v-model="displayHmmerHits" color="primary" label="Display HMM-only hits" hide-details="auto"
+                    class="mr-2"></v-switch>
+                <v-menu>
+                    <template v-slot:activator="{ props }">
+                        <v-btn color="primary" prepend-icon="md:download" class="mr-2" v-bind="props">
+                            Export
+                        </v-btn>
+                    </template>
+                    <v-list>
+                        <v-list-subheader>Images</v-list-subheader>
+                        <v-list-item prepend-icon="mdi-svg" value="svg"
+                            @click="downloadSvg(figureRef, `df-results-${props.analysisName}-${props.sequences.name}.svg`)">
+                            <v-list-item-title>to
+                                svg</v-list-item-title>
+
+                        </v-list-item>
+                        <v-list-item prepend-icon="mdi-image" value="png"
+                            @click="downloadPng(figureRef, `df-results-${props.analysisName}-${props.sequences.name}.png`)">
+
+                            <v-list-item-title>to
+                                png</v-list-item-title>
+                        </v-list-item>
+                    </v-list>
+                </v-menu>
+                <v-btn v-if="props.canClose" icon="mdi-close"
+                    @click="emit('close', { contig: props.sequences.name })"></v-btn>
+            </v-toolbar>
+
+            <v-card-subtitle>
+                Defense system hits: {{ props.sequences.defenseSystemHits }} | Hmmer hits: {{ props.sequences.hmmerHits
+                }}
+            </v-card-subtitle>
+            <div ref="figureRef">
+                <svg ref="svgRef" :width="computedWidth" :height="height">
+                    <g class="x-axis" />
+                </svg>
+            </div>
+        </v-card>
+    </div>
+</template>
\ No newline at end of file
diff --git a/frontend/components/tableResults/GenesTable.vue b/frontend/components/tableResults/GenesTable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4c3ea83a5522bbc1b24e4f8069e5b83308876d01
--- /dev/null
+++ b/frontend/components/tableResults/GenesTable.vue
@@ -0,0 +1,76 @@
+<script setup lang="ts">
+import type { GenesOut } from "~/types";
+
+
+interface Props {
+    contig: string,
+    fromNt: boolean
+}
+const props = defineProps<Props>()
+
+const route = useRoute();
+const analysisId = computed(() => {
+    if (Array.isArray(route.params.analysisId)) return null
+    return parseInt(route.params.analysisId)
+})
+
+const { data: genes, error } = await useAPI<GenesOut>(
+    `/analysis/${toValue(analysisId)}/genes`
+);
+
+if (error.value) {
+    throw createError({ message: `Error while getting the list of genes for analysis ${toValue(analysisId)}` })
+}
+
+
+const headers = ref([
+    { title: "Hit id", align: "end", key: "hit_id", fixed: true },
+    { title: "Replicon", align: "end", key: "replicon" },
+    { title: "Position", align: "end", key: "hit_pos" },
+    { title: "Model fqn", align: "end", key: "model_fqn" },
+    { title: "System id", align: "end", key: "sys_id" },
+    { title: "System loci", align: "end", key: "sys_loci" },
+    { title: "locus_num", align: "end", key: "locus_num" },
+    // { title: "sys_wholeness", align: "end", key: "sys_wholeness" },
+    { title: "Score", align: "end", key: "sys_score" },
+    { title: "sys_occ", align: "end", key: "sys_occ" },
+    { title: "Gene ref", align: "end", key: "hit_gene_ref" },
+    { title: "Status", align: "end", key: "hit_status" },
+    { title: "Seq length", align: "end", key: "hit_seq_len" },
+    { title: "i_eval", align: "end", key: "hit_i_eval" },
+    { title: "Score", align: "end", key: "hit_score" },
+    { title: "Profile cov", align: "end", key: "hit_profile_cov" },
+    { title: "Seq cov", align: "end", key: "hit_seq_cov" },
+]);
+
+const sanitizedGenes = computed(() => {
+    console.log("dans sanitized gene gene tabl")
+    if (genes.value?.genes) {
+        return genes.value.genes.map(gene => {
+            return { ...gene, model_fqn: gene.model_fqn.split("/").slice(-2).join(" - "), sys_id: gene.sys_id.split(gene.replicon)[1].slice(1) }
+        })
+        // .filter(gene => {
+        //     return gene.replicon === props.contig
+        // })
+    }
+    else { return [] }
+})
+const groupBy = ref([
+    {
+        key: 'replicon',
+        order: 'asc',
+    },
+])
+
+</script>
+<template>
+    <AnalysisResultDataTable v-if="sanitizedGenes.length > 0" :items="sanitizedGenes" :headers="headers"
+        :group-by="props.fromNt ? groupBy : false" :contig="props.contig" />
+    <v-card v-else flat color="transparent">
+        <v-card-text>
+            <v-alert type="info" variant="tonal">
+                No gene found
+            </v-alert>
+        </v-card-text>
+    </v-card>
+</template>
\ No newline at end of file
diff --git a/frontend/pages/analyses/[analysisId]/hmmer.vue b/frontend/components/tableResults/HmmersTable.vue
similarity index 66%
rename from frontend/pages/analyses/[analysisId]/hmmer.vue
rename to frontend/components/tableResults/HmmersTable.vue
index b4eeb965a917dc1a9678cbbff952ec0d3f32898f..25d84b92de64f3d26a360d384d85949b9ec3036c 100644
--- a/frontend/pages/analyses/[analysisId]/hmmer.vue
+++ b/frontend/components/tableResults/HmmersTable.vue
@@ -1,22 +1,39 @@
 <script setup lang="ts">
-import { useFetch, useRoute } from "#imports";
+import { useRoute } from "#imports";
 import type { HmmersOut } from "~/types";
 
 
+interface Props {
+    contig: string,
+    fromNt: boolean
 
+}
+const props = defineProps<Props>()
 const route = useRoute();
+const analysisId = computed(() => {
+    if (Array.isArray(route.params.analysisId)) return null
+    return parseInt(route.params.analysisId)
+})
 const { data: hmmers, error } = await useAPI<HmmersOut>(
-    `/analysis/${route.params.analysisId}/hmmers`
+    `/analysis/${toValue(analysisId)}/hmmers`
 );
 
 
 if (error.value) {
-    throw createError({ message: `Error while getting the list of hmmer for analysis ${route.params.analysisId}` })
+    throw createError({ message: `Error while getting the list of hmmer for analysis ${toValue(analysisId)}` })
 }
 
+
+const groupBy = ref([
+    {
+        key: 'replicon',
+        order: 'asc',
+    },
+])
+
 const headers = ref([
     { title: "Hit identifier", align: "end", key: "hit_id", fixed: true },
-    // { title: "Replicon", align: "end", key: "replicon" },
+    { title: "Replicon", align: "end", key: "replicon" },
     { title: "Position", align: "end", key: "hit_pos" },
     { title: "Sequence length", align: "end", key: "hit_sequence_length" },
     { title: "Gene name", align: "end", key: "gene_name" },
@@ -34,11 +51,14 @@ const sanitizedHmmer = computed(() => {
         return hmmers.value.hmmers
     } else { return [] }
 })
+
+
 </script>
 <template>
-    <AnalysisResultDataTable v-if="sanitizedHmmer.length > 0" :items="sanitizedHmmer" :headers="headers" />
+    <AnalysisResultDataTable v-if="sanitizedHmmer.length > 0" :items="sanitizedHmmer" :headers="headers"
+        :group-by="props.fromNt ? groupBy : false" :contig="props.contig" />
     <v-card v-else flat color="transparent">
-        <v-card-text>
+        <v-card-text>s
             <v-alert type="info" variant="tonal">
                 No hmmer hit
             </v-alert>
diff --git a/frontend/components/tableResults/SystemsTable.vue b/frontend/components/tableResults/SystemsTable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1a8ed3dfe7bd63dfd7bb32d731011496c44d6e56
--- /dev/null
+++ b/frontend/components/tableResults/SystemsTable.vue
@@ -0,0 +1,69 @@
+<script setup lang="ts">
+import { useRoute } from "#imports";
+import type { SystemsOut } from "~/types";
+
+interface Props {
+    contig: string,
+    fromNt: boolean
+}
+const props = defineProps<Props>()
+const route = useRoute();
+
+const analysisId = computed(() => {
+    if (Array.isArray(route.params.analysisId)) return null
+    return parseInt(route.params.analysisId)
+})
+
+
+const { data: systems, error } = await useAPI<SystemsOut>(
+    `/analysis/${toValue(analysisId)}/systems`
+);
+
+if (error.value) {
+    throw createError({ message: `Error while getting the list systems for analysis ${toValue(analysisId)}` })
+}
+
+const groupBy = ref([
+    {
+        key: 'replicon',
+        order: 'asc',
+    },
+])
+const headers = ref([
+    { title: "System id", align: "end", key: "sys_id", fixed: true, },
+    { title: "type", align: "end", key: "type", },
+    { title: "subtype", align: "end", key: "subtype" },
+    { title: "sys_beg", align: "end", key: "sys_beg" },
+    { title: "sys_end", align: "end", key: "sys_end" },
+    { title: "protein_in_syst", align: "end", key: "protein_in_syst" },
+]);
+
+const sanitizedSystems = computed(() => {
+    if (systems.value?.systems) {
+        let data = systems.value.systems.map((system) => {
+            let newSystem = {
+                ...system,
+                protein_in_syst: system.protein_in_syst.split(','),
+                replicon: props.fromNt ? system.sys_beg.replace(/_\d+$/, "") : props.contig.replace(/\.[^\.]*?$/, "")
+            }
+            if (system.type === 'CasFinder') return { ...newSystem, type: 'Cas' }
+            return newSystem
+        })
+        return data
+
+    } else { return [] }
+})
+
+</script>
+<template>
+    <AnalysisResultDataTable v-if="sanitizedSystems.length > 0" :items="sanitizedSystems" :headers="headers"
+        :group-by="props.fromNt ? groupBy : false" :contig="props.contig.replace(/\.[^\.]*?$/, '')" />
+    <v-card v-else flat color="transparent">
+        <v-card-text>
+            <v-alert type="info" variant="tonal">
+                No system found
+            </v-alert>
+        </v-card-text>
+    </v-card>
+
+</template>
diff --git a/frontend/composables/useCssSelector.ts b/frontend/composables/useCssSelector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..639edc7abc631fbc30f2ecf824466d168ea4e24c
--- /dev/null
+++ b/frontend/composables/useCssSelector.ts
@@ -0,0 +1,14 @@
+
+
+export function useCssSelector(selector: MaybeRef<string>) {
+    const sanitizedSelector = ref<string | undefined>(undefined)
+    function sanitized(selector: MaybeRef<string>) {
+        return toValue(selector).replace(/[^\w\d_-]/g, "-")
+    }
+    watchEffect(() => {
+        sanitizedSelector.value = sanitized(selector)
+    })
+
+    return { sanitizedSelector }
+
+}
\ No newline at end of file
diff --git a/frontend/composables/useSelectedProtein.ts b/frontend/composables/useSelectedProtein.ts
index d212fc35f116852b690e1d7e746aac038d63d5eb..cb180dc8d17e436b204ef60f97ba47b71aa8cba3 100644
--- a/frontend/composables/useSelectedProtein.ts
+++ b/frontend/composables/useSelectedProtein.ts
@@ -1,14 +1,12 @@
 
-const selectedProtein = ref<string | undefined>(undefined)
+const selectedProtein = ref<{ name: string | undefined, contig: string | undefined } | undefined>(undefined)
 
 export function useSelectedProtein() {
+    function setSelectedProtein(hit_id: string | undefined, contig: string | undefined) {
+        selectedProtein.value = {
+            name: hit_id, contig
+        }
 
-
-
-
-    function setSelectedProtein(hit_id: string | undefined) {
-        selectedProtein.value = hit_id
     }
-
     return { selectedProtein, setSelectedProtein }
 }
\ No newline at end of file
diff --git a/frontend/composables/useSerialize.ts b/frontend/composables/useSerialize.ts
index 47bcbc451f39d721fb4e6527bb2f77303c66bbad..6ee6f1f44db3501d97931aeee614f263dcf48f26 100644
--- a/frontend/composables/useSerialize.ts
+++ b/frontend/composables/useSerialize.ts
@@ -9,7 +9,9 @@ export function useSerialize() {
 
     function serialize(compo: MaybeRef<ComponentPublicInstance | null>) {
         const toValueCompo = toValue(compo)
+
         if (toValueCompo !== null) {
+            console.log(toValueCompo)
             const { svg } = useSvgPlot(toValueCompo)
             const toValueSvg = toValue(svg)
             if (toValueSvg !== null) {
diff --git a/frontend/composables/useSvgPlot.ts b/frontend/composables/useSvgPlot.ts
index b2375d3a89bacfd8b7cc66451c2653fbdee38a91..5836801475aacbdf930a4220c088503824020f1f 100644
--- a/frontend/composables/useSvgPlot.ts
+++ b/frontend/composables/useSvgPlot.ts
@@ -2,8 +2,9 @@ export function useSvgPlot(component: MaybeRef<ComponentPublicInstance>) {
     const svg = ref<SVGElement | null>(null)
 
     const toValueCompo = toValue(component)
-    const rootElem = toValueCompo.$el
+    console.log(toValueCompo)
+    const rootElem = toValueCompo
+    console.log(rootElem)
     svg.value = rootElem.querySelector("svg")
-    console.log(svg.value)
     return { svg }
 }
\ No newline at end of file
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index 46bcdc729132107c84a60172f4db4c6bea74c71b..e1cd81ed3b0ed5b6e53c66ff9cfeacea9fd5075c 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -15,7 +15,8 @@ export default defineNuxtConfig({
     public: {
       wikiUrl: '/wiki',
       dfApiPrefix: "/dfapi",
-      version: pkg.version
+      version: pkg.version,
+      displayMessage: false
     }
   },
   modules: [
diff --git a/frontend/pages/analyses/[analysisId].vue b/frontend/pages/analyses/[analysisId].vue
index 43bcdb5225dbd3ed494924104ebeda752a04ddae..4b9ae99e2b4f8c297d6282d0ecd040933c72173b 100644
--- a/frontend/pages/analyses/[analysisId].vue
+++ b/frontend/pages/analyses/[analysisId].vue
@@ -1,31 +1,28 @@
 <script setup lang="ts">
-import { useThrottleFn } from '@vueuse/core'
-import type { Analysis, Gene, GenesOut, Hmmer, HmmersOut, ProteinsOut } from '@/types'
-// import { useFetchAnalysis } from "../../composables/useFetchAnalysis";
-import { useSelectedProtein } from '@/composables/useSelectedProtein'
+import type { Analysis, Gene, GenesOut, Hmmer, HmmersOut, Protein, ProteinsOut } from '@/types'
 import { useRoute, computed } from "#imports";
 import * as d3 from "d3";
-import { useElementSize } from '@vueuse/core'
 import { joinURL } from "ufo";
 import type { ComponentPublicInstance } from 'vue'
-import { useSerialize } from '~/composables/useSerialize';
-import { useRasterize } from '~/composables/useRasterize';
-import { useDownloadBlob } from '~/composables/useDownloadBlob';
+import { useGoTo } from 'vuetify'
+import { useCssSelector } from "~/composables/useCssSelector"
+
+import SystemsTable from "~/components/tableResults/SystemsTable.vue";
+import GenesTable from "~/components/tableResults/GenesTable.vue";
+import HmmersTable from "~/components/tableResults/HmmersTable.vue";
 
 const route = useRoute();
 const { selectedProtein } = useSelectedProtein()
-
 const runtimeConfig = useRuntimeConfig()
-const { serialize } = useSerialize()
-const { rasterize } = useRasterize()
-const { download } = useDownloadBlob()
-
+const itemsPerPage = ref(-1)
+const itemsPerPageOptions = ref<{ title: string, value: number }[]>([{ title: "2", value: 2 }, { title: "5", value: 5 }, { title: "10", value: 10 }, { title: "All", value: -1 }])
+const onlyWithDefenseSystem = ref(true)
+const goTo = useGoTo()
 const analysisId = computed(() => {
   if (Array.isArray(route.params.analysisId)) return null
   return parseInt(route.params.analysisId)
 })
 
-
 const breadcrumbItems = computed(() => {
   return [
     {
@@ -50,7 +47,6 @@ const breadcrumbItems = computed(() => {
 })
 
 
-const height = ref(300)
 
 const genesMap = computed(() => {
   const mapGenes = new Map<string, Gene>()
@@ -91,14 +87,7 @@ const { data: genes } = await useAPI<GenesOut>(
 const { data: rawHmmer } = await useAPI<HmmersOut>(
   `/analysis/${route.params.analysisId}/hmmers`
 );
-const computedXUnit = computed(() => {
-  if (rawProteins.value?.proteins) {
-    if (rawProteins.value.proteins[0].start === null && rawProteins.value.proteins[0].end === null) {
-      return "aa"
-    }
-    else { return "bp" }
-  }
-})
+
 
 const sanitizedData = computed(() => {
   if (rawProteins.value?.proteins) {
@@ -135,320 +124,163 @@ const sanitizedData = computed(() => {
   else { return [] }
 })
 
-const proteinIndex = computed(() => {
-  return new Map(sanitizedData.value.map((d, i, arr) => {
-    let previousStart = 0
-    if (i > 0) {
-      previousStart = arr[i - 1].start - 10
-    }
-    let nextEnd = previousStart + 400
-    if (i < arr.length - 1) {
-      nextEnd = arr[i + 1].end + 10
-    }
-    return [d.id, { ...d, previousStart, nextEnd }]
-  }))
-
-})
-
-
+const page = ref(1)
+const tab = ref("systems")
+const figureRef = ref<ComponentPublicInstance[] | null>(null)
 
 
-const displayHmmerHits = ref<boolean>(true)
-type ColorScale = (value: string | number) => string
-const color = computed<ColorScale>(() => {
-
-  return d3.scaleOrdinal(["default", undefined, "df", "hmmer"], d3.schemeTableau10)
-})
 
 
-const gbContainer = ref(null)
-const gbContainerWidth = ref(useElementSize(gbContainer, { width: 500, height: 0 }, { box: 'border-box' }).width)
+const groupedPerContigData = computed(() => {
 
-const computedWidth = computed(() => {
-  return gbContainerWidth.value
+  const computedDataVal = toValue(sanitizedData)
+  const analysisVal = toValue(analysis)
+  if (computedDataVal && analysisVal && analysisVal.from_nt) {
+    return d3.groups(computedDataVal, (d) => {
+      return d.name.replace(/_\d+?$/i, "")
+    }).map(([name, proteins]: [string, Array<Protein>]) => {
+      return {
+        name,
+        proteins,
+        defenseSystemHits: proteins.reduce((acc, curr) => {
+          if (curr.isDefenseSystem) return acc + 1
+          return acc
+        }, 0),
+        hmmerHits: proteins.reduce((acc, curr) => {
+          if (curr.isHmmerHit) return acc + 1
+          return acc
+        }, 0),
+      }
+    })
+  }
+  else {
+    return [{
+      name: toValue(analysis)?.input_name.replace(/\.[^\.]*?$/, ""),
+      proteins: computedDataVal,
+      defenseSystemHits: computedDataVal.reduce((acc, curr) => {
+        if (curr.isDefenseSystem) return acc + 1
+        return acc
+      }, 0),
+      hmmerHits: computedDataVal.reduce((acc, curr) => {
+        if (curr.isHmmerHit) return acc + 1
+        return acc
+      }, 0),
+    }]
+  }
 })
 
+const userSelectedContigs = ref<Set<string> | undefined>(undefined)
 
-
-function downloadSvg(component: ComponentPublicInstance | null, filename: string) {
-  const blob = toValue(serialize(toValue(component)))
-  if (blob !== undefined) {
-    download(blob, filename)
+const maxContigCount = ref<null | number>(10)
+const contigDomain = computed(() => {
+  const groupedPerContigDataVal = toValue(groupedPerContigData)
+  if (groupedPerContigDataVal.length >= 1) {
+    return [1, groupedPerContigDataVal.length]
+  } else {
+    return null
   }
-}
 
-async function downloadPng(component: ComponentPublicInstance | null, filename: string) {
-  const blob = await rasterize(toValue(component), filename)?.then((blob) => {
-    download(blob, filename)
-  })
-}
-
-watch(selectedProtein, () => {
-  const toValHitId = toValue(selectedProtein)
-  const toValProtIndex = toValue(proteinIndex)
-  if (toValHitId && toValProtIndex.size > 0 && toValProtIndex.has(toValHitId)) {
 
-    const prot = toValProtIndex.get(toValHitId)
-    domain.value = [prot?.previousStart ?? 0, prot?.nextEnd ?? 400]
-  }
 })
+const filteredGroupedPerContigData = computed(() => {
+  let data = toValue(groupedPerContigData)
+  const maxContigCountVal = toValue(maxContigCount)
+  data = data
+    .sort((a, b) => b.defenseSystemHits - a.defenseSystemHits)
 
-const marginLeftGb = ref(10)
-const marginRightGb = ref(10)
-const marginBottomGb = ref(10)
-
-const innerWidth = computed(() => {
-  return computedWidth.value - marginLeftGb.value - marginRightGb.value
-})
-const innerHeigth = computed(() => {
-  return height.value - marginBottomGb.value
+  return data
 })
 
-const minRange = ref(0)
-const maxRange = ref(innerWidth.value)
-
-
-const svgRef = ref<ComponentPublicInstance | null>(null)
-const figureRef = ref<ComponentPublicInstance | null>(null)
-
-const domain = ref([0, 10000])
-// const range = ref()
-const xScale = ref(d3.scaleLinear()
-  .domain(domain.value)
-  .range([minRange.value, maxRange.value])
-);
-const yScale = ref(d3.scaleLinear()
-  .domain([-1, 1])
-  .range([0, innerHeigth.value]));
-
-
 
 
-const computedData = computed(() => {
-  const newData = sanitizedData.value.filter(gene => {
-    const { start, end } = gene
-    const [scaleStart, scaleEnd] = xScale.value.domain()
 
-    return start <= scaleEnd && end >= scaleStart
-
-  }).map(gene => {
-    const width = xScale.value(gene.end) - xScale.value(gene.start)
-    const height = yScale.value(-0.75)
-    const x = xScale.value(gene.start) + marginLeftGb.value
-    const y = yScale.value(-0.75) - marginBottomGb.value
-
-    return {
-      ...gene,
-      width,
-      height,
-      x,
-      y
-    }
-  })
-  return newData
-})
-
-function drawGene({ width, height, strand }) {
-  const context = d3.path()
-  const halfHeight = height / 2
-  const isWidthLonger = halfHeight < width
-  if (strand < 0) {
-    context.moveTo(0, halfHeight)
-    if (isWidthLonger) context.lineTo(halfHeight, 0)
-    context.lineTo(width, 0)
-    context.lineTo(width, height)
-    if (isWidthLonger) context.lineTo(halfHeight, height)
-    context.closePath()
-  } else if (strand > 0) {
-    context.moveTo(0, 0)
-    if (isWidthLonger) context.lineTo(width - halfHeight, 0)
-    context.lineTo(width, halfHeight)
-    if (isWidthLonger) context.lineTo(width - halfHeight, height)
-    context.lineTo(0, height)
-    context.closePath()
-  }
-  else {
-    context.moveTo(0, 0)
-    context.lineTo(width, 0)
-    context.lineTo(width, height)
-    context.lineTo(0, height)
-    context.closePath()
-  }
-  return context
-}
-
-function positionText(selection) {
-  selection.each(function (d) {
-    const textWidth = this.clientWidth
-    if (d.width < 10) {
-      d3.select(this)
-        .text('')
-    }
-    // else {
-
-    const halfW = d.width / 2
-    const halfTw = textWidth / 2
-    const k = d.height / 8
-    const x = d.strand > 0 ? halfW - halfTw - k : halfW - halfTw + k
-    d3.select(this)
-      .attr("transform", `translate(${halfW},45) rotate(45)`)
-    // }
-  })
+function getResultArchiveUrl(analysisId: number) {
+  const url = joinURL(runtimeConfig.public.dfApiPrefix, `/analysis/${analysisId}/results-archive`)
+  window.location.href = url;
 }
 
-function proteinText(item) {
-  if (item.isHmmerHit && displayHmmerHits.value || item.isDefenseSystem) {
-    return `${item.gene_name} / ${item.name}`
+function handleCloseContig(payload) {
+  const { contig } = payload
+  const userSelectedContigsVal = toValue(userSelectedContigs)
+  if (userSelectedContigsVal?.has(contig)) {
+    userSelectedContigsVal.delete(contig)
   }
-
 }
-
-function proteinTitle(item) {
-  let title = `name=${item.name} | length=${item.length}`
-
-  if (item.isHmmerHit && !item.isDefenseSystem) {
-    title = `gene name=${item.gene_name} | ${title}`
-  }
-  if (item.isDefenseSystem) {
-    title = `system=${item.defenseSystem} | ${title}`
+const computedSelectedContigs = computed(() => {
+  const userSelectedContigsVal = toValue(userSelectedContigs)
+  const filteredGroupedPerContigDataVal = toValue(filteredGroupedPerContigData)
+  if (userSelectedContigsVal === undefined) {
+    return filteredGroupedPerContigDataVal.slice(0, 1)
   }
-
-  return title
-}
-
-const maxLabelSize = ref(38)
-
-function proteinTextTrunc(item) {
-  const text = proteinText(item)
-  return (text && text.length > maxLabelSize.value) ? text.slice(0, maxLabelSize.value - 1) + '...' : text;
-}
-
-
-function drawGenes(genesSelection) {
-  genesSelection
-    .selectAll("g.gene") // get all "existing" lines in svg
-    .data(computedData.value) // sync them with our data
-    .join(
-      enter => {
-        const g = enter.append("g")
-          .classed("gene", true);
-        g.append("path").classed("gene", true)
-        g.append("text")
-          // .attr("fill", "white")
-          .classed("gene-label", true)
-          .attr("fill", "currentColor")
-          .attr("dominant-baseline", "middle")
-        g.append("title")
-      },
-      update => {
-        update
-          .attr("transform", d => `translate(${d.x},${d.y})`)
-        update.select("path.gene")
-          .attr("d", d => drawGene(d).toString())
-          .attr("fill", d => {
-
-            if (d.isDefenseSystem) return toValue(color)("df")
-            if (displayHmmerHits.value && d.isHmmerHit) return toValue(color)("hmmer")
-            return toValue(color)("default")
-          })
-        update.select("text.gene-label").text(proteinTextTrunc).call(positionText)
-        update.select("title").text(proteinTitle)
-      },
-      exit => exit.remove()
-    )
-
-}
-
-function draw() {
-
-  const throttledZoomed = useThrottleFn((event) => {
-    zoomed(event)
-  }, 50)
-
-
-  function zoomed(event) {
-    const { transform } = event
-    const zx = transform.rescaleX(xScale.value);
-    domain.value = zx.domain()
-    minRange.value = zx.range()[0]
-    maxRange.value = zx.range()[1]
-  }
-
-
-  const svg = d3.select(svgRef.value);
-  const zoom = d3.zoom()
-    .scaleExtent([0.5, 32])
-    .on("zoom", throttledZoomed);
-  const xAxis = d3.axisBottom(xScale.value)
-  let gx = svg.select("g.xaxis")
-  if (gx.empty()) {
-    gx = svg.append("g").classed("xaxis", true)
-
-  }
-  gx
-    .attr("transform", `translate(${marginLeftGb.value},${height.value - 18})`)
-    .call(xAxis)
-
-  let gxTitle = gx.select("text.x-axis-title")
-  if (gxTitle.empty()) {
-    gxTitle = gx.append("text")
-      .classed("x-axis-title", true)
-      .attr("text-anchor", "end")
-      .attr("fill", "currentColor")
-      .html(() => `${computedXUnit.value} &rarr;`)
-  }
-  gxTitle
-    .attr("x", innerWidth.value)
-    .attr("y", - 10)
-
-
-
-
-  let gGenes = svg.select("g.genes")
-  if (gGenes.empty()) {
-    gGenes = svg.append("g").classed("genes", true)
+  else {
+    return filteredGroupedPerContigDataVal.filter(g => userSelectedContigsVal.has(g.name))
   }
-  gGenes.call(drawGenes, xScale, yScale)
-  svg.call(zoom).call(zoom.transform, d3.zoomIdentity);
-
+})
 
+const { data: analysis, refresh, pending, error } = await useAPI<Analysis>(`/analysis/${route.params.analysisId}`, {
+  key: `analysis-${route.params.analysisId}`,
+});
 
-}
-function getResultArchiveUrl(analysisId: number) {
-  return joinURL(runtimeConfig.public.dfApiPrefix, `/analysis/${analysisId}/results-archive`)
+if (error.value) {
+  throw createError({ statusCode: 404, statusMessage: 'No analysis found' })
 }
 
-onMounted(() => {
 
-  draw()
+useHead({
+  title: analysis.value?.name ?? 'analysis',
 })
+const proteinIndex = computed(() => {
+  return new Map(props.sequences.proteins.map((d, i, arr) => {
+    return [d.id, { ...d }]
+  }))
 
-watchEffect(() => {
-  xScale.value = d3.scaleLinear()
-    .domain(domain.value) // input values...
-    .range([minRange.value, innerWidth.value])
-  draw()
 })
 
+const isUserSelectedContigsDefined = computed(() => {
+  return toValue(userSelectedContigs) !== undefined
 
-const selectedResult = ref(null);
-
+})
+watch(selectedProtein, async () => {
+  const selectedProt = toValue(selectedProtein)
+  let userSelectedContigsVal = toValue(userSelectedContigs)
+  const itemsPerPageVal = toValue(itemsPerPage)
+  if (selectedProt !== undefined) {
+    const { contig } = selectedProt
+    if (contig !== undefined) {
+      if (userSelectedContigsVal === undefined) {
+        userSelectedContigs.value = new Set([contig])
+      } else {
+        userSelectedContigsVal.add(contig)
+      }
+      const index = toValue(computedSelectedContigs).findIndex(contigObj => {
+        return contigObj.name === contig
+      })
+      if (index > -1) {
+        if (itemsPerPageVal === -1) {
+          page.value = 1
+        } else {
+          const position = index + 1
+          if (position % itemsPerPageVal === 0) {
+            page.value = position / itemsPerPageVal
+          }
+          else {
+            page.value = Math.trunc(position / itemsPerPageVal) + 1
+          }
+        }
+        await nextTick()
+        goTo(`#${toValue(useCssSelector(`contig-${contig}`).sanitizedSelector)}`, { offset: -100 })
+      }
+    }
+  }
 
+}, { deep: true })
 
-const { data: analysis, refresh, pending, error } = await useAPI<Analysis>(`/analysis/${analysisId.value}`, {
-  key: `analysis-${analysisId.value}`,
-});
 
-if (error.value) {
-  throw createError({ statusCode: 404, statusMessage: 'No analysis found' })
+function getSelector(selector: MaybeRef<string>) {
+  const { sanitizedSelector } = useCssSelector(selector)
+  return sanitizedSelector
 }
 
-
-useHead({
-  title: analysis.value?.name ?? 'analysis',
-})
-
-
 </script>
 
 <template>
@@ -459,7 +291,7 @@ useHead({
     </v-app-bar>
 
     <v-card>
-      <v-toolbar density="compact" class="pr-2">
+      <v-toolbar color="transparent" flat>
         <v-toolbar-title>{{ analysis.name }}</v-toolbar-title>
         <v-menu>
           <template v-slot:activator="{ props }">
@@ -469,21 +301,9 @@ useHead({
           </template>
           <v-list>
             <v-list-item v-if="analysis !== null" prepend-icon="mdi-archive" value="archive">
-              <v-list-item-title @click="getResultArchiveUrl(analysis.id)">Download all results
+              <v-list-item-title @click="getResultArchiveUrl(analysisId)">Download all results
               </v-list-item-title>
             </v-list-item>
-            <v-divider></v-divider>
-            <v-list-subheader>Images</v-list-subheader>
-            <v-list-item prepend-icon="mdi-svg" value="svg">
-              <v-list-item-title @click="downloadSvg(figureRef, `df-results-${analysis.name}.svg`)">to
-                svg</v-list-item-title>
-
-            </v-list-item>
-            <v-list-item prepend-icon="mdi-image" value="png">
-
-              <v-list-item-title @click="downloadPng(figureRef, `df-results-${analysis.name}.png`)">to
-                png</v-list-item-title>
-            </v-list-item>
           </v-list>
         </v-menu>
         <v-chip color="primary" rounded>{{ new Date(analysis.create_time).toLocaleString() }}</v-chip>
@@ -511,34 +331,65 @@ useHead({
 
       <template v-else>
         <v-card-text>
-          <div ref="gbContainer">
-            <v-card ref="figureRef" flat color="transparent">
-              <v-toolbar variant="flat" density="compact" color="transparent">
-                <v-spacer></v-spacer><v-toolbar-items>
-                  <v-switch v-model="displayHmmerHits" color="primary" label="Display HMM-only hits
-                " class="mr-2"></v-switch>
-                </v-toolbar-items></v-toolbar>
-
-              <svg ref="svgRef" :width="computedWidth" :height="height">
-                <g class="x-axis" />
-              </svg>
-            </v-card>
-          </div>
+          <v-data-iterator v-if="computedSelectedContigs.length > 0" :items="computedSelectedContigs" :page="page"
+            :items-per-page="itemsPerPage">
+
+            <template v-slot:default="{ items }">
+              <v-card v-for="contig in items" :key="contig.raw.name" :flat="false" class="my-2"
+                :id="toValue(useCssSelector(`contig-${contig.raw.name}`).sanitizedSelector)">
+                <v-card ref="figureRef" flat color="transparent" class="mb-1">
+                  <SequenceBrowser :sequences="contig.raw" :from-nt="analysis.from_nt" :analysis-name="analysis.name"
+                    :can-close="isUserSelectedContigsDefined" @close="handleCloseContig">
+                  </SequenceBrowser>
+                </v-card>
+              </v-card>
+            </template>
+            <template v-slot:footer="{ page, pageCount, prevPage, nextPage }">
+              <div class="d-flex align-center justify-center pa-4">
+                <div class="d-flex text-caption align-center">
+                  <span class="mr-1">items per page</span>
+                  <v-select v-model="itemsPerPage" :items="itemsPerPageOptions" hide-details="auto" density="compact"
+                    variant="outlined" class="mr-2">
+                  </v-select>
+                </div>
+                <v-btn :disabled="page === 1" density="comfortable" icon="mdi-arrow-left" variant="tonal" rounded
+                  @click="prevPage"></v-btn>
+
+                <div class="mx-2 text-caption">
+                  Page {{ page }} of {{ pageCount }}
+                </div>
+
+                <v-btn :disabled="page >= pageCount" density="comfortable" icon="mdi-arrow-right" variant="tonal"
+                  rounded @click="nextPage"></v-btn>
+
+              </div>
+            </template>
+          </v-data-iterator>
+
+          <v-card v-else>
+            <v-alert type="info" variant="tonal">No defense finder hits</v-alert>
+          </v-card>
+          <v-tabs v-model="tab" align-tabs="start" color="primary">
+            <v-tab value="systems">Systems</v-tab>
+            <v-tab value="genes">Genes</v-tab>
+            <v-tab value="hmmers">Hmmers</v-tab>
+          </v-tabs>
+          <v-card flat color="transparent">
+            <v-window v-model="tab">
+              <v-window-item value="systems">
+                <SystemsTable :contig="analysis.input_name" :from-nt="analysis.from_nt"></SystemsTable>
+              </v-window-item>
+              <v-window-item value="genes">
+                <GenesTable :contig="analysis.input_name" :from-nt="analysis.from_nt"></GenesTable>
+              </v-window-item>
+              <v-window-item value="hmmers">
+                <HmmersTable :contig="analysis.input_name" :from-nt="analysis.from_nt"> </HmmersTable>
+              </v-window-item>
+            </v-window>
+          </v-card>
         </v-card-text>
-        <v-card-text>
-          <v-btn-toggle v-model="selectedResult" rounded="0" color="primary" group>
-            <v-btn value="systems" :to="`/analyses/${analysis.id}/systems`">
 
-              Systems
-            </v-btn>
-            <v-btn value="genes" exact :to="`/analyses/${analysis.id}/genes`">
-              Genes
-            </v-btn>
-            <v-btn value="hmmer" :to="`/analyses/${analysis.id}/hmmer`">
-              Hmmer
-            </v-btn>
-          </v-btn-toggle>
-        </v-card-text>
+
       </template>
       <NuxtPage />
     </v-card>
diff --git a/frontend/pages/analyses/[analysisId]/genes.vue b/frontend/pages/analyses/[analysisId]/genes.vue
deleted file mode 100644
index 49f4567e3472fb718090380532a768616e2fd9a2..0000000000000000000000000000000000000000
--- a/frontend/pages/analyses/[analysisId]/genes.vue
+++ /dev/null
@@ -1,60 +0,0 @@
-<script setup lang="ts">
-import { useFetch, useRoute, computed } from "#imports";
-import type { GenesOut } from "~/types";
-const route = useRoute();
-
-
-
-const { data: genes, error } = await useAPI<GenesOut>(
-  `/analysis/${route.params.analysisId}/genes`
-);
-
-
-if (error.value) {
-  throw createError({ message: `Error while getting the list of genes for analysis ${route.params.analysisId}` })
-}
-
-
-const search = ref('')
-const groupBy = ref([{ key: 'model_fqn', order: 'asc' }])
-const headers = ref([
-  { title: "Hit id", align: "end", key: "hit_id", fixed: true },
-  // { title: "Replicon", align: "end", key: "replicon" },
-  { title: "Position", align: "end", key: "hit_pos" },
-  { title: "Model fqn", align: "end", key: "model_fqn" },
-  { title: "System id", align: "end", key: "sys_id" },
-  { title: "System loci", align: "end", key: "sys_loci" },
-  { title: "locus_num", align: "end", key: "locus_num" },
-  // { title: "sys_wholeness", align: "end", key: "sys_wholeness" },
-  { title: "Score", align: "end", key: "sys_score" },
-  { title: "sys_occ", align: "end", key: "sys_occ" },
-  { title: "Gene ref", align: "end", key: "hit_gene_ref" },
-  { title: "Status", align: "end", key: "hit_status" },
-  { title: "Seq length", align: "end", key: "hit_seq_len" },
-  { title: "i_eval", align: "end", key: "hit_i_eval" },
-  { title: "Score", align: "end", key: "hit_score" },
-  { title: "Profile cov", align: "end", key: "hit_profile_cov" },
-  { title: "Seq cov", align: "end", key: "hit_seq_cov" },
-]);
-
-
-const sanitizedGenes = computed(() => {
-  if (genes.value?.genes) {
-    return genes.value.genes.map(gene => {
-      return { ...gene, model_fqn: gene.model_fqn.split("/").slice(-2).join(" - "), sys_id: gene.sys_id.split(gene.replicon)[1].slice(1) }
-    })
-  }
-  else { return [] }
-})
-
-</script>
-<template>
-  <AnalysisResultDataTable v-if="sanitizedGenes.length > 0" :items="sanitizedGenes" :headers="headers" />
-  <v-card v-else flat color="transparent">
-    <v-card-text>
-      <v-alert type="info" variant="tonal">
-        No gene found
-      </v-alert>
-    </v-card-text>
-  </v-card>
-</template>
diff --git a/frontend/pages/analyses/[analysisId]/systems.vue b/frontend/pages/analyses/[analysisId]/systems.vue
deleted file mode 100644
index 8f3d8fc018f199d6a06e637dc4a897558dc8dc69..0000000000000000000000000000000000000000
--- a/frontend/pages/analyses/[analysisId]/systems.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<script setup lang="ts">
-import { useRoute } from "#imports";
-import type { SystemsOut } from "~/types";
-
-
-const route = useRoute();
-const { data: systems, error } = await useAPI<SystemsOut>(
-  `/analysis/${route.params.analysisId}/systems`
-);
-
-if (error.value) {
-  throw createError({ message: `Error while getting the list systems for analysis ${route.params.analysisId}` })
-}
-
-const headers = ref([
-  { title: "type", align: "end", key: "type", fixed: true, },
-  { title: "System id", align: "end", key: "sys_id" },
-  { title: "subtype", align: "end", key: "subtype" },
-  { title: "sys_beg", align: "end", key: "sys_beg" },
-  { title: "sys_end", align: "end", key: "sys_end" },
-  { title: "protein_in_syst", align: "end", key: "protein_in_syst" },
-]);
-
-const sanitizedSystems = computed(() => {
-  if (systems.value?.systems) {
-    return systems.value.systems.map((system) => {
-      let newSystem = { ...system, protein_in_syst: system.protein_in_syst.split(',') }
-      if (system.type === 'CasFinder') return { ...newSystem, type: 'Cas' }
-      return newSystem
-    })
-  } else { return [] }
-})
-
-</script>
-<template>
-  <AnalysisResultDataTable v-if="sanitizedSystems.length > 0" :items="sanitizedSystems" :headers="headers" />
-  <v-card v-else flat color="transparent">
-    <v-card-text>
-      <v-alert type="info" variant="tonal">
-        No system found
-      </v-alert>
-    </v-card-text>
-  </v-card>
-
-</template>
diff --git a/frontend/types.ts b/frontend/types.ts
index d556ab7274d3371360569ebef10832ffe7af1664..41d7882078c04d2cac65520fcc1f19b888830eeb 100644
--- a/frontend/types.ts
+++ b/frontend/types.ts
@@ -17,9 +17,11 @@ export type JobState = NonTerminalState | TerminalJobState
 export interface Analysis {
     id: number;
     name: string;
+    input_name: string;
     status: JobState;
     percentage_done: number;
     create_time: string
+    from_nt: boolean
 }