699 lines
24 KiB
HTML
699 lines
24 KiB
HTML
{% extends "layout.html" %}
|
|
|
|
{% block content %}
|
|
<div class="company-admin-container">
|
|
<!-- Header Section -->
|
|
<div class="page-header">
|
|
<div class="header-content">
|
|
<div class="header-left">
|
|
<h1 class="page-title">
|
|
<span class="page-icon"><i class="ti ti-building"></i></span>
|
|
Company Management
|
|
</h1>
|
|
<p class="page-subtitle">Configure your company settings and policies</p>
|
|
</div>
|
|
<div class="header-actions">
|
|
<a href="{{ url_for('setup_company') }}" class="btn btn-primary">
|
|
<span class="icon">+</span>
|
|
Create New Company
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Company Statistics -->
|
|
<div class="stats-section">
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.total_users }}</div>
|
|
<div class="stat-label">Total Users</div>
|
|
<a href="{{ url_for('companies.company_users') }}" class="stat-link">View all <i class="ti ti-arrow-right"></i></a>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.total_teams }}</div>
|
|
<div class="stat-label">Teams</div>
|
|
<a href="{{ url_for('organization.admin_organization') }}" class="stat-link">Manage <i class="ti ti-arrow-right"></i></a>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.total_projects }}</div>
|
|
<div class="stat-label">Total Projects</div>
|
|
<a href="{{ url_for('projects.admin_projects') }}" class="stat-link">View all <i class="ti ti-arrow-right"></i></a>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.active_projects }}</div>
|
|
<div class="stat-label">Active Projects</div>
|
|
<a href="{{ url_for('projects.admin_projects') }}" class="stat-link">Manage <i class="ti ti-arrow-right"></i></a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content Grid -->
|
|
<div class="content-grid">
|
|
<!-- Left Column -->
|
|
<div class="content-column">
|
|
<!-- Company Information Card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">
|
|
<span class="icon"><i class="ti ti-info-circle"></i></span>
|
|
Company Information
|
|
</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST" class="modern-form">
|
|
<input type="hidden" name="action" value="update_company_details">
|
|
|
|
<div class="form-group">
|
|
<label for="name" class="form-label">Company Name</label>
|
|
<input type="text" id="name" name="name" class="form-control"
|
|
value="{{ company.name }}" required>
|
|
<span class="form-hint">The official name of your company</span>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="max_users" class="form-label">Maximum Users</label>
|
|
<input type="number" id="max_users" name="max_users" class="form-control"
|
|
value="{{ company.max_users or '' }}" min="1" placeholder="Unlimited">
|
|
<span class="form-hint">Leave empty for unlimited</span>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Status</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="is_active" name="is_active"
|
|
{{ 'checked' if company.is_active else '' }}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
<span class="toggle-label">Company is {{ 'active' if company.is_active else 'inactive' }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="description" class="form-label">Description</label>
|
|
<textarea id="description" name="description" class="form-control"
|
|
rows="3" placeholder="Brief description of your company...">{{ company.description or '' }}</textarea>
|
|
<span class="form-hint">Optional: Describe your company's mission or purpose</span>
|
|
</div>
|
|
|
|
<div class="info-panel">
|
|
<div class="info-item">
|
|
<span class="info-icon"><i class="ti ti-key"></i></span>
|
|
<div class="info-content">
|
|
<label class="info-label">Company Code</label>
|
|
<div class="code-display">
|
|
<input type="text" value="{{ company.slug }}" readonly id="companyCode" class="code-input">
|
|
<button type="button" class="btn btn-copy" onclick="copyCompanyCode()">
|
|
<span id="copyIcon"><i class="ti ti-clipboard"></i></span>
|
|
<span id="copyText">Copy</span>
|
|
</button>
|
|
</div>
|
|
<span class="info-hint">Legacy: Use email invitations instead</span>
|
|
</div>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-icon"><i class="ti ti-calendar"></i></span>
|
|
<div class="info-content">
|
|
<label class="info-label">Created</label>
|
|
<span class="info-value">{{ company.created_at|format_datetime }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn-primary">
|
|
<span class="icon"><i class="ti ti-check"></i></span>
|
|
Save Company Details
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions Card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">
|
|
<span class="icon"><i class="ti ti-bolt"></i></span>
|
|
Quick Actions
|
|
</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="action-grid">
|
|
<a href="{{ url_for('organization.admin_organization') }}" class="action-item">
|
|
<div class="action-icon"><i class="ti ti-sitemap"></i></div>
|
|
<div class="action-content">
|
|
<h3>Manage Organization</h3>
|
|
<p>Users, teams & structure</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="{{ url_for('projects.admin_projects') }}" class="action-item">
|
|
<div class="action-icon"><i class="ti ti-folder"></i></div>
|
|
<div class="action-content">
|
|
<h3>Manage Projects</h3>
|
|
<p>Time tracking projects</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="{{ url_for('invitations.send_invitation') }}" class="action-item">
|
|
<div class="action-icon"><i class="ti ti-mail"></i></div>
|
|
<div class="action-content">
|
|
<h3>Send Invitation</h3>
|
|
<p>Invite team members</p>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column -->
|
|
<div class="content-column">
|
|
<!-- Work Policies Card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">
|
|
<span class="icon"><i class="ti ti-clipboard-list"></i></span>
|
|
Work Policies
|
|
</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Regional Presets -->
|
|
<div class="preset-section">
|
|
<h3 class="section-title">Regional Preset</h3>
|
|
<form method="POST" action="{{ url_for('companies.admin_company') }}" class="preset-form">
|
|
<input type="hidden" name="action" value="update_work_policies">
|
|
<input type="hidden" name="apply_preset" value="true">
|
|
<select name="region_preset" class="form-control form-select" onchange="this.form.submit()">
|
|
<option value="">Select a regional preset...</option>
|
|
{% for preset in regional_presets %}
|
|
<option value="{{ preset.code }}" {% if work_config.work_region.value == preset.code %}selected{% endif %}>
|
|
{{ preset.name }} - {{ preset.description }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Current Configuration -->
|
|
<div class="config-section">
|
|
<h3 class="section-title">
|
|
Current Configuration
|
|
<span class="config-badge">{{ work_config.work_region.value if work_config.work_region else 'Custom' }}</span>
|
|
</h3>
|
|
<form method="POST" action="{{ url_for('companies.admin_company') }}" class="modern-form">
|
|
<input type="hidden" name="action" value="update_work_policies">
|
|
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="standard_hours_per_day" class="form-label">Hours per Day</label>
|
|
<input type="number" id="standard_hours_per_day" name="standard_hours_per_day"
|
|
class="form-control" value="{{ work_config.standard_hours_per_day }}"
|
|
step="0.5" min="1" max="24" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="standard_hours_per_week" class="form-label">Hours per Week</label>
|
|
<input type="number" id="standard_hours_per_week" name="standard_hours_per_week"
|
|
class="form-control" value="{{ work_config.standard_hours_per_week }}"
|
|
step="0.5" min="1" max="168" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="break_duration_minutes" class="form-label">Break Duration (min)</label>
|
|
<input type="number" id="break_duration_minutes" name="break_duration_minutes"
|
|
class="form-control" value="{{ work_config.break_duration_minutes }}"
|
|
min="0" max="120" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="break_after_hours" class="form-label">Break After (hours)</label>
|
|
<input type="number" id="break_after_hours" name="break_after_hours"
|
|
class="form-control" value="{{ work_config.break_after_hours }}"
|
|
step="0.5" min="0" max="24" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="overtime_rate" class="form-label">Overtime Rate</label>
|
|
<input type="number" id="overtime_rate" name="overtime_rate"
|
|
class="form-control" value="{{ work_config.overtime_rate }}"
|
|
step="0.1" min="1" max="3" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn-primary">
|
|
<span class="icon"><i class="ti ti-check"></i></span>
|
|
Update Policies
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Registration Settings Card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">
|
|
<span class="icon"><i class="ti ti-user"></i></span>
|
|
User Registration
|
|
</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST" action="{{ url_for('companies.admin_company') }}" class="modern-form">
|
|
<input type="hidden" name="action" value="update_system_settings">
|
|
|
|
<div class="settings-list">
|
|
<div class="setting-item">
|
|
<div class="setting-toggle">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="registration_enabled" name="registration_enabled"
|
|
{% if settings.registration_enabled %}checked{% endif %}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-content">
|
|
<h4 class="setting-title">Enable User Registration</h4>
|
|
<p class="setting-description">Allow new users to register accounts using the company code</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="setting-item">
|
|
<div class="setting-toggle">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="email_verification_required" name="email_verification_required"
|
|
{% if settings.email_verification_required %}checked{% endif %}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-content">
|
|
<h4 class="setting-title">Require Email Verification</h4>
|
|
<p class="setting-description">New users must verify their email address before accessing the system</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn-primary">
|
|
<span class="icon"><i class="ti ti-check"></i></span>
|
|
Update Settings
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* Container */
|
|
|
|
/* Company Admin specific styles */
|
|
|
|
.stat-link {
|
|
position: absolute;
|
|
bottom: 0.75rem;
|
|
right: 1rem;
|
|
font-size: 0.875rem;
|
|
color: #667eea;
|
|
text-decoration: none;
|
|
opacity: 0.7;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.stat-link:hover {
|
|
opacity: 1;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Company-specific content grid - already defined in common styles */
|
|
|
|
/* Company-specific card styles */
|
|
.card-title .icon {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
/* Company-specific form styles */
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.form-control {
|
|
padding: 0.75rem 1rem;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
font-size: 1rem;
|
|
transition: all 0.2s ease;
|
|
background-color: #f9fafb;
|
|
}
|
|
|
|
.form-control:focus {
|
|
outline: none;
|
|
border-color: #667eea;
|
|
background-color: white;
|
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
}
|
|
|
|
.form-select {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.form-hint {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* Toggle Switch */
|
|
.toggle-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.toggle-switch {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 60px;
|
|
height: 32px;
|
|
}
|
|
|
|
.toggle-switch input {
|
|
opacity: 0;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
|
|
.toggle-slider {
|
|
position: absolute;
|
|
cursor: pointer;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: #cbd5e1;
|
|
transition: .4s;
|
|
border-radius: 34px;
|
|
}
|
|
|
|
.toggle-slider:before {
|
|
position: absolute;
|
|
content: "";
|
|
height: 24px;
|
|
width: 24px;
|
|
left: 4px;
|
|
bottom: 4px;
|
|
background-color: white;
|
|
transition: .4s;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
input:checked + .toggle-slider {
|
|
background-color: #667eea;
|
|
}
|
|
|
|
input:checked + .toggle-slider:before {
|
|
transform: translateX(28px);
|
|
}
|
|
|
|
.toggle-label {
|
|
font-weight: 500;
|
|
color: #374151;
|
|
}
|
|
|
|
/* Info Panel */
|
|
.info-panel {
|
|
background: #f3f4f6;
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
gap: 1rem;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.info-icon {
|
|
font-size: 1.5rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.info-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.info-label {
|
|
font-weight: 600;
|
|
color: #374151;
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.info-value {
|
|
color: #667eea;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.info-hint {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.code-display {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.code-input {
|
|
font-family: 'Monaco', 'Courier New', monospace;
|
|
font-weight: 600;
|
|
color: #667eea;
|
|
background: white;
|
|
border: 2px solid #e5e7eb;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 6px;
|
|
flex: 1;
|
|
max-width: 300px;
|
|
}
|
|
|
|
/* Company-specific button styles */
|
|
|
|
.btn-copy {
|
|
background: white;
|
|
color: #6b7280;
|
|
border: 2px solid #e5e7eb;
|
|
padding: 0.5rem 1rem;
|
|
}
|
|
|
|
.btn-copy:hover {
|
|
background: #f3f4f6;
|
|
border-color: #d1d5db;
|
|
}
|
|
|
|
.btn-copy.success {
|
|
background: #d1fae5;
|
|
color: #059669;
|
|
border-color: #10b981;
|
|
}
|
|
|
|
/* Company-specific action grid - already defined in common styles */
|
|
|
|
.action-item {
|
|
display: flex;
|
|
gap: 1rem;
|
|
padding: 1.25rem;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
transition: all 0.2s ease;
|
|
border: 2px solid transparent;
|
|
}
|
|
|
|
.action-item:hover {
|
|
background: white;
|
|
border-color: #667eea;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
|
}
|
|
|
|
.action-icon {
|
|
font-size: 2rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.action-icon i {
|
|
font-size: 2rem;
|
|
color: #667eea;
|
|
}
|
|
|
|
.action-content h3 {
|
|
font-size: 1.05rem;
|
|
font-weight: 600;
|
|
color: #1f2937;
|
|
margin: 0 0 0.25rem 0;
|
|
}
|
|
|
|
.action-content p {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Work Policies */
|
|
.preset-section,
|
|
.config-section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: #1f2937;
|
|
margin-bottom: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.config-badge {
|
|
background: #ede9fe;
|
|
color: #5b21b6;
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Settings List */
|
|
.settings-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.setting-item {
|
|
display: flex;
|
|
gap: 1rem;
|
|
padding: 1rem;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.setting-toggle {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.setting-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.setting-title {
|
|
font-size: 1.05rem;
|
|
font-weight: 600;
|
|
color: #1f2937;
|
|
margin: 0 0 0.25rem 0;
|
|
}
|
|
|
|
.setting-description {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Company-specific responsive styles */
|
|
@media (max-width: 768px) {
|
|
.company-admin-container {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.form-row,
|
|
.form-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.code-input {
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Animations */
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.card {
|
|
animation: slideIn 0.3s ease-out;
|
|
animation-fill-mode: both;
|
|
}
|
|
|
|
.card:nth-child(1) { animation-delay: 0.1s; }
|
|
.card:nth-child(2) { animation-delay: 0.2s; }
|
|
.card:nth-child(3) { animation-delay: 0.3s; }
|
|
</style>
|
|
|
|
<script>
|
|
function copyCompanyCode() {
|
|
const codeInput = document.getElementById('companyCode');
|
|
codeInput.select();
|
|
codeInput.setSelectionRange(0, 99999);
|
|
|
|
try {
|
|
document.execCommand('copy');
|
|
|
|
// Show feedback
|
|
const button = event.currentTarget;
|
|
const copyIcon = document.getElementById('copyIcon');
|
|
const copyText = document.getElementById('copyText');
|
|
|
|
// Store original values
|
|
const originalIcon = copyIcon.innerHTML;
|
|
const originalText = copyText.textContent;
|
|
|
|
// Update to success state
|
|
copyIcon.innerHTML = '<i class="ti ti-check"></i>';
|
|
copyText.textContent = 'Copied!';
|
|
button.classList.add('success');
|
|
|
|
// Reset after 2 seconds
|
|
setTimeout(() => {
|
|
copyIcon.innerHTML = originalIcon;
|
|
copyText.textContent = originalText;
|
|
button.classList.remove('success');
|
|
}, 2000);
|
|
} catch (err) {
|
|
alert('Failed to copy code. Please select and copy manually.');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{% endblock %} |