2025-08-01 14:56:30 +05:00
{% extends "baseTemplate/index.html" %}
{% load i18n %}
{% block title %}{% trans "Containers List - CyberPanel" %}{% endblock %}
{% block content %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
< style >
.modern-container {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
.page-header {
text-align: center;
margin-bottom: 3rem;
padding: 3rem 0;
2025-08-03 01:59:50 +05:00
background: linear-gradient(135deg, var(--bg-hover, #f8f9ff) 0%, var(--bg-gradient, #f0f1ff) 100%);
2025-08-01 14:56:30 +05:00
border-radius: 20px;
animation: fadeInDown 0.5s ease-out;
position: relative;
overflow: hidden;
}
.page-header::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
2025-08-03 01:59:50 +05:00
background: radial-gradient(circle at 70% 30%, var(--accent-shadow-light, rgba(91, 95, 207, 0.15)) 0%, transparent 50%);
2025-08-01 14:56:30 +05:00
animation: rotate 30s linear infinite;
}
.page-title {
font-size: 3rem;
font-weight: 700;
2025-08-03 01:59:50 +05:00
color: var(--text-primary, #1e293b);
2025-08-01 14:56:30 +05:00
margin-bottom: 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
position: relative;
z-index: 1;
}
.docker-icon {
width: 60px;
height: 60px;
2025-08-03 01:59:50 +05:00
background: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
2025-08-03 01:59:50 +05:00
box-shadow: 0 4px 12px var(--shadow-medium, rgba(0,0,0,0.1));
2025-08-01 14:56:30 +05:00
}
.page-subtitle {
font-size: 1.25rem;
2025-08-03 01:59:50 +05:00
color: var(--text-secondary, #64748b);
2025-08-01 14:56:30 +05:00
margin-bottom: 1.5rem;
position: relative;
z-index: 1;
}
.header-actions {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
.btn-primary {
2025-08-03 01:59:50 +05:00
background: var(--accent-color, #5b5fcf);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border: none;
padding: 0.75rem 2rem;
border-radius: 10px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
font-size: 0.875rem;
}
.btn-primary:hover {
2025-08-03 01:59:50 +05:00
background: var(--accent-hover, #4547a9);
2025-08-01 14:56:30 +05:00
transform: translateY(-2px);
2025-08-03 01:59:50 +05:00
box-shadow: 0 8px 20px var(--accent-shadow-hover, rgba(91, 95, 207, 0.4));
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
}
.btn-danger {
2025-08-03 01:59:50 +05:00
background: var(--danger-color, #ef4444);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.825rem;
margin-right: 0.5rem;
}
.btn-danger:hover {
2025-08-03 01:59:50 +05:00
background: var(--danger-hover, #dc2626);
2025-08-01 14:56:30 +05:00
transform: translateY(-2px);
2025-08-03 01:59:50 +05:00
box-shadow: 0 4px 12px var(--danger-shadow, rgba(239, 68, 68, 0.4));
2025-08-01 14:56:30 +05:00
}
.btn-info {
2025-08-03 01:59:50 +05:00
background: var(--info-color, #3b82f6);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.825rem;
margin-right: 0.5rem;
}
.btn-info:hover {
2025-08-03 01:59:50 +05:00
background: var(--info-hover, #2563eb);
2025-08-01 14:56:30 +05:00
transform: translateY(-2px);
2025-08-03 01:59:50 +05:00
box-shadow: 0 4px 12px var(--info-shadow, rgba(59, 130, 246, 0.4));
2025-08-01 14:56:30 +05:00
}
.btn-success {
2025-08-03 01:59:50 +05:00
background: var(--success-color, #10b981);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.825rem;
margin-right: 0.5rem;
}
.btn-success:hover {
background: #059669;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
}
2025-09-18 22:16:42 +02:00
.btn-warning {
background: var(--warning-color, #f59e0b);
color: var(--bg-secondary, white);
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.825rem;
margin-right: 0.5rem;
}
.btn-warning:hover {
background: #d97706;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
}
.btn-group {
position: relative;
display: inline-block;
vertical-align: middle;
}
.btn-group .dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
min-width: 200px;
padding: 0.5rem 0;
margin: 0.125rem 0 0;
background-color: var(--bg-secondary, white);
border: 1px solid var(--border-color, #e8e9ff);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-medium, rgba(0,0,0,0.15));
display: none;
}
.btn-group .dropdown-menu.show {
display: block;
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.5rem 1rem;
clear: both;
font-weight: 400;
color: var(--text-primary, #1e293b);
text-align: inherit;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
transition: all 0.2s ease;
}
.dropdown-item:hover {
background-color: var(--bg-hover, #f8f9ff);
color: var(--accent-color, #5b5fcf);
}
.dropdown-item i {
margin-right: 0.5rem;
width: 16px;
text-align: center;
}
/* Bootstrap dropdown toggle */
.dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0.3em solid;
border-right: 0.3em solid transparent;
border-bottom: 0;
border-left: 0.3em solid transparent;
}
/* Show dropdown on hover */
.btn-group:hover .dropdown-menu {
display: block;
}
/* Alert styles */
.alert {
padding: 1rem 1.5rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: 8px;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
.alert strong {
font-weight: 600;
}
2025-08-01 14:56:30 +05:00
.main-card {
2025-08-03 01:59:50 +05:00
background: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border-radius: 16px;
2025-08-03 01:59:50 +05:00
box-shadow: 0 1px 3px var(--shadow-light, rgba(0,0,0,0.05)), 0 10px 40px var(--shadow-color, rgba(0,0,0,0.08));
border: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
overflow: hidden;
margin-bottom: 2rem;
animation: fadeInUp 0.5s ease-out;
}
.card-header {
2025-08-03 01:59:50 +05:00
background: linear-gradient(135deg, var(--bg-hover, #f8f9ff) 0%, var(--bg-gradient, #f0f1ff) 100%);
2025-08-01 14:56:30 +05:00
padding: 1.5rem 2rem;
2025-08-03 01:59:50 +05:00
border-bottom: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 1.25rem;
font-weight: 600;
2025-08-03 01:59:50 +05:00
color: var(--text-primary, #1e293b);
2025-08-01 14:56:30 +05:00
margin: 0;
display: flex;
align-items: center;
gap: 0.75rem;
}
.card-body {
padding: 2rem;
}
.containers-table {
width: 100%;
2025-08-03 01:59:50 +05:00
background: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
border-radius: 12px;
overflow: hidden;
2025-08-03 01:59:50 +05:00
border: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
margin-bottom: 2rem;
2025-09-20 20:46:32 +02:00
table-layout: fixed;
2025-08-01 14:56:30 +05:00
}
.containers-table thead {
2025-08-03 01:59:50 +05:00
background: linear-gradient(135deg, var(--bg-hover, #f8f9ff) 0%, var(--bg-gradient, #f0f1ff) 100%);
2025-08-01 14:56:30 +05:00
}
.containers-table th {
padding: 1rem;
text-align: left;
font-weight: 600;
2025-08-03 01:59:50 +05:00
color: var(--text-primary, #1e293b);
2025-08-01 14:56:30 +05:00
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
2025-08-03 01:59:50 +05:00
border-bottom: 2px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
}
.containers-table td {
padding: 1rem;
2025-08-03 01:59:50 +05:00
color: var(--text-secondary, #64748b);
2025-08-01 14:56:30 +05:00
font-size: 0.875rem;
2025-08-03 01:59:50 +05:00
border-bottom: 1px solid var(--border-light, #f3f4f6);
2025-08-01 14:56:30 +05:00
vertical-align: middle;
}
.containers-table tbody tr {
transition: all 0.2s ease;
}
.containers-table tbody tr:hover {
2025-08-03 01:59:50 +05:00
background: var(--bg-hover, #f8f9ff);
2025-08-01 14:56:30 +05:00
}
.containers-table tbody tr:last-child td {
border-bottom: none;
}
.container-name {
display: flex;
align-items: center;
gap: 0.75rem;
}
.container-icon {
width: 32px;
height: 32px;
2025-08-03 01:59:50 +05:00
background: var(--accent-bg, #e0e7ff);
2025-08-01 14:56:30 +05:00
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
2025-08-03 01:59:50 +05:00
color: var(--accent-color, #5b5fcf);
2025-08-01 14:56:30 +05:00
}
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.status-running {
2025-08-03 01:59:50 +05:00
background: var(--success-bg, #d1fae5);
color: var(--success-text, #065f46);
2025-08-01 14:56:30 +05:00
}
.status-stopped {
background: #fee2e2;
color: #991b1b;
}
.action-buttons {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.loading-spinner {
width: 20px;
height: 20px;
border: 3px solid #e8e9ff;
2025-08-03 01:59:50 +05:00
border-top-color: var(--accent-color, #5b5fcf);
2025-08-01 14:56:30 +05:00
border-radius: 50%;
animation: spin 1s linear infinite;
display: inline-block;
margin-left: 1rem;
}
.alert {
background: #fee2e2;
border: 1px solid #fecaca;
color: #991b1b;
padding: 1rem 1.5rem;
border-radius: 12px;
margin: 1rem 0;
}
.pagination {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 2rem;
}
.pagination li {
list-style: none;
}
.pagination li a {
display: block;
padding: 0.5rem 1rem;
2025-08-03 01:59:50 +05:00
background: var(--bg-hover, #f8f9ff);
border: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
border-radius: 8px;
2025-08-03 01:59:50 +05:00
color: var(--accent-color, #5b5fcf);
2025-08-01 14:56:30 +05:00
text-decoration: none;
font-weight: 500;
transition: all 0.2s ease;
}
.pagination li a:hover {
2025-08-03 01:59:50 +05:00
background: var(--accent-color, #5b5fcf);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
}
.modal-content {
border-radius: 16px;
overflow: hidden;
border: none;
2025-08-03 01:59:50 +05:00
box-shadow: 0 25px 50px -12px var(--shadow-dark, rgba(0, 0, 0, 0.25));
2025-08-01 14:56:30 +05:00
}
.modal-header {
2025-08-03 01:59:50 +05:00
background: linear-gradient(135deg, var(--bg-hover, #f8f9ff) 0%, var(--bg-gradient, #f0f1ff) 100%);
border-bottom: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
padding: 1.5rem 2rem;
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
2025-08-03 01:59:50 +05:00
color: var(--text-primary, #1e293b);
2025-08-01 14:56:30 +05:00
margin: 0;
}
.modal-body {
padding: 2rem;
}
.modal-footer {
2025-08-03 01:59:50 +05:00
background: var(--bg-hover, #f8f9ff);
border-top: 1px solid var(--border-color, #e8e9ff);
2025-08-01 14:56:30 +05:00
padding: 1rem 2rem;
}
.modal-footer .btn {
padding: 0.5rem 1.5rem;
border-radius: 8px;
font-weight: 500;
font-size: 0.875rem;
transition: all 0.3s ease;
border: none;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.modal-footer .btn.btn-primary {
2025-08-03 01:59:50 +05:00
background: var(--accent-color, #5b5fcf);
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
}
.modal-footer .btn.btn-primary:hover {
2025-08-03 01:59:50 +05:00
background: var(--accent-hover, #4547a9);
2025-08-01 14:56:30 +05:00
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.3);
}
.modal-footer .btn.btn-secondary {
background: #6b7280;
2025-08-03 01:59:50 +05:00
color: var(--bg-secondary, white);
2025-08-01 14:56:30 +05:00
margin-left: 0.5rem;
}
.modal-footer .btn.btn-secondary:hover {
background: #4b5563;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(107, 114, 128, 0.3);
}
/* Fix button click issues */
.modal-footer .btn {
pointer-events: auto !important;
z-index: 1051 !important;
position: relative !important;
}
.modal {
z-index: 1050 !important;
}
.modal-backdrop {
z-index: 1040 !important;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.page-title {
font-size: 2rem;
}
.action-buttons {
flex-direction: column;
}
.action-buttons button {
width: 100%;
}
.containers-table {
font-size: 0.75rem;
}
.containers-table th,
.containers-table td {
padding: 0.5rem;
}
}
< / style >
< div class = "modern-container" ng-controller = "listContainers" >
< div class = "page-header" >
< h1 class = "page-title" >
< div class = "docker-icon" >
2025-08-03 01:59:50 +05:00
< i class = "fab fa-docker" style = "color: var(--accent-color, #5b5fcf); font-size: 1.75rem;" > < / i >
2025-08-01 14:56:30 +05:00
< / div >
{% trans "Container Management" %}
< / h1 >
< p class = "page-subtitle" > {% trans "Manage and monitor Docker containers on your server" %}< / p >
< div class = "header-actions" >
< a href = "{% url 'containerImage' %}" class = "btn-primary" >
< i class = "fas fa-plus" > < / i >
{% trans "Create Container" %}
< / a >
< a href = "{% url 'manageImages' %}" class = "btn-primary" >
< i class = "fas fa-hdd" > < / i >
{% trans "Manage Images" %}
< / a >
< / div >
< / div >
<!-- Main Containers Section -->
< div class = "main-card" >
< div class = "card-header" >
< h2 class = "card-title" >
< i class = "fas fa-cube" > < / i >
{% trans "Active Containers" %}
< span id = "imageLoading" style = "display: none;" class = "loading-spinner" > < / span >
< / h2 >
< / div >
< div class = "card-body" >
< div class = "table-responsive" >
< table class = "containers-table" >
< thead >
< tr >
2025-09-20 20:46:32 +02:00
< th style = "width: 25%;" > {% trans "Container" %}< / th >
< th style = "width: 15%;" > {% trans "Owner" %}< / th >
< th style = "width: 25%;" > {% trans "Image" %}< / th >
< th style = "width: 15%;" > {% trans "Tag" %}< / th >
< th style = "width: 20%; text-align: center;" > {% trans "Actions" %}< / th >
2025-08-01 14:56:30 +05:00
< / tr >
< / thead >
< tbody >
< tr ng-repeat = "web in ContainerList track by $index" >
< td >
< div class = "container-name" >
< div class = "container-icon" >
< i class = "fab fa-docker" > < / i >
< / div >
< div >
< strong ng-bind = "web.name" > < / strong >
< / div >
< / div >
< / td >
< td ng-bind = "web.admin" > < / td >
< td ng-bind = "web.image" > < / td >
< td ng-bind = "web.tag" > < / td >
< td style = "text-align: center;" >
< div class = "action-buttons" >
< a class = "btn-success" href = "/docker/view/{$ web.name $}" title = "{% trans 'Manage Container' %}" >
< i class = "fas fa-cog" > < / i >
< / a >
< button class = "btn-info" ng-click = "showLog(web.name)" title = "{% trans 'View Logs' %}" >
< i class = "fas fa-file-alt" > < / i >
< / button >
2025-09-18 22:16:42 +02:00
< button class = "btn-warning" ng-click = "showUpdateModal(web.name, web.image, web.tag)" title = "{% trans 'Update Container' %}" >
< i class = "fas fa-sync-alt" > < / i >
2025-08-01 14:56:30 +05:00
< / button >
2025-09-18 22:16:42 +02:00
< div class = "btn-group" style = "display: inline-block;" >
< button class = "btn-danger dropdown-toggle" type = "button" data-toggle = "dropdown" aria-haspopup = "true" aria-expanded = "false" title = "{% trans 'Delete Options' %}" >
< i class = "fas fa-trash" > < / i >
< / button >
< div class = "dropdown-menu" >
< a class = "dropdown-item" href = "#" ng-click = "deleteContainerKeepData(web.name)" >
< i class = "fas fa-save" style = "margin-right: 5px;" > < / i >
{% trans "Delete Container (Keep Data)" %}
< / a >
< a class = "dropdown-item" href = "#" ng-click = "deleteContainerWithData(web.name)" >
< i class = "fas fa-trash" style = "margin-right: 5px;" > < / i >
{% trans "Delete Container + Data" %}
< / a >
< / div >
< / div >
2025-08-01 14:56:30 +05:00
< / div >
< / td >
< / tr >
< / tbody >
< / table >
< / div >
< div id = "listFail" class = "alert" ng-show = "errorMessage" >
< i class = "fas fa-exclamation-triangle" style = "margin-right: 0.5rem;" > < / i >
< strong > {% trans "Error:" %}< / strong > {$ errorMessage $}
< / div >
< div class = "pagination" ng-if = "pagination" >
< ul >
{% for items in pagination %}
< li ng-click = "getFurtherContainersFromDB({{ forloop.counter }})" >
< a href = "" > {{ forloop.counter }}< / a >
< / li >
{% endfor %}
< / ul >
< / div >
< / div >
< / div >
<!-- Unlisted Containers Section -->
{% if showUnlistedContainer %}
< div class = "main-card" >
< div class = "card-header" >
< h2 class = "card-title" >
< i class = "fas fa-question-circle" > < / i >
{% trans "Unlisted Containers" %}
< / h2 >
< / div >
< div class = "card-body" >
< p style = "color: #64748b; margin-bottom: 1.5rem;" >
< i class = "fas fa-info-circle" style = "margin-right: 0.5rem;" > < / i >
{% trans "Containers listed below were either not created through the panel or were not saved to database properly" %}
< / p >
< div class = "table-responsive" >
< table class = "containers-table" >
< thead >
< tr >
2025-09-20 20:46:32 +02:00
< th style = "width: 40%;" > {% trans "Container" %}< / th >
< th style = "width: 20%;" > {% trans "Status" %}< / th >
< th style = "width: 40%; text-align: center;" > {% trans "Actions" %}< / th >
2025-08-01 14:56:30 +05:00
< / tr >
< / thead >
< tbody >
{% for container in unlistedContainers %}
< tr >
< td >
< div class = "container-name" >
< div class = "container-icon" >
< i class = "fab fa-docker" > < / i >
< / div >
< div >
< strong > {{container.name}}< / strong >
< / div >
< / div >
< / td >
< td >
< span class = "status-badge {% if container.status == 'running' %}status-running{% else %}status-stopped{% endif %}" >
{{container.status}}
< / span >
< / td >
< td style = "text-align: center;" >
< div class = "action-buttons" >
< button class = "btn-success" ng-click = "assignContainer('{{container.name}}')" title = "{% trans 'Assign to User' %}" >
< i class = "fas fa-user-plus" > < / i >
< / button >
< button class = "btn-info" ng-click = "showLog('{{container.name}}')" title = "{% trans 'View Logs' %}" >
< i class = "fas fa-file-alt" > < / i >
< / button >
2025-09-18 22:16:42 +02:00
< button class = "btn-warning" ng-click = "showUpdateModal('{{container.name}}', 'unknown', 'unknown')" title = "{% trans 'Update Container' %}" >
< i class = "fas fa-sync-alt" > < / i >
2025-08-01 14:56:30 +05:00
< / button >
2025-09-18 22:16:42 +02:00
< div class = "btn-group" style = "display: inline-block;" >
< button class = "btn-danger dropdown-toggle" type = "button" data-toggle = "dropdown" aria-haspopup = "true" aria-expanded = "false" title = "{% trans 'Delete Options' %}" >
< i class = "fas fa-trash" > < / i >
< / button >
< div class = "dropdown-menu" >
< a class = "dropdown-item" href = "#" ng-click = "deleteContainerKeepData('{{container.name}}', true)" >
< i class = "fas fa-save" style = "margin-right: 5px;" > < / i >
{% trans "Delete Container (Keep Data)" %}
< / a >
< a class = "dropdown-item" href = "#" ng-click = "deleteContainerWithData('{{container.name}}', true)" >
< i class = "fas fa-trash" style = "margin-right: 5px;" > < / i >
{% trans "Delete Container + Data" %}
< / a >
< / div >
< / div >
2025-08-01 14:56:30 +05:00
< / div >
< / td >
< / tr >
{% endfor %}
< / tbody >
< / table >
< / div >
< / div >
< / div >
{% endif %}
<!-- Container Logs Modal -->
< div id = "logs" class = "modal fade" role = "dialog" >
2025-09-20 21:14:12 +02:00
< div class = "modal-dialog modal-xl" >
2025-08-01 14:56:30 +05:00
< div class = "modal-content" >
< div class = "modal-header" >
< h4 class = "modal-title" >
< i class = "fas fa-file-alt" style = "margin-right: 0.5rem;" > < / i >
{% trans "Container Logs" %}
2025-09-20 21:14:12 +02:00
< small class = "text-muted" ng-if = "logInfo" >
- Status: < span class = "badge badge-info" > {$ logInfo.container_status $}< / span >
< span ng-if = "logInfo.log_count" > | Lines: < span class = "badge badge-secondary" > {$ logInfo.log_count $}< / span > < / span >
< / small >
2025-08-01 14:56:30 +05:00
< / h4 >
< button type = "button" class = "close" data-dismiss = "modal"
2025-08-03 01:59:50 +05:00
style="font-size: 1.5rem; background: transparent; border: none;">× < / button >
2025-08-01 14:56:30 +05:00
< / div >
2025-09-20 21:14:12 +02:00
< div class = "modal-body" style = "padding: 0;" >
<!-- Log Controls -->
< div class = "bg-light p-2 border-bottom" >
< div class = "row align-items-center" >
< div class = "col-md-6" >
< div class = "btn-group" role = "group" >
< button type = "button" class = "btn btn-sm btn-outline-primary" ng-click = "showLog('', true)" >
< i class = "fas fa-sync-alt" > < / i > Refresh
< / button >
< button type = "button" class = "btn btn-sm btn-outline-secondary" ng-click = "scrollToTop()" >
< i class = "fas fa-arrow-up" > < / i > Top
< / button >
< button type = "button" class = "btn btn-sm btn-outline-secondary" ng-click = "scrollToBottom()" >
< i class = "fas fa-arrow-down" > < / i > Bottom
< / button >
< button type = "button" class = "btn btn-sm btn-outline-info" ng-click = "clearLogs()" >
< i class = "fas fa-trash" > < / i > Clear
< / button >
< / div >
< / div >
< div class = "col-md-6 text-right" >
< small class = "text-muted" >
< i class = "fas fa-info-circle" > < / i >
Use Ctrl+F to search within logs
< / small >
< / div >
< / div >
< / div >
<!-- Log Display -->
< div id = "logContainer" style = "height: 500px; overflow-y: auto; background: #1e1e1e; color: #d4d4d4; padding: 15px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 13px; line-height: 1.4; white-space: pre-wrap; word-wrap: break-word;" >
< div ng-if = "logs === 'Loading...'" class = "text-center text-muted" >
< i class = "fas fa-spinner fa-spin" > < / i > Loading logs...
< / div >
< div ng-if = "logs && logs !== 'Loading...'" ng-bind-html = "formattedLogs" > < / div >
< div ng-if = "!logs || logs === ''" class = "text-center text-muted" >
< i class = "fas fa-file-alt" > < / i > No logs available
< / div >
< / div >
2025-08-01 14:56:30 +05:00
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-dismiss = "modal" >
< i class = "fas fa-times" > < / i >
{% trans "Close" %}
< / button >
< / div >
< / div >
< / div >
< / div >
<!-- Assign Container Modal -->
< div id = "assign" class = "modal fade" role = "dialog" >
< div class = "modal-dialog" >
< div class = "modal-content" >
< div class = "modal-header" >
< h4 class = "modal-title" >
< i class = "fas fa-user-plus" style = "margin-right: 0.5rem;" > < / i >
{% trans "Assign Container to User" %}
< / h4 >
< button type = "button" class = "close" data-dismiss = "modal"
2025-08-03 01:59:50 +05:00
style="font-size: 1.5rem; background: transparent; border: none;">× < / button >
2025-08-01 14:56:30 +05:00
< / div >
< div class = "modal-body" >
< div class = "form-group" >
2025-08-03 01:59:50 +05:00
< label style = "display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--text-primary, #1e293b);" >
2025-08-01 14:56:30 +05:00
{% trans "Select Owner" %}
< / label >
< select ng-model = "dockerOwner" class = "form-control"
2025-08-03 01:59:50 +05:00
style="width: 100%; padding: 0.75rem; border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px;">
2025-08-01 14:56:30 +05:00
{% for user in adminNames %}
< option > {{user}}< / option >
{% endfor %}
< / select >
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-warning" ng-click = "testScope()" style = "margin-right: 0.5rem;" >
Test Scope
< / button >
< button type = "button" class = "btn btn-primary" ng-click = "submitAssignContainer()" >
< i class = "fas fa-check" > < / i >
{% trans "Assign" %}
< / button >
< button type = "button" class = "btn btn-secondary" data-dismiss = "modal" >
< i class = "fas fa-times" > < / i >
{% trans "Cancel" %}
< / button >
< / div >
< / div >
< / div >
< / div >
2025-09-18 22:16:42 +02:00
<!-- Update Container Modal -->
< div id = "updateContainer" class = "modal fade" role = "dialog" >
< div class = "modal-dialog modal-lg" >
< div class = "modal-content" >
< div class = "modal-header" >
< h4 class = "modal-title" >
< i class = "fas fa-sync-alt" style = "margin-right: 0.5rem;" > < / i >
{% trans "Update Container" %}
< / h4 >
< button type = "button" class = "close" data-dismiss = "modal"
style="font-size: 1.5rem; background: transparent; border: none;">× < / button >
< / div >
< div class = "modal-body" >
< div class = "form-group" >
< label style = "display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--text-primary, #1e293b);" >
{% trans "Container Name" %}
< / label >
< input type = "text" ng-model = "updateContainerName" class = "form-control" readonly
style="width: 100%; padding: 0.75rem; border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px; background-color: #f8f9fa;">
< / div >
< div class = "form-group" >
< label style = "display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--text-primary, #1e293b);" >
{% trans "Current Image" %}
< / label >
< input type = "text" ng-model = "currentImage" class = "form-control" readonly
style="width: 100%; padding: 0.75rem; border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px; background-color: #f8f9fa;">
< / div >
< div class = "form-group" >
< label style = "display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--text-primary, #1e293b);" >
{% trans "New Image" %} < span style = "color: #ef4444;" > *< / span >
< / label >
< input type = "text" ng-model = "newImage" class = "form-control"
placeholder="e.g., nginx, mysql, postgres"
style="width: 100%; padding: 0.75rem; border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px;">
< small class = "form-text text-muted" >
{% trans "Enter the image name (without tag). Leave empty to update current image to latest tag." %}
< / small >
< / div >
< div class = "form-group" >
< label style = "display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--text-primary, #1e293b);" >
{% trans "New Tag" %}
< / label >
< input type = "text" ng-model = "newTag" class = "form-control"
placeholder="e.g., latest, 1.21, alpine"
style="width: 100%; padding: 0.75rem; border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px;">
< small class = "form-text text-muted" >
{% trans "Enter the tag version. Defaults to 'latest' if not specified." %}
< / small >
< / div >
< div class = "alert alert-info" style = "margin-top: 1rem;" >
< i class = "fas fa-info-circle" style = "margin-right: 0.5rem;" > < / i >
< strong > {% trans "Data Safety:" %}< / strong >
{% trans "This operation will preserve all your data using Docker volumes. The container will be recreated with the new image while keeping all volumes intact." %}
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-primary" ng-click = "performUpdate()" ng-disabled = "!newImage && !newTag" >
< i class = "fas fa-sync-alt" > < / i >
{% trans "Update Container" %}
< / button >
< button type = "button" class = "btn btn-secondary" data-dismiss = "modal" >
< i class = "fas fa-times" > < / i >
{% trans "Cancel" %}
< / button >
< / div >
< / div >
< / div >
< / div >
2025-08-01 14:56:30 +05:00
{% endblock %}
{% block footer_scripts %}
< script src = "{% static 'dockerManager/dockerManager.js' %}" > < / script >
{% endblock %}