diff --git a/web/components/management/area.js b/web/components/management/area.js
index f13e766db58fb963e0b0160fe75f420f1c5ae9f7..12d14334733315178f6d326d9886b8c3a1d242bd 100644
--- a/web/components/management/area.js
+++ b/web/components/management/area.js
@@ -329,4 +329,58 @@ class Area {
     document.getElementById("desk-new").setAttribute("disabled", "true");
     document.getElementById("section-new").setAttribute("disabled", "true");
   }
+
+  async cloneAreas(electionCloned, electionToClone) {
+    let areaHandler = this;
+    await electionToClone.Areas.forEach(async (areaToClone) => {
+      try {
+        areaToClone = await areaHandler.getArea(areaToClone.ID);
+        const response = await fetch("/api/Area/", {
+          method: "POST",
+          headers: new Headers({
+            "XSRF-Token": current_user.xsrftoken,
+          }),
+          body: JSON.stringify({
+            ID: null,
+            ElectionID: electionCloned.ID,
+            Name: areaToClone.Name,
+            SeatNumber: areaToClone.SeatNumber,
+          }),
+        });
+        if (response.status !== 200) {
+          throw new Error(
+            `Area could not be cloned (status ${response.status})`
+          );
+        }
+        let areaCloned = await response.json();
+        await areaHandler.parent.sectionHandler.cloneSections(
+          areaCloned,
+          areaToClone
+        );
+      } catch (e) {
+        Messages.Show("is-warning", e.message);
+        console.error(e);
+      }
+    });
+  }
+
+  async getArea(id) {
+    try {
+      const response = await fetch("/api/Area/" + id, {
+        method: "GET",
+        headers: new Headers({
+          "XSRF-Token": current_user.xsrftoken,
+        }),
+      });
+      if (response.status !== 200) {
+        throw new Error(
+          `Area could not be fetched (status ${response.status})`
+        );
+      }
+      return await response.json();
+    } catch (e) {
+      Messages.Show("is-warning", e.message);
+      console.error(e);
+    }
+  }
 }
diff --git a/web/components/management/desk.js b/web/components/management/desk.js
index c009bde58e089e7c4b0d95dff20c2526ae9b4145..9f6418ae4e303327270a99ca8116515efbcc0800 100644
--- a/web/components/management/desk.js
+++ b/web/components/management/desk.js
@@ -294,4 +294,33 @@ class Desk {
     }
     this.displayDesks();
   }
+
+  async cloneDesks(sectionCloned, sectionToClone) {
+    let deskHandler = this;
+    await sectionToClone.Desks.forEach(async (deskToClone) => {
+      try {
+        const response = await fetch("/api/Desk/", {
+          method: "POST",
+          headers: new Headers({
+            "XSRF-Token": current_user.xsrftoken,
+          }),
+          body: JSON.stringify({
+            ID: null,
+            SectionID: sectionCloned.ID,
+            Name: deskToClone.Name,
+            Subscribed: deskToClone.Subscribed,
+            WitnessDesk: deskToClone.WitnessDesk,
+          }),
+        });
+        if (response.status !== 200) {
+          throw new Error(
+            `Desk could not be cloned (status ${response.status})`
+          );
+        }
+      } catch (e) {
+        Messages.Show("is-warning", e.message);
+        console.error(e);
+      }
+    });
+  }
 }
diff --git a/web/components/management/election.js b/web/components/management/election.js
index ffbcb2a0bec474d37b4075e2efb316668af71c15..6a33f955ce42797230a1e0c4c34a6a435e328af9 100644
--- a/web/components/management/election.js
+++ b/web/components/management/election.js
@@ -40,6 +40,7 @@ class Election {
     `;
     current_user = await Auth.GetUser();
     this.mountModal("election-modal");
+    this.mountCloneModal("election-clone-modal");
     this.handleDom();
     this.displayElections();
   }
@@ -67,6 +68,21 @@ class Election {
       .addEventListener("click", async function () {
         await electionHandler.saveElection();
       });
+    document
+      .getElementById(`election-clone-modal-close`)
+      .addEventListener("click", function () {
+        Common.toggleModal("election-clone-modal", "election-clone-modal-card");
+      });
+    document
+      .getElementById(`election-clone-modal-cancel`)
+      .addEventListener("click", function () {
+        Common.toggleModal("election-clone-modal", "election-clone-modal-card");
+      });
+    document
+      .getElementById(`election-clone-modal-save`)
+      .addEventListener("click", async function () {
+        electionHandler.saveCloneElection();
+      });
     document
       .getElementById(`election-modal-save-area`)
       .addEventListener("click", async function () {
@@ -133,6 +149,63 @@ class Election {
     `;
   }
 
+  mountCloneModal(where) {
+    const mountpoint = where;
+    document.getElementById(mountpoint).innerHTML = /* HTML */ `
+      <div class="modal-background"></div>
+      <div class="modal-card" id="election-clone-modal-card">
+        <header class="modal-card-head">
+          <p class="modal-card-title">Cloner une élection</p>
+          <button
+            class="delete"
+            aria-label="close"
+            id="election-clone-modal-close"
+          ></button>
+        </header>
+        <section class="modal-card-body">
+          <div class="field">
+            <label>Id</label>
+            <div class="control">
+              <input
+                class="input"
+                type="number"
+                id="election-modal-id-clone"
+                disabled
+              />
+            </div>
+          </div>
+          <div class="field">
+            <label>Nom</label>
+            <div class="control">
+              <input class="input" type="text" id="election-modal-name-clone" />
+            </div>
+          </div>
+          <div class="field">
+            <label>Système de vote</label><br />
+            <div class="control select">
+              <select name="ballot-type" id="election-modal-ballot-type-clone">
+                <option value="local-counsil-direct"
+                  >Conseil communautaire au suffrage direct</option
+                >
+                <option value="local-counsil-indirect"
+                  >Conseil communautaire au suffrage indirect</option
+                >
+              </select>
+            </div>
+          </div>
+        </section>
+        <footer class="modal-card-foot">
+          <button id="election-clone-modal-save" class="button is-success">
+            Dupliquer
+          </button>
+          <button id="election-clone-modal-cancel" class="button">
+            Annuler
+          </button>
+        </footer>
+      </div>
+    `;
+  }
+
   async displayElections() {
     let elections = await this.updateElections();
     const markup = elections
@@ -147,6 +220,11 @@ class Election {
         .addEventListener("click", function () {
           electionHandler.editElection(election);
         });
+      document
+        .getElementById(`elections-election-clone-${election.ID}`)
+        .addEventListener("click", function () {
+          electionHandler.cloneElection(election);
+        });
       document
         .getElementById(`elections-election-delete-${election.ID}`)
         .addEventListener("click", function () {
@@ -187,6 +265,15 @@ class Election {
                   <i class="fas fa-pen"></i>
                 </span>
               </a>
+              <a
+                id="elections-election-clone-${election.ID}"
+                class="button is-link is-small"
+                title="Dupliquer"
+              >
+                <span class="icon is-small">
+                  <i class="fas fa-clone"></i>
+                </span>
+              </a>
               <a
                 id="elections-election-delete-${election.ID}"
                 class="button is-danger is-small"
@@ -259,7 +346,6 @@ class Election {
       if (response.status == 409) {
         throw new Error(
           `The name of the election already exist (status ${response.status})`
-
         );
       }
       if (response.status !== 200) {
@@ -272,7 +358,7 @@ class Election {
     } catch (e) {
       Messages.Show("is-warning", e.message);
       console.error(e);
-      return
+      return;
     }
     Common.toggleModal("election-modal", "election-modal-card");
     this.activateElection(election);
@@ -326,4 +412,77 @@ class Election {
     document.getElementById("section-new").setAttribute("disabled", "true");
     document.getElementById("area-new").setAttribute("disabled", "true");
   }
+
+  cloneElection(election) {
+    Common.toggleModal("election-clone-modal", "election-clone-modal-card");
+    document.getElementById("election-modal-id-clone").value = election.ID;
+    document.getElementById("election-modal-name-clone").value =
+      election.Name + " (New)";
+    document.getElementById("election-modal-ballot-type-clone").value =
+      election.BallotType;
+  }
+
+  async saveCloneElection() {
+    let electionCloned;
+    let electionToClone = await this.getElection(
+      document.getElementById("election-modal-id-clone").value
+    );
+
+    try {
+      const response = await fetch("/api/Election/", {
+        method: "POST",
+        headers: new Headers({
+          "XSRF-Token": current_user.xsrftoken,
+        }),
+        body: JSON.stringify({
+          ID: null,
+          Name: document.getElementById("election-modal-name-clone").value,
+          BallotType: document.getElementById(
+            "election-modal-ballot-type-clone"
+          ).value,
+        }),
+      });
+      if (response.status == 409) {
+        throw new Error(
+          `The name of the election already exist (status ${response.status})`
+        );
+      }
+      if (response.status !== 200) {
+        throw new Error(
+          `Election could not be cloned (status ${response.status})`
+        );
+      }
+      electionCloned = await response.json();
+    } catch (e) {
+      Messages.Show("is-warning", e.message);
+      console.error(e);
+      return;
+    }
+    await this.parent.areaHandler.cloneAreas(electionCloned, electionToClone);
+    Common.toggleModal("election-clone-modal", "election-clone-modal-card");
+    await this.displayElections();
+    await this.activateElection(electionCloned);
+    this.parent.areaHandler.election = electionCloned;
+    await this.parent.areaHandler.displayAreas();
+  }
+
+  async getElection(id) {
+    try {
+      const response = await fetch("/api/Election/" + id, {
+        method: "GET",
+        headers: new Headers({
+          "XSRF-Token": current_user.xsrftoken,
+        }),
+      });
+      if (response.status !== 200) {
+        throw new Error(
+          `Election could not be fetched (status ${response.status})`
+        );
+      }
+      return await response.json();
+    } catch (e) {
+      Messages.Show("is-warning", e.message);
+      console.error(e);
+    }
+  }
 }
diff --git a/web/components/management/genericElection.js b/web/components/management/genericElection.js
index 21b09fa18c83c588763311dfd79d836e6fe72287..0b54e17e5ffd1a199d7079e39387237bbac60891 100644
--- a/web/components/management/genericElection.js
+++ b/web/components/management/genericElection.js
@@ -37,6 +37,7 @@ class GenericElection {
       </div>
 
       <div class="modal" id="election-modal"></div>
+      <div class="modal" id="election-clone-modal"></div>
       <div class="modal" id="area-modal"></div>
       <div class="modal" id="section-modal"></div>
       <div class="modal" id="desk-modal"></div>
diff --git a/web/components/management/section.js b/web/components/management/section.js
index c6a08c3c4d7268a6b8e81cc6ddfc1797a7822123..7f7c6e265a51eec70deb6cba7a5ffab9d0da977f 100644
--- a/web/components/management/section.js
+++ b/web/components/management/section.js
@@ -323,4 +323,57 @@ class Section {
     this.parent.deskHandler.emptyDesks();
     document.getElementById("desk-new").setAttribute("disabled", "true");
   }
+
+  async cloneSections(areaCloned, areaToClone) {
+    let sectionHandler = this;
+    await areaToClone.Sections.forEach(async (sectionToClone) => {
+      try {
+        sectionToClone = await sectionHandler.getSection(sectionToClone.ID);
+        const response = await fetch("/api/Section/", {
+          method: "POST",
+          headers: new Headers({
+            "XSRF-Token": current_user.xsrftoken,
+          }),
+          body: JSON.stringify({
+            ID: null,
+            AreaID: areaCloned.ID,
+            Name: sectionToClone.Name,
+          }),
+        });
+        if (response.status !== 200) {
+          throw new Error(
+            `Section could not be cloned (status ${response.status})`
+          );
+        }
+        let sectionCloned = await response.json();
+        await sectionHandler.parent.deskHandler.cloneDesks(
+          sectionCloned,
+          sectionToClone
+        );
+      } catch (e) {
+        Messages.Show("is-warning", e.message);
+        console.error(e);
+      }
+    });
+  }
+
+  async getSection(id) {
+    try {
+      const response = await fetch("/api/Section/" + id, {
+        method: "GET",
+        headers: new Headers({
+          "XSRF-Token": current_user.xsrftoken,
+        }),
+      });
+      if (response.status !== 200) {
+        throw new Error(
+          `Section could not be fetched (status ${response.status})`
+        );
+      }
+      return await response.json();
+    } catch (e) {
+      Messages.Show("is-warning", e.message);
+      console.error(e);
+    }
+  }
 }
diff --git a/web/style.css b/web/style.css
index 2267274a1b80fa2ba7c498da1e460938909f241c..2d78c889105e00db119eab187ff61bbca757b9d1 100644
--- a/web/style.css
+++ b/web/style.css
@@ -103,4 +103,8 @@ img {
   background-color: rgba(55,122,195,.95);
   font-weight: bold;
   color: white;
+}
+
+.card-content .level-left {
+  flex-basis: 70%;
 }
\ No newline at end of file