diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index aca60563cc1dcba78d665f918a881731253cb52b..dee6d44d4c47aec8dec18603bd41738db512804b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -202,6 +202,8 @@ deploy-back-staging:
   rules:
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
       when: never
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+      when: never
     - if: $CI_COMMIT_BRANCH
       when: always
   variables:
@@ -216,6 +218,8 @@ deploy-front-staging:
   rules:
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
       when: never
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+      when: never
     - if: $CI_COMMIT_BRANCH
       when: always
   variables:
diff --git a/README.md b/README.md
index c8107378dada63bf4e092718fbe13b49905a3a2f..5a6add68b759839d8335b6938de512ab66b94a5b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 ==========
 # Run the server-side FastAPI app
 ## In development mode
+Using the template files, you first need to create backend/.env and backend/cameras.py.
 You can start a virtual environment with the following instructions.
 
 ```sh
@@ -34,6 +35,8 @@ Run the server, `docker-compose up -d`
 
 # Run the client-side React app in a different terminal window:
 
+Using the template file, you first need to create frontend/.env.
+
 ```sh
 $ cd frontend
 $ npm install
@@ -44,6 +47,18 @@ Navigate to [http://localhost:3000](http://localhost:3000)
 
 <br/>  
 
+# Documentation
+
+## Algorithm
+
+The crowd-counting AI model used is based on this repository:
+https://github.com/ZhengPeng7/W-Net-Keras
+The dataset used is ShanghaiTech Part B.
+The model is given a 3-channel image and generates a density map of half the size of the input image.
+The estimated number of people is obtained by summing on all pixels of the density map.
+
+<br/>
+
 # TODO
 ## Coté algo
 - Accéder à d'autre infos telle que l'API des cours sur demande à la DISI pour intégrer ça aux prédictions (ex: cours en promo complète juste implique plus d'attente)
@@ -67,3 +82,9 @@ Navigate to [http://localhost:3000](http://localhost:3000)
 ## Documentation
 - Documenter le projet au maximum
 - Réfléchir à opensourcer (ça peut permettre d'étendre plus facilement à d'autre RU)
+
+## Déploiement
+- Monitorer la VM de prod avec Datadog
+- Écrire un rôle Ansible de déploiement en prod
+- Monitorer la VM de staging avec Datadog
+- Écrire un rôle Ansible de déploiement en staging
diff --git a/backend/cameras.py b/backend/cameras.py
new file mode 100644
index 0000000000000000000000000000000000000000..276a8d2c6b9370c7cbe4b25af81474ee915be18c
--- /dev/null
+++ b/backend/cameras.py
@@ -0,0 +1,43 @@
+restaurants = [
+    {
+        "restaurant": "local",
+        "a_factor": 30,
+        "b_factor": 120,
+        "cameras":
+            [
+                {
+                    "IP": "10.148.38.3",
+                    "user": "viarezocam",
+                    "password": "superponey",
+                    "stream": "stream1",
+                    "mask_points":
+                        [
+                            [
+                                [70, 370],
+                                [420, 720],
+                                [1280, 720],
+                                [1280, 250],
+                                [930, 215],
+                                [450, 550],
+                                [130, 350]
+                            ]
+                        ],
+                    "checkouts":
+                        [
+                            {
+                                "x1": 380,
+                                "x2": 435,
+                                "y1": 740,
+                                "y2": 780
+                            },
+                            {
+                                "x1": 300,
+                                "x2": 350,
+                                "y1": 830,
+                                "y2": 880
+                            }
+                        ]
+                }
+            ]
+    }
+]
diff --git a/backend/cameras.py.template b/backend/cameras.py.template
index 5d197e572134b2e9d63518bcbcd5a227ee42bde0..001fe763574175b9b429e3c475371d012175d57b 100644
--- a/backend/cameras.py.template
+++ b/backend/cameras.py.template
@@ -1,18 +1,25 @@
 restaurants = [
     {
         "restaurant": "local",
-        "a_factor": 30,
-        "b_factor": 120,
+        # the linear function estimating the waiting time t based on the number of people n and the number c of open checkouts is:
+        # t = a * n / c + b
+        "a_factor": 30, # slope
+        "b_factor": 120, # y-intercept
+        # list of all the cameras that correspond to a same restaurant
         "cameras":
             [
-                {
+                {   
+                    # the RTSP url will be: rtsp://user:password@IP:stream
                     "IP": "",
                     "user": "",
                     "password": "",
                     "stream":  "stream1",
+                    # list of the coordinates of the points which constitue the region that should be masked on the picture
                     "mask_points": 
                         [
+                            # polygon which should be part of the mask
                             [
+                                # [x, y] coordinates of each vertex of the polygon (on a 1280*720 picture)
                                 [70, 370],
                                 [420, 720],
                                 [1280, 720],
@@ -22,8 +29,10 @@ restaurants = [
                                 [130, 350]
                             ]
                         ],
-                    "caisses":
+                    # list of the coordinates of each checkout
+                    "checkouts":
                         [
+                            # each checkout corresponds to a rectangle, indicated by two x and two y coordinates
                             {
                                 "x1": 380,
                                 "x2": 435,
diff --git a/backend/db/crud.py b/backend/db/crud.py
index a856d45a090291fc14ce11865323dec349e15d31..853195a6872997706fa95c64a7d6d4b9ba37a8fb 100644
--- a/backend/db/crud.py
+++ b/backend/db/crud.py
@@ -214,7 +214,7 @@ def delete_comment(id: int, db: Session):
 
 # Define CRUD operation for the news
 
-def get_news(place: str, db: Session):
+def get_news(place: str, admin: bool, db: Session):
     """ Get the news for the given place """
     current_date = datetime.now(tz=pytz.timezone("Europe/Paris"))
     news = db.query(
@@ -222,37 +222,50 @@ def get_news(place: str, db: Session):
         models.News.place == place,
         models.News.end_date >= current_date).order_by(
         models.News.published_at.desc()).all()
+    if admin:
+        return news
+
     opening_hours = db.query(
         models.OpeningHours.open_time,
-        models.OpeningHours.close_time).filter(
+        models.OpeningHours.close_time,
+        models.OpeningHours.day).filter(
         models.OpeningHours.place == place,
-        models.OpeningHours.day == current_date.weekday()).order_by(
+        models.OpeningHours.day >= current_date.weekday()).order_by(
+        models.OpeningHours.day,
         models.OpeningHours.open_time).all()
-    next_timetable = None
-    for time_slot in opening_hours:
-        if current_date.time() < time_slot.open_time:
-            next_timetable = time_slot.open_time
+    if not opening_hours:
+        opening_hours = db.query(
+            models.OpeningHours.open_time,
+            models.OpeningHours.close_time,
+            models.OpeningHours.day).filter(
+            models.OpeningHours.place == place).order_by(
+            models.OpeningHours.day,
+            models.OpeningHours.open_time).all()
+    next_time_slot = None
+    for open_time, close_time, day in opening_hours:
+        next_date = current_date + timedelta(days=day - current_date.weekday())
+        if day < current_date.weekday():
+            next_date = next_date + timedelta(days=7)
+            next_time_slot = datetime.combine(next_date.date(), open_time)
             break
-    if not next_timetable:
-        closure = db.query(
-            models.Closure).filter(
-            models.Closure.place == place,
-            models.Closure.beginning_date <= current_date,
-            models.Closure.end_date >= current_date).first()
-    else:
+        if current_date < pytz.timezone("Europe/Paris").localize(datetime.combine(next_date.date(), close_time)):
+            next_time_slot = datetime.combine(next_date.date(), open_time)
+            break
+    if next_time_slot:
         closure = db.query(
             models.Closure).filter(
             models.Closure.place == place,
-            models.Closure.beginning_date <= datetime.combine(current_date.date(), next_timetable),
-            models.Closure.end_date >= datetime.combine(current_date.date(), next_timetable)).first()
-    if closure:
-        closure_news = schemas.News(
-            title="Fermeture exceptionnelle",
-            content=f"{place} est exceptionnellement hors service jusqu'au {closure.end_date.strftime('%d/%m/%y à %Hh%M')}",
-            end_date=closure.end_date,
-            place=place,
-            published_at=closure.beginning_date)
-        news.append(closure_news)
+            models.Closure.beginning_date <= next_time_slot,
+            models.Closure.end_date > next_time_slot).first()
+        if closure:
+            closure_news = schemas.News(
+                title="Fermeture exceptionnelle",
+                content=f"{place} est exceptionnellement hors service jusqu'au {closure.end_date.strftime('%d/%m/%y à %Hh%M')}",
+                end_date=closure.end_date,
+                place=place,
+                published_at=current_date)
+            news.append(closure_news)
+
     return news
 
 
@@ -382,6 +395,7 @@ def update_user(user: schemas.User, user_info: dict, db: Session):
     if existing_user:
         existing_user.cookie = user.cookie
         existing_user.expiration_date = expiration_date
+        existing_user.admin = "admin eatfast" in user_info["roles"]
         db.delete(user)
         db.add(existing_user)
         db.commit()
@@ -390,6 +404,7 @@ def update_user(user: schemas.User, user_info: dict, db: Session):
     else:
         user.username = full_name
         user.expiration_date = expiration_date
+        user.admin = "admin eatfast" in user_info["roles"]
         db.add(user)
         db.commit()
         db.refresh(user)
@@ -531,7 +546,7 @@ def create_closure(closure: schemas.Closure, db: Session):
     db.add(db_closure)
     db.commit()
     db.refresh(db_closure)
-    return schemas.Closure(**closure.dict())
+    return schemas.Closure(**db_closure.__dict__)
 
 
 def delete_closure(id: int, db: Session):
diff --git a/backend/db/models.py b/backend/db/models.py
index c80580bdfbec9ff61ad427397134f7463cdc0e09..1e6ad00d5a1b93a377d7a48501f89194ffa0f781 100644
--- a/backend/db/models.py
+++ b/backend/db/models.py
@@ -1,7 +1,7 @@
 """
 Models of the database for magasin app
 """
-from sqlalchemy import Column, ForeignKey, Integer, DateTime, Float, Interval, String, Text, Time
+from sqlalchemy import Boolean, Column, ForeignKey, Integer, DateTime, Float, Interval, String, Text, Time
 from sqlalchemy.orm import relationship
 
 from db.database import Base
@@ -82,5 +82,6 @@ class Users(Base):
     username = Column(String(50))
     cookie = Column(String(50))
     expiration_date = Column(DateTime)
+    admin = Column(Boolean)
     comments = relationship("Comments")
     comments = relationship("CollaborativeRecords")
diff --git a/backend/db/schemas.py b/backend/db/schemas.py
index 325d2071b40bdcb8c3061058154b8d9ab1c8a74c..757bfbd0dd438b3a0b7252fc301f97400a155621 100644
--- a/backend/db/schemas.py
+++ b/backend/db/schemas.py
@@ -68,7 +68,7 @@ class NewsBase(BaseModel):
 
 class News(NewsBase):
     """Database news base schema"""
-    id: int
+    id: Optional[int]
     published_at: datetime = Field(..., title="Publication date of the news")
 
     class Config:
@@ -138,3 +138,4 @@ class User(BaseModel):
     username: str
     cookie: str
     expiration_date: datetime
+    admin: Optional[bool] = Field(default=False, title="Set to true to allow access to the admin interface")
diff --git a/backend/main.py b/backend/main.py
index ba7a7273e7fbcd7de70ec586c1bd11c248187224..51f0771b85ca32ef4be7f6b1f2acc4ea9b9d4dc1 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,15 +1,20 @@
-from fastapi import FastAPI
+from fastapi import Cookie, Depends, FastAPI
 from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import JSONResponse
+from fastapi.openapi.docs import get_swagger_ui_html
+from fastapi.openapi.utils import get_openapi
+from sqlalchemy.orm import Session
 from dotenv import load_dotenv
 from threading import Thread
 from asyncio import run
 import os
 
-from db import database, models
+from db import database, models, crud
+from db.database import get_db
 from routers import *
 from video_capture import handle_cameras
 
-app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json")
+app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
 
 # load environment variables
 load_dotenv()
@@ -35,6 +40,21 @@ async def on_startup():
     t.start()
 
 
+# Docs OpenAPI
+@app.get("/api/openapi.json")
+async def get_open_api_endpoint(connect_id: str = Cookie(...), db: Session = Depends(get_db)):
+    user = crud.get_user(connect_id, db)
+    if user.admin:
+        return JSONResponse(get_openapi(title="FastAPI", version=1, routes=app.routes))
+
+
+@app.get("/api/docs")
+async def get_documentation(connect_id: str = Cookie(...), db: Session = Depends(get_db)):
+    user = crud.get_user(connect_id, db)
+    if user.admin:
+        return get_swagger_ui_html(openapi_url="/api/openapi.json", title="docs")
+
+
 # Integration of routers
 app.include_router(infos.router)
 app.include_router(records.router)
diff --git a/backend/routers/authentication.py b/backend/routers/authentication.py
index 32ab07f5d4969da1bfb9ad9f3d22dff5498f963f..7f8386b5e8f74c352d84e2b82e39e716c964f1f3 100644
--- a/backend/routers/authentication.py
+++ b/backend/routers/authentication.py
@@ -19,7 +19,7 @@ router = APIRouter(prefix="/api/auth", tags=["auth"])
 @router.get("/")
 async def whoami(connect_id: str = Cookie(...), db: Session = Depends(get_db)):
     user = crud.get_user(connect_id, db)
-    return user.username
+    return {"name": user.username, "admin": user.admin}
 
 
 @router.get("/login")
diff --git a/backend/routers/infos.py b/backend/routers/infos.py
index 16636c4ebb80b20d5bc2d9d897e958188dd10dbe..67b0bfdef28154a11b3821033a93a62d2926ff9e 100644
--- a/backend/routers/infos.py
+++ b/backend/routers/infos.py
@@ -1,4 +1,4 @@
-from fastapi import APIRouter, Depends
+from fastapi import APIRouter, Cookie, Depends, HTTPException
 from sqlalchemy.orm import Session
 from typing import List
 
@@ -34,8 +34,12 @@ async def get_closure(place: str, db: Session = Depends(get_db)):
 
 
 @router.post('/closure', response_model=schemas.Closure)
-async def create_closure(closure: schemas.Closure, db: Session = Depends(get_db)):
-    return crud.create_closure(closure, db)
+async def create_closure(closure: schemas.ClosureBase, connect_id: str = Cookie(...), db: Session = Depends(get_db)):
+    user = crud.get_user(connect_id, db)
+    if user.admin:
+        return crud.create_closure(closure, db)
+    else:
+        raise HTTPException(status_code=403, detail="Administrator privilege required")
 
 
 @router.delete('/closure/{id}', response_model=None)
diff --git a/backend/routers/news.py b/backend/routers/news.py
index 7c4b8136fda0bd567297b83c6c41d859399452b6..1c1183a675f6cc570e9e73912c0408aabe035a84 100644
--- a/backend/routers/news.py
+++ b/backend/routers/news.py
@@ -1,4 +1,4 @@
-from fastapi import APIRouter, Depends
+from fastapi import APIRouter, Cookie, Depends, HTTPException
 from sqlalchemy.orm import Session
 from typing import List
 import json
@@ -12,17 +12,25 @@ router = APIRouter(prefix="/api", tags=["news"])
 
 
 @router.get('/{place}/news', response_model=List[schemas.News])
-async def get_news(place: str, db: Session = Depends(get_db)):
-    return crud.get_news(place, db)
+async def get_news(place: str, admin: bool = False, db: Session = Depends(get_db)):
+    return crud.get_news(place, admin, db)
 
 
 @router.post('/news', response_model=schemas.News)
-async def create_news(news: schemas.NewsBase, db: Session = Depends(get_db)):
-    saved_news = crud.create_news(news, db)
-    await manager.broadcast(json.dumps({"type": "news", "comment": saved_news.__dict__}, default=str))
-    return saved_news
+async def create_news(news: schemas.NewsBase, connect_id: str = Cookie(...), db: Session = Depends(get_db)):
+    user = crud.get_user(connect_id, db)
+    if user.admin:
+        saved_news = crud.create_news(news, db)
+        await manager.broadcast(json.dumps({"type": "news", "comment": saved_news.__dict__}, default=str))
+        return saved_news
+    else:
+        raise HTTPException(status_code=403, detail="Administrator privilege required")
 
 
 @router.delete('/news/{id}', response_model=None)
-async def delete_news(id: int, db: Session = Depends(get_db)):
-    return crud.delete_news(id, db)
+async def delete_news(id: int, connect_id: str = Cookie(...), db: Session = Depends(get_db)):
+    user = crud.get_user(connect_id, db)
+    if user.admin:
+        return crud.delete_news(id, db)
+    else:
+        raise HTTPException(status_code=403, detail="Administrator privilege required")
diff --git a/backend/video_capture.py b/backend/video_capture.py
index 8a9e78763a0542f0aea4b1721c576ce2aaac8e3c..66eafc8c784c757f3df2b2aaf1fc90101d763376 100644
--- a/backend/video_capture.py
+++ b/backend/video_capture.py
@@ -36,6 +36,7 @@ async def handle_cameras():
     db = SessionLocal()
     for restaurant in restaurants:
         for camera in restaurant["cameras"]:
+            # each camera masked is constructed based on the given coordiantes and saved
             mask = np.zeros((720, 1280, 3), dtype=np.float32)
             cv2.fillPoly(mask, np.array(camera["mask_points"]), (255, 255, 255))
             camera["mask"] = mask
@@ -47,6 +48,7 @@ async def handle_cameras():
 
         for restaurant in restaurants:
 
+            # we check whether or not the restaurant is opened
             is_open = db.query(
                 models.OpeningHours).filter(
                 models.OpeningHours.place == restaurant["restaurant"],
@@ -55,13 +57,15 @@ async def handle_cameras():
                 models.OpeningHours.close_time >= current_time).first() is not None
 
             if is_open:
-                count_prediction = 0
-                open_checkouts = 0
-                cams_working = True
+                count_prediction = 0  # estimated number of people in the restaurant
+                open_checkouts = 0  # estimated number of open checkouts in the restaurant
+                cams_working = True  # boolean indicating whether or not all cams are working in the restaurant
 
                 for camera in restaurant["cameras"]:
+                    # connection to the rtsp stream
                     cap = cv2.VideoCapture(f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}')
                     if cams_working and cap.isOpened():
+                        # extraction and preoprocessing of the first frame
                         _, frame = cap.read()
                         masked_img = cv2.bitwise_and(
                             frame.astype(np.float32), camera["mask"])
@@ -72,16 +76,19 @@ async def handle_cameras():
                                     np.array(
                                         [treated_img]))),
                             axis=0)
+                        # getting the density map from the model and the number of people
                         pred_map = np.squeeze(make_prediction(input_image))
                         count_prediction += np.sum(pred_map)
-                        for caisse in camera["caisses"]:
-                            if np.sum(pred_map[caisse["x1"] // 2:caisse["x2"] // 2, caisse["y1"] // 2:caisse["y2"] // 2]) > 0.5:
+                        for checkout in camera["checkouts"]:
+                            # we check whether or not the density in the checkout area is high enough to determine if it is open or not
+                            if np.sum(pred_map[checkout["x1"] // 2:checkout["x2"] // 2, checkout["y1"] // 2:checkout["y2"] // 2]) > 0.5:
                                 open_checkouts += 1
                     else:
                         cams_working = False
                     cap.release()
 
                 if cams_working and open_checkouts:
+                    # the estimated waiting time is calculated and put in the database
                     waiting_time = timedelta(
                         seconds=restaurant['b_factor'] +
                         int(count_prediction *
@@ -94,4 +101,5 @@ async def handle_cameras():
                     db.add(db_record)
                     db.commit()
                     await manager.broadcast(json.dumps({"type": "data"}))
+        # sleeping enough time so that there is a 60 second delay between the start of two consecutive loops
         time.sleep(max(0, 60 - (datetime.now() - current_date).total_seconds()))
diff --git a/frontend/src/components/ClosureForm.js b/frontend/src/components/ClosureForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..f7e15b6ae930f45a47724f5a7195e826ae7a93ea
--- /dev/null
+++ b/frontend/src/components/ClosureForm.js
@@ -0,0 +1,57 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+
+export default function ClosureForm() {
+  const [places, setPlaces] = useState([]);
+
+  const fetchPlaces = () => {
+    axios
+      .get(`${process.env.REACT_APP_BASE_URL_BACK}/restaurants`)
+      .then((res) => {
+        setPlaces(res.data);
+      })
+      .catch((e) => {
+        console.log(e);
+      });
+  };
+
+  const Submit = (ev) => {
+    ev.preventDefault();
+    let { place, beginning_date, end_date } = ev.target.elements;
+
+    axios
+      .post(`${process.env.REACT_APP_BASE_URL_BACK}/closure`, {
+        place: place.value,
+        beginning_date: beginning_date.value,
+        end_date: end_date.value,
+      })
+      .then((res) => {
+        console.log(res);
+      })
+      .catch((e) => {
+        console.log(e);
+      });
+  };
+
+  useEffect(fetchPlaces, []);
+
+  return (
+    <form onSubmit={Submit} id="news-form-container">
+      <label htmlFor="place">Restaurant concerné</label>
+      <select name="place">
+        {places.map((place) => (
+          <option key={place.name}>{place.name}</option>
+        ))}
+      </select>
+      <label htmlFor="beginning_date" className="news-form-label">
+        Date de fermeture
+      </label>
+      <input type="datetime-local" name="beginning_date" style={{ minHeight: "2rem" }} />
+      <label htmlFor="end_date" className="news-form-label">
+        Date de réouverture
+      </label>
+      <input type="datetime-local" name="end_date" style={{ minHeight: "2rem" }} />
+      <input id="submit_event" type="submit" className="news-form-label" />
+    </form>
+  );
+}
diff --git a/frontend/src/components/Comments.js b/frontend/src/components/Comments.js
index a3a13046f98e55ee8f203236941ba282c56c070a..09f7589a40b0f4479746c9b9bbcc503598248c7e 100644
--- a/frontend/src/components/Comments.js
+++ b/frontend/src/components/Comments.js
@@ -1,6 +1,6 @@
 import React, { useContext, useEffect, useRef, useState } from "react";
 import axios from "axios";
-import { AiOutlineInfoCircle } from "react-icons/ai";
+import { AiOutlineInfoCircle, AiOutlineDelete } from "react-icons/ai";
 import { BiSend } from "react-icons/bi";
 import { BsChatText } from "react-icons/bs";
 
@@ -9,7 +9,7 @@ import { getSiblings } from "../utils";
 
 import "../styles/Comments.css";
 
-export default function Messages({ place, infos, lastMessage }) {
+export default function Messages({ place, infos, lastMessage, admin }) {
   const [user] = useContext(User);
   const [messages, setMessages] = useState([]);
   const [newComment, setNewComment] = useState("");
@@ -35,11 +35,29 @@ export default function Messages({ place, infos, lastMessage }) {
         })
         .catch((e) => {
           console.log(e);
+          alert("Une erreur est survenue");
           updateValue("");
         });
     }
   };
 
+  const Delete = (ev) => {
+    if (infos && admin) {
+      ev.preventDefault();
+      axios
+        .delete(`${process.env.REACT_APP_BASE_URL_BACK}/news/${ev.target.id}`, {
+          withCredentials: true,
+        })
+        .then(() => {
+          setLoading(true);
+        })
+        .catch((e) => {
+          alert("Une erreur est survenue");
+          console.log(e);
+        });
+    }
+  };
+
   const updateValue = (value) => {
     setNewComment(value);
     if (input.current) {
@@ -81,7 +99,7 @@ export default function Messages({ place, infos, lastMessage }) {
       .get(
         `${process.env.REACT_APP_BASE_URL_BACK}/${encodeURIComponent(place)}/${
           infos ? "news" : "comments"
-        }`,
+        }${admin ? "?admin=true" : ""}`,
       )
       .then((res) => {
         setMessages(res.data);
@@ -95,7 +113,7 @@ export default function Messages({ place, infos, lastMessage }) {
         console.log(e);
         setLoading(false);
       });
-  }, []);
+  }, [place, loading]);
 
   useEffect(() => {
     if (chat.current) {
@@ -127,20 +145,22 @@ export default function Messages({ place, infos, lastMessage }) {
   }, [lastMessage]);
 
   return (
-    <div className="comments-side-bar">
-      <div className="comments-title">
-        {infos ? (
-          <>
-            <AiOutlineInfoCircle id="comments-icon-left" onClick={OpenSideBar} />
-            Infos
-          </>
-        ) : (
-          <>
-            <BsChatText id="comments-icon-right" onClick={OpenSideBar} />
-            Commentaires
-          </>
-        )}
-      </div>
+    <div className={`comments-side-bar ${admin ? "comments-full-size" : ""}`}>
+      {!admin && (
+        <div className="comments-title">
+          {infos ? (
+            <>
+              <AiOutlineInfoCircle id="comments-icon-left" onClick={OpenSideBar} />
+              Infos
+            </>
+          ) : (
+            <>
+              <BsChatText id="comments-icon-right" onClick={OpenSideBar} />
+              Commentaires
+            </>
+          )}
+        </div>
+      )}
       <div ref={chat} className={`comments-scroll-bar ${infos && "infos-scroll-bar"}`}>
         {!messages.length ? (
           loading ? (
@@ -162,6 +182,13 @@ export default function Messages({ place, infos, lastMessage }) {
             return (
               <div key={index} className="comment">
                 <div className={`comment-title${infos ? "-infos" : ""}`}>
+                  {admin && (
+                    <AiOutlineDelete
+                      id={message.id}
+                      onClick={Delete}
+                      className="comment-delete-button"
+                    />
+                  )}
                   {infos ? message.title : message.username}
                 </div>
                 <div className="comment-content">{message.content}</div>
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index b3ab6f54adf318f7a5b1ddd484107eec09de05d8..2a2ccad3ced779d57fd6e0632b79929c6e180828 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -19,7 +19,7 @@ export default function Header({ selection, setSelection }) {
         .get(`${process.env.REACT_APP_BASE_URL_BACK}/auth`, { withCredentials: true })
         .then((res) => {
           setUser(res.data);
-          localStorage.setItem("user", res.data);
+          localStorage.setItem("user", JSON.stringify(res.data));
         })
         .catch((e) => console.log(e));
     }
diff --git a/frontend/src/components/NewsForm.js b/frontend/src/components/NewsForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..5ee9350c86061291ce6f01e725addfe56430b16e
--- /dev/null
+++ b/frontend/src/components/NewsForm.js
@@ -0,0 +1,70 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+
+import "../styles/NewsForm.css";
+
+export default function NewsForm() {
+  const [places, setPlaces] = useState([]);
+
+  const fetchPlaces = () => {
+    axios
+      .get(`${process.env.REACT_APP_BASE_URL_BACK}/restaurants`)
+      .then((res) => {
+        setPlaces(res.data);
+      })
+      .catch((e) => {
+        console.log(e);
+      });
+  };
+
+  const Submit = (ev) => {
+    ev.preventDefault();
+    let { title, content, end_date, place } = ev.target.elements;
+
+    axios
+      .post(
+        `${process.env.REACT_APP_BASE_URL_BACK}/news`,
+        {
+          title: title.value,
+          content: content.value,
+          end_date: end_date.value,
+          place: place.value,
+        },
+        { withCredentials: true },
+      )
+      .then(() => {
+        ev.target.reset();
+        alert("L'actualité a bien été postée !");
+      })
+      .catch((e) => {
+        console.log(e);
+        alert("Une erreur est survenue");
+      });
+  };
+
+  useEffect(fetchPlaces, []);
+
+  return (
+    <form onSubmit={Submit} id="news-form-container">
+      <label htmlFor="title">Titre</label>
+      <input name="title" />
+      <label htmlFor="content" className="news-form-label">
+        Contenu
+      </label>
+      <textarea name="content" style={{ resize: "none" }} />
+      <label htmlFor="end_date" className="news-form-label">
+        Date de fin
+      </label>
+      <input type="datetime-local" name="end_date" style={{ minHeight: "2rem" }} />
+      <label htmlFor="place" className="news-form-label">
+        Restaurant concerné
+      </label>
+      <select name="place">
+        {places.map((place) => (
+          <option key={place.name}>{place.name}</option>
+        ))}
+      </select>
+      <input id="submit_event" type="submit" className="news-form-label" />
+    </form>
+  );
+}
diff --git a/frontend/src/components/index.js b/frontend/src/components/index.js
index de9175be93666cca57812a64f53e7e8eaf74791a..2bad7f39f6c30e3a0a3ac924aa0059b6fede5e10 100644
--- a/frontend/src/components/index.js
+++ b/frontend/src/components/index.js
@@ -3,3 +3,5 @@ export { default as Footer } from "./Footer";
 export { default as WaitingTime } from "./WaitingTime";
 export { default as Graph } from "./Graph";
 export { default as Comments } from "./Comments";
+export { default as NewsForm } from "./NewsForm";
+export { default as ClosureForm } from "./ClosureForm";
diff --git a/frontend/src/index.js b/frontend/src/index.js
index a3108a326bcf5590db5ae45e66cd20f96d037783..293bcb9a7ffdfbc2b0479872d126be4d8f05235e 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -5,7 +5,7 @@ import useWebSocket from "react-use-websocket";
 import axios from "axios";
 
 import { Footer, Header } from "./components";
-import { HomePage, RestaurantPage, NotFoundPage } from "./views";
+import { HomePage, RestaurantPage, NotFoundPage, AdminPage } from "./views";
 
 import "bootstrap/dist/css/bootstrap.min.css";
 import "./styles/index.css";
@@ -14,11 +14,12 @@ const socketUrl = `${process.env.REACT_APP_SOCKET_URL}/ws`;
 export const User = createContext(null);
 
 export default function App() {
+  const storedSession = localStorage.getItem("user");
   const [restaurantsList, setRestaurantsList] = useState([]);
   const [selection, setSelection] = useState(null);
   const [loading, setLoading] = useState(true);
   const [reload, setReload] = useState(true);
-  const [user, setUser] = useState(localStorage.getItem("user"));
+  const [user, setUser] = useState(storedSession ? JSON.parse(storedSession) : null);
   const { lastMessage } = useWebSocket(socketUrl, {
     shouldReconnect: () => true,
   });
@@ -78,6 +79,9 @@ export default function App() {
                 path="/"
                 element={<HomePage {...{ restaurantsList, setSelection, loading }} />}
               />
+              {user?.admin && (
+                <Route path="/admin" element={<AdminPage lastmessage={lastMessage} />} />
+              )}
               <Route
                 path="/:restaurant"
                 element={<RestaurantPage {...{ selection, setSelection, lastMessage }} />}
diff --git a/frontend/src/styles/Admin.css b/frontend/src/styles/Admin.css
new file mode 100644
index 0000000000000000000000000000000000000000..b49874f5277879ec05928fcedba76c7c726c0397
--- /dev/null
+++ b/frontend/src/styles/Admin.css
@@ -0,0 +1,31 @@
+#admin-container {
+    width: 100%; 
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 1rem;
+}
+
+#admin-news-list {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 40rem;
+    max-width: 90%;
+    height: 100%;
+    border: 1px solid white;
+    padding: 1rem;
+    margin: 1rem;
+    margin-top: 2rem;
+    overflow-y: auto;
+}
+
+.admin-select-option {
+    font-style: 1.2rem;
+    margin-bottom: 1rem;
+}
+
+#admin-select-place {
+    width: fit-content;
+}
\ No newline at end of file
diff --git a/frontend/src/styles/Comments.css b/frontend/src/styles/Comments.css
index d52fe85b8f500b3991b1773e839b72127013b9d7..492205d4012b862c51bb121517d6088918bfcd35 100644
--- a/frontend/src/styles/Comments.css
+++ b/frontend/src/styles/Comments.css
@@ -32,6 +32,10 @@
     overflow: hidden;
 }
 
+.comments-full-size {
+    width: 100%;
+}
+
 .comments-scroll-bar {
     flex: 1;
     display: flex;
@@ -119,6 +123,10 @@
     font-size : 1.2rem;
 }
 
+.comment-delete-button { 
+    margin-left: 1rem;
+}
+
 @media only screen and (max-width: 600px) {
     .comments-side-bar {
         width: 0px;
diff --git a/frontend/src/styles/NewsForm.css b/frontend/src/styles/NewsForm.css
new file mode 100644
index 0000000000000000000000000000000000000000..98412d5a357ea3bc547c0e5fc7927b89f93e4984
--- /dev/null
+++ b/frontend/src/styles/NewsForm.css
@@ -0,0 +1,23 @@
+#news-form-container {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    width: 40rem;
+    max-width: 90%;
+    height: 100%;
+    border: 1px solid white;
+    padding: 1rem;
+    margin: 1rem;
+    margin-top: 2rem;
+    overflow-y: auto;
+}
+
+.news-form-label {
+    margin-top: 1rem;
+}
+
+input::placeholder {
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+}
\ No newline at end of file
diff --git a/frontend/src/views/AdminPage.js b/frontend/src/views/AdminPage.js
new file mode 100644
index 0000000000000000000000000000000000000000..43678b20224a86e0a56f2da23bcc83b1c25feb3e
--- /dev/null
+++ b/frontend/src/views/AdminPage.js
@@ -0,0 +1,66 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+
+import { NewsForm, ClosureForm, Comments } from "../components";
+
+import "../styles/Admin.css";
+
+export default function AdminPage({ lastMessage }) {
+  const [form, setForm] = useState("");
+
+  function NewsList() {
+    const [place, setPlace] = useState(null);
+    const [places, setPlaces] = useState([]);
+
+    const fetchPlaces = () => {
+      axios
+        .get(`${process.env.REACT_APP_BASE_URL_BACK}/restaurants`)
+        .then((res) => {
+          setPlaces(res.data);
+          res.data.length && setPlace(res.data[0].name);
+        })
+        .catch((e) => {
+          console.log(e);
+        });
+    };
+
+    useEffect(fetchPlaces, []);
+
+    return (
+      <div id="admin-news-list">
+        <select id="admin-select-place" name="place" onChange={(ev) => setPlace(ev.target.value)}>
+          {places.map((place) => (
+            <option key={place.name}>{place.name}</option>
+          ))}
+        </select>
+        <Comments place={place} lastMessage={lastMessage} infos admin />
+      </div>
+    );
+  }
+
+  let forms = {
+    news: <NewsForm />,
+    newsList: <NewsList />,
+    closure: <ClosureForm />,
+  };
+
+  return (
+    <div id="admin-container">
+      <select defaultValue="" onChange={(ev) => setForm(ev.target.value)}>
+        <option className="admin-select-option" value="">
+          -- Sélectionner une action --
+        </option>
+        <option className="admin-select-option" value="news">
+          Ajouter une actualité
+        </option>
+        <option className="admin-select-option" value="newsList">
+          Supprimer une actualité
+        </option>
+        <option className="admin-select-option" value="closure">
+          Renseigner une fermeture exceptionnelle
+        </option>
+      </select>
+      {form && forms[form]}
+    </div>
+  );
+}
diff --git a/frontend/src/views/index.js b/frontend/src/views/index.js
index 77ab82fcd6c65ec32cf59292d0e7eea5cb8681cc..6cbe89865e52890313a19dccc5eba29cff61f8d3 100644
--- a/frontend/src/views/index.js
+++ b/frontend/src/views/index.js
@@ -1,3 +1,4 @@
 export { default as HomePage } from "./HomePage";
 export { default as RestaurantPage } from "./Restaurant";
 export { default as NotFoundPage } from "./NotFoundPage";
+export { default as AdminPage } from "./AdminPage";