Files
BetterDesk/web-nodejs/views/commercialization.ejs
T
UNITRONIX 5a51548ebe feat(billing): refactor billing contract management and enhance API
- Updated billing contract structure to support multiple target types (organization, device group, folder, device).
- Refactored API handlers to accommodate new billing contract model, including creation, deletion, and listing functionalities.
- Enhanced database interactions for billing contracts, ensuring backward compatibility with legacy billing_org_contracts.
- Added new billing statistics endpoint to provide insights on active sessions and expiring contracts.
- Improved error handling and validation in billing package management.
2026-07-09 06:39:25 +02:00

291 lines
17 KiB
HTML

<%- include('layouts/main', {
title: title,
pageStyles: pageStyles,
pageScripts: pageScripts,
currentPage: currentPage,
breadcrumb: breadcrumb,
body: `
<div class="commercialization-page" data-active-tab="${typeof activeTab !== 'undefined' ? activeTab : 'overview'}">
<div class="page-header">
<h1>${activeTab === 'overview' ? _('commercialization.title') : _('commercialization.tabs.' + activeTab)} <span class="beta-badge" title="${_('commercialization.alpha_tooltip')}">${_('commercialization.alpha_badge')}</span></h1>
<p class="page-subtitle">${activeTab === 'overview' ? _('commercialization.subtitle') : _('commercialization.title')}</p>
</div>
<div id="timesync-banner" class="timesync-banner hidden"></div>
<section id="tab-overview" class="tab-panel ${activeTab === 'overview' ? '' : 'hidden'}">
<div class="card-grid">
<div class="stat-card" id="stat-clock">
<h3>${_('commercialization.clock.title')}</h3>
<p class="stat-value" id="clock-status"></p>
<p class="stat-meta" id="clock-offset"></p>
</div>
<div class="stat-card">
<h3>${_('commercialization.stats.active_sessions')}</h3>
<p class="stat-value" id="stat-active-sessions">0</p>
</div>
<div class="stat-card">
<h3>${_('commercialization.stats.expiring_contracts')}</h3>
<p class="stat-value" id="stat-expiring-contracts">0</p>
<p class="stat-meta">${_('commercialization.stats.expiring_hint')}</p>
</div>
</div>
</section>
<section id="tab-packages" class="tab-panel ${activeTab === 'packages' ? '' : 'hidden'}">
<div class="panel-toolbar">
<button type="button" class="btn btn-primary" id="btn-new-package">${_('commercialization.packages.new')}</button>
<button type="button" class="btn btn-secondary" id="btn-new-contract">${_('commercialization.contracts.new')}</button>
</div>
<h3 class="section-heading">${_('commercialization.packages.heading')}</h3>
<div class="table-wrap">
<table class="data-table" id="packages-table">
<thead>
<tr>
<th>${_('commercialization.packages.name')}</th>
<th>${_('commercialization.packages.description')}</th>
<th>${_('commercialization.packages.included_minutes')}</th>
<th>${_('commercialization.packages.overage_rate')}</th>
<th>${_('commercialization.packages.currency')}</th>
<th>${_('commercialization.contracts.actions')}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<h3 class="section-heading">${_('commercialization.contracts.assignments_title')}</h3>
<div class="panel-toolbar filters-toolbar">
<label class="filter-label">${_('commercialization.filters.target_type')}
<select id="filter-contract-type" class="form-control">
<option value="">${_('commercialization.filters.all_types')}</option>
<option value="org">${_('commercialization.targets.org')}</option>
<option value="device_group">${_('commercialization.targets.device_group')}</option>
<option value="folder">${_('commercialization.targets.folder')}</option>
<option value="device">${_('commercialization.targets.device')}</option>
</select>
</label>
<label class="filter-label">${_('commercialization.contracts.status')}
<select id="filter-contract-status" class="form-control">
<option value="">${_('commercialization.filters.all_statuses')}</option>
<option value="active">${_('commercialization.status.active')}</option>
<option value="suspended">${_('commercialization.status.suspended')}</option>
</select>
</label>
<label class="filter-label">${_('commercialization.filters.search')}
<input type="search" id="filter-contract-search" class="form-control" placeholder="${_('commercialization.filters.search_placeholder')}">
</label>
</div>
<div class="table-wrap">
<table class="data-table" id="contracts-table">
<thead>
<tr>
<th>${_('commercialization.contracts.target')}</th>
<th>${_('commercialization.contracts.package')}</th>
<th>${_('commercialization.contracts.remaining')}</th>
<th>${_('commercialization.contracts.status')}</th>
<th>${_('commercialization.contracts.validity')}</th>
<th>${_('commercialization.contracts.actions')}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</section>
<section id="tab-sessions" class="tab-panel ${activeTab === 'sessions' ? '' : 'hidden'}">
<div class="panel-toolbar filters-toolbar">
<label class="filter-label">${_('commercialization.filters.organization')}
<select id="filter-session-org" class="form-control"></select>
</label>
<label class="filter-label">${_('commercialization.contracts.status')}
<select id="filter-session-status" class="form-control">
<option value="">${_('commercialization.filters.all_statuses')}</option>
<option value="active">${_('commercialization.status.active')}</option>
<option value="pending_report">${_('commercialization.status.pending_report')}</option>
<option value="closed">${_('commercialization.status.closed')}</option>
</select>
</label>
<label class="filter-label">${_('commercialization.sessions.device')}
<input type="text" id="filter-session-device" class="form-control" placeholder="${_('commercialization.filters.device_id_placeholder')}">
</label>
<button type="button" class="btn btn-secondary" id="btn-refresh-sessions">${_('commercialization.actions.refresh')}</button>
<button type="button" class="btn btn-secondary" id="btn-export-sessions">${_('commercialization.export.sessions_csv')}</button>
</div>
<div class="table-wrap">
<table class="data-table" id="sessions-table">
<thead>
<tr>
<th>${_('commercialization.sessions.device')}</th>
<th>${_('commercialization.sessions.org')}</th>
<th>${_('commercialization.sessions.operator')}</th>
<th>${_('commercialization.sessions.duration')}</th>
<th>${_('commercialization.sessions.phase')}</th>
<th>${_('commercialization.sessions.amount')}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</section>
<section id="tab-reports" class="tab-panel ${activeTab === 'reports' ? '' : 'hidden'}">
<div class="panel-toolbar filters-toolbar">
<label class="filter-label">${_('commercialization.filters.organization')}
<select id="filter-report-org" class="form-control"></select>
</label>
<button type="button" class="btn btn-secondary" id="btn-refresh-reports">${_('commercialization.actions.refresh')}</button>
<button type="button" class="btn btn-secondary" id="btn-export-reports-csv">${_('commercialization.export.csv')}</button>
<button type="button" class="btn btn-secondary" id="btn-export-reports-pdf">${_('commercialization.export.pdf')}</button>
</div>
<div class="table-wrap">
<table class="data-table" id="reports-table">
<thead>
<tr>
<th>${_('commercialization.reports.session')}</th>
<th>${_('commercialization.reports.summary')}</th>
<th>${_('commercialization.reports.date')}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</section>
<section id="tab-settings" class="tab-panel ${activeTab === 'settings' ? '' : 'hidden'}">
<div class="card">
<h3>${_('commercialization.clock.title')}</h3>
<p id="settings-clock-detail" class="text-muted"></p>
<dl class="clock-status-grid">
<div><dt>${_('commercialization.clock.status_offset')}</dt><dd id="clock-offset"></dd></div>
<div><dt>${_('commercialization.clock.status_server')}</dt><dd id="clock-ntp-server"></dd></div>
<div><dt>${_('commercialization.clock.status_os')}</dt><dd id="clock-os-sync"></dd></div>
<div><dt>${_('commercialization.clock.status_error')}</dt><dd id="clock-last-error"></dd></div>
</dl>
<button type="button" class="btn btn-secondary" id="btn-timesync-check">${_('commercialization.clock.check_now')}</button>
<form id="clock-settings-form" class="settings-form" style="margin-top: 1rem;">
<label class="form-label">${_('commercialization.clock.ntp_servers')}
<textarea id="clock-ntp-servers" class="form-control" rows="2" placeholder="pool.ntp.org,time.google.com"></textarea>
</label>
<p class="text-muted form-hint">${_('commercialization.clock.ntp_servers_hint')}</p>
<label class="form-label">${_('commercialization.clock.max_skew_ms')}
<input type="number" id="clock-max-skew" class="form-control" min="100" max="600000" step="100" value="2000">
</label>
<div class="form-group">
<label><input type="checkbox" id="clock-require-sync"> ${_('commercialization.clock.require_sync')}</label>
</div>
<div class="form-group">
<label><input type="checkbox" id="clock-trust-os"> ${_('commercialization.clock.trust_os')}</label>
</div>
<p class="text-muted form-hint">${_('commercialization.clock.trust_os_hint')}</p>
<button type="button" class="btn btn-primary" id="btn-save-clock-settings">${_('commercialization.clock.save')}</button>
</form>
</div>
<div class="card" style="margin-top: 1rem;">
<h3>${_('commercialization.notifications.title')}</h3>
<p class="text-muted">${_('commercialization.notifications.desc')}</p>
<p id="email-smtp-status" class="smtp-status-line"></p>
<p><a href="/settings#email">${_('commercialization.notifications.smtp_link')}</a></p>
<form id="email-notifications-form" class="settings-form">
<div class="form-group">
<label><input type="checkbox" id="notif-help-requests-enabled"> ${_('commercialization.notifications.help_requests_enabled')}</label>
</div>
<div class="form-group">
<label><input type="checkbox" id="notif-assigned-operators"> ${_('commercialization.notifications.notify_assigned_operators')}</label>
</div>
<div class="form-group">
<label><input type="checkbox" id="notif-fallback-alert"> ${_('commercialization.notifications.fallback_alert_email')}</label>
</div>
<div class="form-group">
<label><input type="checkbox" id="notif-folder-subject"> ${_('commercialization.notifications.include_folder_in_subject')}</label>
</div>
<button type="button" class="btn btn-primary" id="btn-save-email-notifications">${_('commercialization.notifications.save')}</button>
</form>
</div>
</section>
</div>
<div class="modal-overlay" id="package-modal">
<div class="modal" role="dialog" aria-modal="true">
<div class="modal-header">
<h2 class="modal-title" id="package-modal-title">${_('commercialization.packages.create_title')}</h2>
<button type="button" class="modal-close" data-close-modal="package-modal">&times;</button>
</div>
<div class="modal-body">
<label class="form-label">${_('commercialization.packages.name')}
<input type="text" id="package-name" class="form-control" maxlength="128" required>
</label>
<label class="form-label">${_('commercialization.packages.description')}
<textarea id="package-description" class="form-control" rows="2" maxlength="512"></textarea>
</label>
<label class="form-label">${_('commercialization.packages.included_minutes')}
<input type="number" id="package-minutes" class="form-control" value="600" min="1" step="1" required>
</label>
<label class="form-label">${_('commercialization.packages.overage_rate')}
<input type="number" id="package-overage" class="form-control" value="100" min="0" step="0.01" required>
</label>
<label class="form-label">${_('commercialization.packages.currency')}
<input type="text" id="package-currency" class="form-control" value="PLN" maxlength="8" required>
</label>
<p id="package-modal-error" class="form-error hidden"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-close-modal="package-modal">${_('commercialization.form.cancel')}</button>
<button type="button" class="btn btn-primary" id="package-modal-submit">${_('commercialization.packages.create_submit')}</button>
</div>
</div>
</div>
<div class="modal-overlay" id="assign-modal">
<div class="modal modal-wide" role="dialog" aria-modal="true">
<div class="modal-header">
<h2 class="modal-title" id="assign-modal-title">${_('commercialization.assign.title')}</h2>
<button type="button" class="modal-close" data-close-modal="assign-modal">&times;</button>
</div>
<div class="modal-body">
<label class="form-label">${_('commercialization.assign.target_type')}
<select id="assign-target-type" class="form-control">
<option value="org">${_('commercialization.targets.org')}</option>
<option value="device_group">${_('commercialization.targets.device_group')}</option>
<option value="folder">${_('commercialization.targets.folder')}</option>
<option value="device">${_('commercialization.targets.device')}</option>
</select>
</label>
<label class="form-label">${_('commercialization.assign.target')}
<select id="assign-target-select" class="form-control" required></select>
</label>
<label class="form-label">${_('commercialization.contracts.package')}
<select id="assign-package-select" class="form-control" required></select>
</label>
<div class="form-row">
<label class="form-label">${_('commercialization.contracts.remaining')}
<input type="number" id="assign-remaining" class="form-control" min="0" step="1" placeholder="${_('commercialization.assign.optional')}">
</label>
<label class="form-label">${_('commercialization.contracts.hourly_rate')}
<input type="number" id="assign-hourly-rate" class="form-control" min="0" step="0.01" placeholder="${_('commercialization.assign.optional')}">
</label>
</div>
<div class="form-row">
<label class="form-label">${_('commercialization.contracts.custom_overage')}
<input type="number" id="assign-overage-rate" class="form-control" min="0" step="0.01" placeholder="${_('commercialization.assign.optional')}">
</label>
<label class="form-label">${_('commercialization.contracts.valid_from')}
<input type="date" id="assign-valid-from" class="form-control">
</label>
<label class="form-label">${_('commercialization.contracts.valid_until')}
<input type="date" id="assign-valid-until" class="form-control">
</label>
</div>
<p id="assign-modal-error" class="form-error hidden"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-close-modal="assign-modal">${_('commercialization.form.cancel')}</button>
<button type="button" class="btn btn-primary" id="assign-modal-submit">${_('commercialization.assign.submit')}</button>
</div>
</div>
</div>
`
}) %>