- Created IT audit form template with 9 sections (1-3 fully implemented) - Section 1: IT Contact (IT manager, outsourcing, provider, contact info) - Section 2: Cloud & Identity (Azure AD, M365, Google Workspace, local AD) - Section 3: Server Infrastructure (server count, types, virtualization, OS) - Sections 4-9: Placeholder structure ready for implementation - Added progress bar with section navigation dots - Implemented toggle switches, chip selects for multi-value fields - Added conditional field visibility logic - Included loading overlay and info modal patterns - Responsive design following existing audit templates (gbp_audit.html, seo_audit.html) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1420 lines
52 KiB
HTML
1420 lines
52 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Audyt IT - {{ company.name }} - Norda Biznes Hub{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.audit-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: var(--spacing-xl);
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.audit-header-info h1 {
|
|
font-size: var(--font-size-2xl);
|
|
color: var(--text-primary);
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.audit-header-info p {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.data-source-info {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
margin-top: var(--spacing-sm);
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
background: var(--info-light, #e0f2fe);
|
|
border-radius: var(--radius);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--info, #0284c7);
|
|
}
|
|
|
|
.data-source-info svg {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
align-items: center;
|
|
}
|
|
|
|
/* Progress Bar */
|
|
.progress-container {
|
|
background: var(--surface);
|
|
padding: var(--spacing-md);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.progress-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-sm);
|
|
}
|
|
|
|
.progress-title {
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.progress-percentage {
|
|
font-size: var(--font-size-lg);
|
|
font-weight: 700;
|
|
color: var(--primary);
|
|
}
|
|
|
|
.progress-bar {
|
|
height: 8px;
|
|
background: var(--border);
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: var(--primary);
|
|
border-radius: 4px;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.progress-sections {
|
|
display: flex;
|
|
gap: var(--spacing-xs);
|
|
margin-top: var(--spacing-sm);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.progress-section-dot {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
background: var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
color: var(--text-tertiary);
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.progress-section-dot.active {
|
|
background: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
.progress-section-dot.complete {
|
|
background: var(--success);
|
|
color: white;
|
|
}
|
|
|
|
.progress-section-dot:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
/* Form Sections */
|
|
.form-section {
|
|
background: var(--surface);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
margin-bottom: var(--spacing-xl);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.section-header {
|
|
background: var(--bg-secondary);
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.section-header:hover {
|
|
background: var(--bg-tertiary);
|
|
}
|
|
|
|
.section-number {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
background: var(--primary);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 700;
|
|
font-size: var(--font-size-sm);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.section-number.complete {
|
|
background: var(--success);
|
|
}
|
|
|
|
.section-title-group {
|
|
flex: 1;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: var(--font-size-lg);
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.section-subtitle {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.section-toggle {
|
|
width: 24px;
|
|
height: 24px;
|
|
color: var(--text-secondary);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.section-header.collapsed .section-toggle {
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
.section-content {
|
|
padding: var(--spacing-lg);
|
|
}
|
|
|
|
.section-content.collapsed {
|
|
display: none;
|
|
}
|
|
|
|
/* Form Fields */
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: var(--spacing-lg);
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-xs);
|
|
}
|
|
|
|
.form-group.full-width {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.form-label {
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
font-size: var(--font-size-sm);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
}
|
|
|
|
.form-label .required {
|
|
color: var(--error);
|
|
}
|
|
|
|
.form-label .help-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
color: var(--text-tertiary);
|
|
cursor: help;
|
|
}
|
|
|
|
.form-input,
|
|
.form-select,
|
|
.form-textarea {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-primary);
|
|
background: var(--surface);
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.form-input:focus,
|
|
.form-select:focus,
|
|
.form-textarea:focus {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
}
|
|
|
|
.form-select {
|
|
cursor: pointer;
|
|
appearance: none;
|
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
background-position: right 0.5rem center;
|
|
background-repeat: no-repeat;
|
|
background-size: 1.5em 1.5em;
|
|
padding-right: 2.5rem;
|
|
}
|
|
|
|
.form-hint {
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-tertiary);
|
|
}
|
|
|
|
/* Checkbox and Toggle */
|
|
.form-checkbox-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.form-checkbox {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.form-checkbox input[type="checkbox"] {
|
|
width: 18px;
|
|
height: 18px;
|
|
accent-color: var(--primary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.form-checkbox-label {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.form-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
padding: var(--spacing-sm);
|
|
background: var(--bg-secondary);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
.toggle-switch {
|
|
position: relative;
|
|
width: 48px;
|
|
height: 24px;
|
|
background: var(--border);
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.toggle-switch.active {
|
|
background: var(--success);
|
|
}
|
|
|
|
.toggle-switch::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
top: 2px;
|
|
left: 2px;
|
|
transition: var(--transition);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.toggle-switch.active::after {
|
|
left: 26px;
|
|
}
|
|
|
|
.toggle-label {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Conditional Fields */
|
|
.conditional-fields {
|
|
margin-top: var(--spacing-md);
|
|
padding: var(--spacing-md);
|
|
background: var(--bg-secondary);
|
|
border-radius: var(--radius);
|
|
border-left: 3px solid var(--primary);
|
|
}
|
|
|
|
.conditional-fields.hidden {
|
|
display: none;
|
|
}
|
|
|
|
/* Multi-select chips */
|
|
.chip-select {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.chip-option {
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-full);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.chip-option:hover {
|
|
border-color: var(--primary);
|
|
color: var(--primary);
|
|
}
|
|
|
|
.chip-option.selected {
|
|
background: var(--primary);
|
|
border-color: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
/* Form Actions */
|
|
.form-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: var(--spacing-lg);
|
|
background: var(--surface);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
margin-top: var(--spacing-xl);
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.form-actions-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.form-actions-right {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
/* Breadcrumb */
|
|
.breadcrumb {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.breadcrumb a {
|
|
color: var(--primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.breadcrumb a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.breadcrumb-separator {
|
|
color: var(--border);
|
|
}
|
|
|
|
/* Loading Overlay */
|
|
.loading-overlay {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
z-index: 1000;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
gap: var(--spacing-lg);
|
|
}
|
|
|
|
.loading-overlay.active {
|
|
display: flex;
|
|
}
|
|
|
|
.loading-content {
|
|
background: var(--surface);
|
|
padding: var(--spacing-xl);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-lg);
|
|
max-width: 400px;
|
|
width: 90%;
|
|
text-align: center;
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 48px;
|
|
height: 48px;
|
|
border: 4px solid var(--border);
|
|
border-top-color: var(--primary);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto var(--spacing-md);
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Modal */
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 1000;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.modal.active {
|
|
display: flex;
|
|
}
|
|
|
|
.modal-content {
|
|
background: var(--surface);
|
|
padding: var(--spacing-xl);
|
|
border-radius: var(--radius-lg);
|
|
max-width: 480px;
|
|
width: 90%;
|
|
box-shadow: var(--shadow-lg);
|
|
animation: modalSlideIn 0.2s ease-out;
|
|
}
|
|
|
|
@keyframes modalSlideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.modal-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.modal-icon.success { background: #dcfce7; color: #16a34a; }
|
|
.modal-icon.info { background: #dbeafe; color: #2563eb; }
|
|
.modal-icon.warning { background: #fef3c7; color: #d97706; }
|
|
.modal-icon.error { background: #fee2e2; color: #dc2626; }
|
|
|
|
.modal-title {
|
|
font-size: var(--font-size-xl);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.modal-body {
|
|
color: var(--text-secondary);
|
|
margin-bottom: var(--spacing-lg);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
/* Section icons */
|
|
.section-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.audit-header {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.header-actions {
|
|
width: 100%;
|
|
justify-content: center;
|
|
}
|
|
|
|
.form-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.form-actions {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.form-actions-left,
|
|
.form-actions-right {
|
|
width: 100%;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Breadcrumb -->
|
|
<div class="breadcrumb">
|
|
<a href="{{ url_for('index') }}">Firmy</a>
|
|
<span class="breadcrumb-separator">/</span>
|
|
<a href="{{ url_for('company_detail', company_id=company.id) }}">{{ company.name }}</a>
|
|
<span class="breadcrumb-separator">/</span>
|
|
<span>Audyt IT</span>
|
|
</div>
|
|
|
|
<div class="audit-header">
|
|
<div class="audit-header-info">
|
|
<h1>Audyt Infrastruktury IT</h1>
|
|
<p>{{ company.name }}</p>
|
|
<div class="data-source-info">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/>
|
|
</svg>
|
|
<span>Formularz oceny infrastruktury IT i gotowosci do wspolpracy</span>
|
|
</div>
|
|
</div>
|
|
<div class="header-actions">
|
|
<a href="{{ url_for('company_detail', company_id=company.id) }}" class="btn btn-outline btn-sm">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
|
</svg>
|
|
Profil firmy
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Progress Bar -->
|
|
<div class="progress-container">
|
|
<div class="progress-header">
|
|
<span class="progress-title">Postep wypelniania formularza</span>
|
|
<span class="progress-percentage" id="progressPercentage">0%</span>
|
|
</div>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" id="progressFill" style="width: 0%;"></div>
|
|
</div>
|
|
<div class="progress-sections">
|
|
<div class="progress-section-dot active" data-section="1" title="Kontakt IT">1</div>
|
|
<div class="progress-section-dot" data-section="2" title="Chmura i Tozsamosc">2</div>
|
|
<div class="progress-section-dot" data-section="3" title="Infrastruktura Serwerowa">3</div>
|
|
<div class="progress-section-dot" data-section="4" title="Stacje Robocze">4</div>
|
|
<div class="progress-section-dot" data-section="5" title="Bezpieczenstwo">5</div>
|
|
<div class="progress-section-dot" data-section="6" title="Backup i DR">6</div>
|
|
<div class="progress-section-dot" data-section="7" title="Monitoring">7</div>
|
|
<div class="progress-section-dot" data-section="8" title="Aplikacje Biznesowe">8</div>
|
|
<div class="progress-section-dot" data-section="9" title="Wspolpraca">9</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="itAuditForm" method="POST" action="{{ url_for('save_it_audit', company_id=company.id) }}">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
|
|
<!-- Section 1: IT Contact -->
|
|
<div class="form-section" data-section="1">
|
|
<div class="section-header" onclick="toggleSection(1)">
|
|
<div class="section-number" id="sectionNumber1">1</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">
|
|
<svg class="section-icon" style="display: inline; vertical-align: middle; margin-right: 8px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
|
</svg>
|
|
Kontakt IT
|
|
</div>
|
|
<div class="section-subtitle">Informacje o osobie odpowiedzialnej za IT w firmie</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content" id="sectionContent1">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy firma ma wewnetrznego menedzera IT?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasItManager" onclick="toggleSwitch('hasItManager')"></div>
|
|
<span class="toggle-label" id="labelHasItManager">Nie</span>
|
|
<input type="hidden" name="has_it_manager" id="hasItManager" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy IT jest outsourcowane?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleItOutsourced" onclick="toggleSwitch('itOutsourced')"></div>
|
|
<span class="toggle-label" id="labelItOutsourced">Nie</span>
|
|
<input type="hidden" name="it_outsourced" id="itOutsourced" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group conditional-fields" id="itProviderFields">
|
|
<label class="form-label">
|
|
Nazwa dostawcy uslug IT
|
|
</label>
|
|
<input type="text" class="form-input" name="it_provider_name" id="itProviderName" placeholder="np. INPI, Comarch, IT Partner">
|
|
<span class="form-hint">Nazwa firmy zewnetrznej obslugujaca IT</span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Imie i nazwisko osoby kontaktowej ds. IT
|
|
</label>
|
|
<input type="text" class="form-input" name="it_contact_name" id="itContactName" placeholder="np. Jan Kowalski">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Email kontaktowy IT
|
|
</label>
|
|
<input type="email" class="form-input" name="it_contact_email" id="itContactEmail" placeholder="it@firma.pl">
|
|
<span class="form-hint">Adres email do spraw technicznych</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 2: Cloud & Identity -->
|
|
<div class="form-section" data-section="2">
|
|
<div class="section-header" onclick="toggleSection(2)">
|
|
<div class="section-number" id="sectionNumber2">2</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">
|
|
<svg class="section-icon" style="display: inline; vertical-align: middle; margin-right: 8px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/>
|
|
</svg>
|
|
Chmura i Tozsamosc
|
|
</div>
|
|
<div class="section-subtitle">Uslugi chmurowe, Azure AD, Microsoft 365, Google Workspace</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent2">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy firma korzysta z Azure Active Directory (Entra ID)?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasAzureAd" onclick="toggleSwitch('hasAzureAd')"></div>
|
|
<span class="toggle-label" id="labelHasAzureAd">Nie</span>
|
|
<input type="hidden" name="has_azure_ad" id="hasAzureAd" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group conditional-fields hidden" id="azureAdFields">
|
|
<label class="form-label">
|
|
Nazwa tenanta Azure (domena)
|
|
</label>
|
|
<input type="text" class="form-input" name="azure_tenant_name" id="azureTenantName" placeholder="np. firma.onmicrosoft.com">
|
|
<span class="form-hint">Domena glowna lub .onmicrosoft.com</span>
|
|
</div>
|
|
|
|
<div class="form-group conditional-fields hidden" id="azureUserCountField">
|
|
<label class="form-label">
|
|
Liczba uzytkownikow w Azure AD
|
|
</label>
|
|
<select class="form-select" name="azure_user_count" id="azureUserCount">
|
|
<option value="">Wybierz przedzial</option>
|
|
<option value="1-10">1-10 uzytkownikow</option>
|
|
<option value="11-25">11-25 uzytkownikow</option>
|
|
<option value="26-50">26-50 uzytkownikow</option>
|
|
<option value="51-100">51-100 uzytkownikow</option>
|
|
<option value="101-250">101-250 uzytkownikow</option>
|
|
<option value="250+">Ponad 250 uzytkownikow</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy firma korzysta z Microsoft 365?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasM365" onclick="toggleSwitch('hasM365')"></div>
|
|
<span class="toggle-label" id="labelHasM365">Nie</span>
|
|
<input type="hidden" name="has_m365" id="hasM365" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group full-width conditional-fields hidden" id="m365PlansField">
|
|
<label class="form-label">
|
|
Posiadane plany Microsoft 365 (wybierz wszystkie)
|
|
</label>
|
|
<div class="chip-select" id="m365PlansChips">
|
|
<div class="chip-option" data-value="basic" onclick="toggleChip(this, 'm365Plans')">Microsoft 365 Basic</div>
|
|
<div class="chip-option" data-value="standard" onclick="toggleChip(this, 'm365Plans')">Microsoft 365 Business Standard</div>
|
|
<div class="chip-option" data-value="premium" onclick="toggleChip(this, 'm365Plans')">Microsoft 365 Business Premium</div>
|
|
<div class="chip-option" data-value="e3" onclick="toggleChip(this, 'm365Plans')">Microsoft 365 E3</div>
|
|
<div class="chip-option" data-value="e5" onclick="toggleChip(this, 'm365Plans')">Microsoft 365 E5</div>
|
|
<div class="chip-option" data-value="exchange" onclick="toggleChip(this, 'm365Plans')">Exchange Online</div>
|
|
<div class="chip-option" data-value="sharepoint" onclick="toggleChip(this, 'm365Plans')">SharePoint Online</div>
|
|
</div>
|
|
<input type="hidden" name="m365_plans" id="m365Plans" value="">
|
|
</div>
|
|
|
|
<div class="form-group full-width conditional-fields hidden" id="teamsUsageField">
|
|
<label class="form-label">
|
|
Wykorzystanie Microsoft Teams (wybierz wszystkie)
|
|
</label>
|
|
<div class="chip-select" id="teamsUsageChips">
|
|
<div class="chip-option" data-value="chat" onclick="toggleChip(this, 'teamsUsage')">Chat</div>
|
|
<div class="chip-option" data-value="calls" onclick="toggleChip(this, 'teamsUsage')">Polaczenia glosowe</div>
|
|
<div class="chip-option" data-value="meetings" onclick="toggleChip(this, 'teamsUsage')">Spotkania video</div>
|
|
<div class="chip-option" data-value="channels" onclick="toggleChip(this, 'teamsUsage')">Kanaly zespolow</div>
|
|
<div class="chip-option" data-value="files" onclick="toggleChip(this, 'teamsUsage')">Udostepnianie plikow</div>
|
|
<div class="chip-option" data-value="apps" onclick="toggleChip(this, 'teamsUsage')">Aplikacje i integracje</div>
|
|
</div>
|
|
<input type="hidden" name="teams_usage" id="teamsUsage" value="">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy firma korzysta z Google Workspace?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasGoogleWorkspace" onclick="toggleSwitch('hasGoogleWorkspace')"></div>
|
|
<span class="toggle-label" id="labelHasGoogleWorkspace">Nie</span>
|
|
<input type="hidden" name="has_google_workspace" id="hasGoogleWorkspace" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Czy firma ma lokalna Active Directory (on-premises)?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasLocalAd" onclick="toggleSwitch('hasLocalAd')"></div>
|
|
<span class="toggle-label" id="labelHasLocalAd">Nie</span>
|
|
<input type="hidden" name="has_local_ad" id="hasLocalAd" value="false">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group conditional-fields hidden" id="localAdFields">
|
|
<label class="form-label">
|
|
Nazwa domeny AD
|
|
</label>
|
|
<input type="text" class="form-input" name="ad_domain_name" id="adDomainName" placeholder="np. firma.local">
|
|
</div>
|
|
|
|
<div class="form-group conditional-fields hidden" id="adSyncField">
|
|
<label class="form-label">
|
|
Czy AD jest zsynchronizowane z Azure AD?
|
|
</label>
|
|
<div class="form-toggle">
|
|
<div class="toggle-switch" id="toggleHasAdAzureSync" onclick="toggleSwitch('hasAdAzureSync')"></div>
|
|
<span class="toggle-label" id="labelHasAdAzureSync">Nie</span>
|
|
<input type="hidden" name="has_ad_azure_sync" id="hasAdAzureSync" value="false">
|
|
</div>
|
|
<span class="form-hint">Azure AD Connect / Cloud Sync</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 3: Server Infrastructure -->
|
|
<div class="form-section" data-section="3">
|
|
<div class="section-header" onclick="toggleSection(3)">
|
|
<div class="section-number" id="sectionNumber3">3</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">
|
|
<svg class="section-icon" style="display: inline; vertical-align: middle; margin-right: 8px;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
|
|
</svg>
|
|
Infrastruktura Serwerowa
|
|
</div>
|
|
<div class="section-subtitle">Serwery fizyczne i wirtualne, platformy wirtualizacji, systemy operacyjne</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent3">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Liczba serwerow (fizycznych + wirtualnych)
|
|
</label>
|
|
<select class="form-select" name="server_count" id="serverCount">
|
|
<option value="">Wybierz przedzial</option>
|
|
<option value="0">Brak serwerow</option>
|
|
<option value="1-3">1-3 serwerow</option>
|
|
<option value="4-10">4-10 serwerow</option>
|
|
<option value="11-25">11-25 serwerow</option>
|
|
<option value="26-50">26-50 serwerow</option>
|
|
<option value="50+">Ponad 50 serwerow</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group full-width">
|
|
<label class="form-label">
|
|
Typy serwerow (wybierz wszystkie)
|
|
</label>
|
|
<div class="chip-select" id="serverTypesChips">
|
|
<div class="chip-option" data-value="physical_onprem" onclick="toggleChip(this, 'serverTypes')">Fizyczne (on-premises)</div>
|
|
<div class="chip-option" data-value="virtual_onprem" onclick="toggleChip(this, 'serverTypes')">Wirtualne (on-premises)</div>
|
|
<div class="chip-option" data-value="vps" onclick="toggleChip(this, 'serverTypes')">VPS (hosting zewnetrzny)</div>
|
|
<div class="chip-option" data-value="azure_vm" onclick="toggleChip(this, 'serverTypes')">Azure VM</div>
|
|
<div class="chip-option" data-value="aws_ec2" onclick="toggleChip(this, 'serverTypes')">AWS EC2</div>
|
|
<div class="chip-option" data-value="gcp_compute" onclick="toggleChip(this, 'serverTypes')">Google Cloud Compute</div>
|
|
<div class="chip-option" data-value="dedicated" onclick="toggleChip(this, 'serverTypes')">Serwery dedykowane (kolokacja)</div>
|
|
</div>
|
|
<input type="hidden" name="server_types" id="serverTypes" value="">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Platforma wirtualizacji (glowna)
|
|
</label>
|
|
<select class="form-select" name="virtualization_platform" id="virtualizationPlatform">
|
|
<option value="">Wybierz platforme</option>
|
|
<option value="none">Brak wirtualizacji</option>
|
|
<option value="proxmox">Proxmox VE</option>
|
|
<option value="vmware_esxi">VMware ESXi / vSphere</option>
|
|
<option value="hyperv">Microsoft Hyper-V</option>
|
|
<option value="xcp-ng">XCP-ng</option>
|
|
<option value="kvm">KVM / QEMU</option>
|
|
<option value="nutanix">Nutanix AHV</option>
|
|
<option value="other">Inna</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group full-width">
|
|
<label class="form-label">
|
|
Systemy operacyjne serwerow (wybierz wszystkie)
|
|
</label>
|
|
<div class="chip-select" id="serverOsChips">
|
|
<div class="chip-option" data-value="windows_2022" onclick="toggleChip(this, 'serverOs')">Windows Server 2022</div>
|
|
<div class="chip-option" data-value="windows_2019" onclick="toggleChip(this, 'serverOs')">Windows Server 2019</div>
|
|
<div class="chip-option" data-value="windows_2016" onclick="toggleChip(this, 'serverOs')">Windows Server 2016</div>
|
|
<div class="chip-option" data-value="windows_older" onclick="toggleChip(this, 'serverOs')">Windows Server (starszy)</div>
|
|
<div class="chip-option" data-value="ubuntu" onclick="toggleChip(this, 'serverOs')">Ubuntu Server</div>
|
|
<div class="chip-option" data-value="debian" onclick="toggleChip(this, 'serverOs')">Debian</div>
|
|
<div class="chip-option" data-value="rhel" onclick="toggleChip(this, 'serverOs')">RHEL / CentOS / Rocky</div>
|
|
<div class="chip-option" data-value="suse" onclick="toggleChip(this, 'serverOs')">SUSE Linux</div>
|
|
<div class="chip-option" data-value="freebsd" onclick="toggleChip(this, 'serverOs')">FreeBSD / TrueNAS</div>
|
|
</div>
|
|
<input type="hidden" name="server_os" id="serverOs" value="">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">
|
|
Marka firewalla / UTM sieciowego
|
|
</label>
|
|
<select class="form-select" name="network_firewall_brand" id="networkFirewallBrand">
|
|
<option value="">Wybierz marke</option>
|
|
<option value="none">Brak dedykowanego firewalla</option>
|
|
<option value="fortigate">Fortinet FortiGate</option>
|
|
<option value="pfsense">pfSense / OPNsense</option>
|
|
<option value="sophos">Sophos XG / XGS</option>
|
|
<option value="cisco">Cisco ASA / Meraki</option>
|
|
<option value="paloalto">Palo Alto Networks</option>
|
|
<option value="watchguard">WatchGuard</option>
|
|
<option value="mikrotik">MikroTik</option>
|
|
<option value="ubiquiti">Ubiquiti UDM / USG</option>
|
|
<option value="checkpoint">Check Point</option>
|
|
<option value="other">Inny</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Placeholder sections 4-9 (to be implemented in next subtask) -->
|
|
<div class="form-section" data-section="4">
|
|
<div class="section-header collapsed" onclick="toggleSection(4)">
|
|
<div class="section-number" id="sectionNumber4">4</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Stacje Robocze</div>
|
|
<div class="section-subtitle">Komputery pracownikow, systemy operacyjne, MDM</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent4">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section" data-section="5">
|
|
<div class="section-header collapsed" onclick="toggleSection(5)">
|
|
<div class="section-number" id="sectionNumber5">5</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Bezpieczenstwo</div>
|
|
<div class="section-subtitle">Antywirus, EDR, VPN, MFA, firewall</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent5">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section" data-section="6">
|
|
<div class="section-header collapsed" onclick="toggleSection(6)">
|
|
<div class="section-number" id="sectionNumber6">6</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Backup i Disaster Recovery</div>
|
|
<div class="section-subtitle">Rozwiazania backupowe, cele, czestotliwosc, plan DR</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent6">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section" data-section="7">
|
|
<div class="section-header collapsed" onclick="toggleSection(7)">
|
|
<div class="section-number" id="sectionNumber7">7</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Monitoring</div>
|
|
<div class="section-subtitle">Rozwiazania monitoringu, Zabbix, logi</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent7">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section" data-section="8">
|
|
<div class="section-header collapsed" onclick="toggleSection(8)">
|
|
<div class="section-number" id="sectionNumber8">8</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Aplikacje Biznesowe</div>
|
|
<div class="section-subtitle">System ticketowy, ERP, CRM, dokumenty</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent8">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section" data-section="9">
|
|
<div class="section-header collapsed" onclick="toggleSection(9)">
|
|
<div class="section-number" id="sectionNumber9">9</div>
|
|
<div class="section-title-group">
|
|
<div class="section-title">Gotowosc do Wspolpracy</div>
|
|
<div class="section-subtitle">Mozliwosci wspolpracy z innymi firmami Norda Biznes</div>
|
|
</div>
|
|
<svg class="section-toggle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="section-content collapsed" id="sectionContent9">
|
|
<p style="color: var(--text-secondary); padding: var(--spacing-lg);">Ta sekcja zostanie dodana wkrotce...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="form-actions">
|
|
<div class="form-actions-left">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
</svg>
|
|
<span>Formularz mozna zapisac w dowolnym momencie. Postep zostanie zachowany.</span>
|
|
</div>
|
|
<div class="form-actions-right">
|
|
<button type="button" class="btn btn-outline" onclick="saveDraft()">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/>
|
|
</svg>
|
|
Zapisz wersje robocza
|
|
</button>
|
|
<button type="submit" class="btn btn-primary" id="submitBtn">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
</svg>
|
|
Zapisz audyt
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Loading Overlay -->
|
|
<div class="loading-overlay" id="loadingOverlay">
|
|
<div class="loading-content">
|
|
<div class="loading-spinner"></div>
|
|
<h3>Zapisywanie audytu...</h3>
|
|
<p>Prosze czekac, dane sa przetwarzane.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Modal -->
|
|
<div class="modal" id="infoModal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<div class="modal-icon success" id="modalIcon">
|
|
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="modal-title" id="modalTitle">Informacja</div>
|
|
</div>
|
|
<div class="modal-body" id="modalBody">
|
|
Tresc informacji.
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-primary" onclick="closeInfoModal()">OK</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
const csrfToken = '{{ csrf_token() }}';
|
|
const companyId = {{ company.id }};
|
|
|
|
// Track selected values for chip selects
|
|
const chipSelections = {
|
|
m365Plans: [],
|
|
teamsUsage: [],
|
|
serverTypes: [],
|
|
serverOs: []
|
|
};
|
|
|
|
// Toggle switch functionality
|
|
function toggleSwitch(fieldId) {
|
|
const toggle = document.getElementById('toggle' + fieldId.charAt(0).toUpperCase() + fieldId.slice(1));
|
|
const input = document.getElementById(fieldId);
|
|
const label = document.getElementById('label' + fieldId.charAt(0).toUpperCase() + fieldId.slice(1));
|
|
|
|
const isActive = toggle.classList.toggle('active');
|
|
input.value = isActive ? 'true' : 'false';
|
|
label.textContent = isActive ? 'Tak' : 'Nie';
|
|
|
|
// Handle conditional fields visibility
|
|
handleConditionalFields(fieldId, isActive);
|
|
updateProgress();
|
|
}
|
|
|
|
// Handle conditional fields visibility
|
|
function handleConditionalFields(fieldId, isActive) {
|
|
const conditionalMappings = {
|
|
'itOutsourced': ['itProviderFields'],
|
|
'hasAzureAd': ['azureAdFields', 'azureUserCountField'],
|
|
'hasM365': ['m365PlansField', 'teamsUsageField'],
|
|
'hasLocalAd': ['localAdFields', 'adSyncField']
|
|
};
|
|
|
|
const fieldsToToggle = conditionalMappings[fieldId];
|
|
if (fieldsToToggle) {
|
|
fieldsToToggle.forEach(fieldGroupId => {
|
|
const fieldGroup = document.getElementById(fieldGroupId);
|
|
if (fieldGroup) {
|
|
if (isActive) {
|
|
fieldGroup.classList.remove('hidden');
|
|
} else {
|
|
fieldGroup.classList.add('hidden');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Toggle chip selection
|
|
function toggleChip(element, fieldId) {
|
|
const value = element.dataset.value;
|
|
const isSelected = element.classList.toggle('selected');
|
|
|
|
if (isSelected) {
|
|
if (!chipSelections[fieldId].includes(value)) {
|
|
chipSelections[fieldId].push(value);
|
|
}
|
|
} else {
|
|
chipSelections[fieldId] = chipSelections[fieldId].filter(v => v !== value);
|
|
}
|
|
|
|
// Update hidden input
|
|
document.getElementById(fieldId).value = chipSelections[fieldId].join(',');
|
|
updateProgress();
|
|
}
|
|
|
|
// Toggle section visibility
|
|
function toggleSection(sectionNum) {
|
|
const header = document.querySelector(`.form-section[data-section="${sectionNum}"] .section-header`);
|
|
const content = document.getElementById('sectionContent' + sectionNum);
|
|
|
|
if (content) {
|
|
header.classList.toggle('collapsed');
|
|
content.classList.toggle('collapsed');
|
|
}
|
|
|
|
// Update progress dots
|
|
updateProgressDots(sectionNum);
|
|
}
|
|
|
|
// Update progress dots when navigating sections
|
|
function updateProgressDots(activeSectionNum) {
|
|
document.querySelectorAll('.progress-section-dot').forEach(dot => {
|
|
const section = parseInt(dot.dataset.section);
|
|
if (section === activeSectionNum) {
|
|
dot.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Calculate and update form progress
|
|
function updateProgress() {
|
|
const form = document.getElementById('itAuditForm');
|
|
const inputs = form.querySelectorAll('input:not([type="hidden"]), select');
|
|
let filledCount = 0;
|
|
let totalCount = 0;
|
|
|
|
inputs.forEach(input => {
|
|
if (input.offsetParent !== null) { // Check if visible
|
|
totalCount++;
|
|
if (input.value && input.value.trim() !== '') {
|
|
filledCount++;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Also count toggle switches
|
|
document.querySelectorAll('.toggle-switch.active').forEach(() => {
|
|
filledCount++;
|
|
});
|
|
|
|
// Count chip selections
|
|
Object.values(chipSelections).forEach(selection => {
|
|
if (selection.length > 0) {
|
|
filledCount++;
|
|
}
|
|
});
|
|
|
|
const percentage = totalCount > 0 ? Math.round((filledCount / Math.max(totalCount, 1)) * 100) : 0;
|
|
|
|
document.getElementById('progressPercentage').textContent = percentage + '%';
|
|
document.getElementById('progressFill').style.width = percentage + '%';
|
|
}
|
|
|
|
// Save draft
|
|
function saveDraft() {
|
|
const form = document.getElementById('itAuditForm');
|
|
const formData = new FormData(form);
|
|
formData.append('is_draft', 'true');
|
|
|
|
showLoading();
|
|
|
|
fetch(form.action, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
hideLoading();
|
|
if (data.success) {
|
|
showInfoModal('Zapisano', 'Wersja robocza audytu zostala zapisana.', true);
|
|
} else {
|
|
showInfoModal('Blad', data.error || 'Nie udalo sie zapisac wersji roboczej.', false);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
hideLoading();
|
|
showInfoModal('Blad polaczenia', 'Nie udalo sie polaczyc z serwerem: ' + error.message, false);
|
|
});
|
|
}
|
|
|
|
// Form submission
|
|
document.getElementById('itAuditForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(this);
|
|
|
|
showLoading();
|
|
|
|
fetch(this.action, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
hideLoading();
|
|
if (data.success) {
|
|
showInfoModal('Audyt zapisany', 'Audyt IT zostal zapisany pomyslnie. Za chwile zostaniesz przekierowany.', true);
|
|
setTimeout(() => {
|
|
window.location.href = data.redirect_url || '/company/' + companyId;
|
|
}, 1500);
|
|
} else {
|
|
showInfoModal('Blad', data.error || 'Nie udalo sie zapisac audytu.', false);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
hideLoading();
|
|
showInfoModal('Blad polaczenia', 'Nie udalo sie polaczyc z serwerem: ' + error.message, false);
|
|
});
|
|
});
|
|
|
|
// Loading overlay
|
|
function showLoading() {
|
|
document.getElementById('loadingOverlay').classList.add('active');
|
|
}
|
|
|
|
function hideLoading() {
|
|
document.getElementById('loadingOverlay').classList.remove('active');
|
|
}
|
|
|
|
// Info modal
|
|
function showInfoModal(title, body, isSuccess) {
|
|
document.getElementById('modalTitle').textContent = title;
|
|
document.getElementById('modalBody').textContent = body;
|
|
const icon = document.getElementById('modalIcon');
|
|
icon.className = 'modal-icon ' + (isSuccess ? 'success' : 'error');
|
|
document.getElementById('infoModal').classList.add('active');
|
|
}
|
|
|
|
function closeInfoModal() {
|
|
document.getElementById('infoModal').classList.remove('active');
|
|
}
|
|
|
|
// Close modal on outside click
|
|
document.getElementById('infoModal')?.addEventListener('click', (e) => {
|
|
if (e.target.id === 'infoModal') closeInfoModal();
|
|
});
|
|
|
|
// Initialize form with existing data if editing
|
|
{% if audit %}
|
|
function initializeForm() {
|
|
// Initialize toggle switches
|
|
{% if audit.has_it_manager %}
|
|
toggleSwitch('hasItManager');
|
|
{% endif %}
|
|
{% if audit.it_outsourced %}
|
|
toggleSwitch('itOutsourced');
|
|
{% endif %}
|
|
{% if audit.has_azure_ad %}
|
|
toggleSwitch('hasAzureAd');
|
|
{% endif %}
|
|
{% if audit.has_m365 %}
|
|
toggleSwitch('hasM365');
|
|
{% endif %}
|
|
{% if audit.has_google_workspace %}
|
|
toggleSwitch('hasGoogleWorkspace');
|
|
{% endif %}
|
|
{% if audit.has_local_ad %}
|
|
toggleSwitch('hasLocalAd');
|
|
{% endif %}
|
|
{% if audit.has_ad_azure_sync %}
|
|
toggleSwitch('hasAdAzureSync');
|
|
{% endif %}
|
|
|
|
// Initialize chip selections
|
|
{% if audit.m365_plans %}
|
|
'{{ audit.m365_plans | join(",") }}'.split(',').forEach(value => {
|
|
const chip = document.querySelector('#m365PlansChips .chip-option[data-value="' + value + '"]');
|
|
if (chip) toggleChip(chip, 'm365Plans');
|
|
});
|
|
{% endif %}
|
|
|
|
{% if audit.teams_usage %}
|
|
'{{ audit.teams_usage | join(",") }}'.split(',').forEach(value => {
|
|
const chip = document.querySelector('#teamsUsageChips .chip-option[data-value="' + value + '"]');
|
|
if (chip) toggleChip(chip, 'teamsUsage');
|
|
});
|
|
{% endif %}
|
|
|
|
{% if audit.server_types %}
|
|
'{{ audit.server_types | join(",") }}'.split(',').forEach(value => {
|
|
const chip = document.querySelector('#serverTypesChips .chip-option[data-value="' + value + '"]');
|
|
if (chip) toggleChip(chip, 'serverTypes');
|
|
});
|
|
{% endif %}
|
|
|
|
{% if audit.server_os %}
|
|
'{{ audit.server_os | join(",") }}'.split(',').forEach(value => {
|
|
const chip = document.querySelector('#serverOsChips .chip-option[data-value="' + value + '"]');
|
|
if (chip) toggleChip(chip, 'serverOs');
|
|
});
|
|
{% endif %}
|
|
|
|
updateProgress();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', initializeForm);
|
|
{% else %}
|
|
document.addEventListener('DOMContentLoaded', updateProgress);
|
|
{% endif %}
|
|
|
|
// Navigation via progress dots
|
|
document.querySelectorAll('.progress-section-dot').forEach(dot => {
|
|
dot.addEventListener('click', () => {
|
|
const sectionNum = parseInt(dot.dataset.section);
|
|
// Close all sections
|
|
document.querySelectorAll('.section-content').forEach(content => {
|
|
content.classList.add('collapsed');
|
|
});
|
|
document.querySelectorAll('.section-header').forEach(header => {
|
|
header.classList.add('collapsed');
|
|
});
|
|
// Open target section
|
|
const targetContent = document.getElementById('sectionContent' + sectionNum);
|
|
const targetHeader = document.querySelector(`.form-section[data-section="${sectionNum}"] .section-header`);
|
|
if (targetContent) {
|
|
targetContent.classList.remove('collapsed');
|
|
targetHeader?.classList.remove('collapsed');
|
|
// Scroll to section
|
|
document.querySelector(`.form-section[data-section="${sectionNum}"]`).scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
// Update active dot
|
|
document.querySelectorAll('.progress-section-dot').forEach(d => d.classList.remove('active'));
|
|
dot.classList.add('active');
|
|
});
|
|
});
|
|
{% endblock %}
|