diff --git a/app/routes/links_routes.py b/app/routes/links_routes.py
index 90ca1bd..984ccd8 100644
--- a/app/routes/links_routes.py
+++ b/app/routes/links_routes.py
@@ -61,7 +61,7 @@ async def create_link(
return {"link": link_path, "expire_date": new_link.expire_date}
-@router.delete("/{link}", summary="Delete a link")
+@router.delete("/{link}", summary="Delete a link and all associated logs")
async def delete_link(
link: Annotated[str, Path(title="Link to delete")],
current_user: Annotated[User, Depends(get_current_user)],
@@ -126,9 +126,13 @@ async def get_link_logs(
return logs
-@router.delete("/{link}/logs", summary="Delete logs associated with a link")
-async def delete_link_logs(
- link: Annotated[str, Path(title="Link to delete logs for")],
+@router.delete(
+ "/{link}/logs/{log_id}",
+ summary="Delete a specific log associated with a link",
+)
+async def delete_single_log(
+ link: Annotated[str, Path(title="Link associated with the log to delete")],
+ log_id: Annotated[int, Path(title="Log ID to delete")],
current_user: Annotated[User, Depends(get_current_user)],
db=Depends(get_db),
):
@@ -148,10 +152,13 @@ async def delete_link_logs(
detail="Link not associated with your account",
)
- # Get all of the logs
- logs = db.query(Log).filter(Log.link == link.link).all()
- for log in logs:
- db.delete(log)
+ # Get the log and delete it
+ log = db.query(Log).filter(Log.id == log_id).first()
+ if not log:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND, detail="Log not found"
+ )
+ db.delete(log)
db.commit()
return status.HTTP_204_NO_CONTENT
diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html
index c65b62b..0a383af 100644
--- a/app/templates/dashboard.html
+++ b/app/templates/dashboard.html
@@ -4,46 +4,19 @@
LinkLogger | Dashboard
+
+
-
+
- Link |
- Visits |
- Redirect |
- Expire Date |
+ Link |
+ Visits |
+ Redirect |
+ Expire Date |
-
-
-
-
-
-
-
-
@@ -58,68 +31,62 @@
}
div {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- text-align: center;
- font-size: 25px;
- color: #ccc;
+ display: flex;
+ justify-content: center;
+ margin-top: 100px;
}
table {
- border-collapse: collapse;
- background-color: #333;
+ margin: 20px 0 20px 0;
+ text-align: center;
+ font-size: 25px;
+ width: 1250px;
color: #ccc;
- }
-
- th, td {
- border: 1px solid #ccc;
- padding: 10px;
- }
-
- th {
- background-color: #444;
- }
-
- tr:nth-child(even) {
- background-color: #444;
- }
-
- tr:nth-child(odd) {
- background-color: #333;
- }
-
- .log-table-row {
- display: none;
- }
-
- .log-table-row td {
- padding: 0;
- }
-
- .log-table-row table {
- width: 100%;
border-collapse: collapse;
+ overflow: hidden;
}
- .log-table-row th, .log-table-row td {
- border: 1px solid #ccc;
+ table th {
+ background-color: #415eac;
+ padding: 10px;
+ border: 2px solid #ccc;
+ }
+
+ .link-table-row {
+ border: 2px solid #ccc;
+ }
+
+ table td {
padding: 10px;
}
- .log-table-row th {
- background-color: #444;
+ .link-table-row td {
+ padding: 20px;
}
- .log-table-row tr:nth-child(even) {
- background-color: #444;
+ .log-table-row table td {
+ background-color: #3b4148;
+ padding: 10px;
}
- .log-table-row tr:nth-child(odd) {
- background-color: #333;
+ .log-table-row table tr {
+ border: 2px solid #ccc;
}
+ .link-button {
+ background-color: #3b4148;
+ color: #ccc;
+ border: none;
+ padding: 10px;
+ cursor: pointer;
+ font-size: 25px;
+ border-radius: 5px;
+ }
+
+ .fa-trash:hover {
+ color: rgb(238, 86, 86);
+ cursor: pointer;
+ }
@@ -146,24 +113,25 @@
function createRow(index, link, logs) {
// Create the sub-table with the logs
let subTable = `
-
+
- ID |
- Timestamp |
- IP |
- Location |
- ISP |
+ ID |
+ Timestamp |
+ IP |
+ Location |
+ ISP |
`;
// Loop through the logs and create a row for each one
logs.forEach((log, index) => {
let row = `
-
+
${logs.length - index} |
${log.timestamp} |
${log.ip} |
${log.location} |
${log.isp} |
+ |
`;
subTable += row;
@@ -184,16 +152,16 @@
// Create the HTML for the row with sub-table
let row = `
-
+
-
+
|
${logs.length} |
${link.redirect_link} |
${expireDate} |
-
-
+ |
+
${subTable}
|
@@ -221,26 +189,41 @@
});
}
- function hideLogTables() {
- let tables = document.querySelectorAll('table tr table');
- tables.forEach(table => {
- table.style.display = 'none';
+ // hideLogRows to all log-table-rows
+ function hideLogRows() {
+ let logTRs = document.querySelectorAll('.log-table-row');
+ logTRs.forEach(row => {
+ row.style.display = 'none';
});
}
- getData();
- hideLogTables();
-
- // Add event listener to each element with the class of link-button
+ // Add event listener to all link buttons
document.addEventListener('click', (event) => {
if (event.target.classList.contains('link-button')) {
let id = event.target.id;
- let table = document.getElementById(`${id}-log`);
- if (table.style.display === 'none') {
- table.style.display = 'table-row';
+ let logTR = document.getElementById(`${id}-logTR`);
+ if (logTR.style.display === 'none') {
+ hideLogRows();
+ logTR.style.display = 'table-row';
} else {
- table.style.display = 'none';
+ logTR.style.display = 'none';
}
}
});
+
+ // Add an event listen to all trash bins
+ document.addEventListener('click', (event) => {
+ if (event.target.classList.contains('fa-trash')) {
+ let id = event.target.id;
+ let link = id.split('/')[1];
+ let logId = id.split('/')[0];
+ fetch(`/api/links/${link}/logs/${logId}`, {
+ method: 'DELETE'
+ });
+ let logRow = document.getElementById(`${logId}-log`)
+ logRow.remove();
+ }
+ });
+
+ getData();
\ No newline at end of file