Files
sao2.0/index.html
2026-07-19 16:57:58 +02:00

466 lines
25 KiB
HTML

<!DOCTYPE html>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<html style="overflow: auto;" lang="en" theme="default">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Tryton</title>
<link rel="icon" type="image/png" href="images/tradon-icon.png">
<script type="text/javascript" src="bower_components/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript" src="bower_components/moment/moment.js"></script>
<script type="text/javascript" src="bower_components/moment/min/locales.js"></script>
<script type="text/javascript" src="bower_components/gettext.js/dist/gettext.js"></script>
<script type="text/javascript" src="bower_components/d3/d3.js"></script>
<script type="text/javascript" src="bower_components/c3/c3.js"></script>
<script type="text/javascript" src="bower_components/papaparse/papaparse.js"></script>
<script type="text/javascript" src="bower_components/fullcalendar/dist/fullcalendar.js"></script>
<script type="text/javascript" src="bower_components/fullcalendar/dist/locale-all.js"></script>
<script type="text/javascript" src="bower_components/mousetrap/mousetrap.js"></script>
<script type="text/javascript" src="bower_components/mousetrap/plugins/pause/mousetrap-pause.js"></script>
<script type="text/javascript" src="bower_components/Sortable/Sortable.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ag-grid-community@31.3.4/dist/ag-grid-community.min.js"></script>
<link rel="stylesheet" href="bower_components/c3/c3.css">
<link rel="stylesheet" href="bower_components/fullcalendar/dist/fullcalendar.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@31.3.4/styles/ag-grid.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@31.3.4/styles/ag-theme-alpine.css">
<script type="text/javascript" src="dist/tryton-sao_v1.2.js"></script>
<link rel="stylesheet" type="text/css" href="dist/tryton-sao_v1.2.css" media="screen"/>
<script type="text/javascript" src="custom.js"></script>
<link rel="stylesheet" type="text/css" href="custom.css" media="screen"/>
<script type="text/javascript">
(function() {
try {
var storedMode = window.localStorage &&
localStorage.getItem('sao_theme_mode');
document.documentElement.setAttribute(
'data-theme-mode',
storedMode || 'light');
} catch (error) {
document.documentElement.setAttribute(
'data-theme-mode', 'light');
}
try {
var storedDensity = window.localStorage &&
localStorage.getItem('sao_density_mode');
document.documentElement.setAttribute(
'data-density',
storedDensity || 'compact');
} catch (error) {
document.documentElement.setAttribute(
'data-density', 'compact');
}
}());
</script>
</head>
<body>
<noscript>
<strong>Sorry sao does not work without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div class="body" style="display: none;">
<nav class="navbar navbar-inverse navbar-static-top" role="navigation" style="margin-bottom: 0px;">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#main_navbar">
<span class="caret"></span>
</button>
<a class="navbar-brand" href="javascript:void(0)" data-toggle="offcanvas">
<span class="icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
</svg>
</span>
<span id="title">Tryton</span>
</a>
<button type="button" id="theme-mode-toggle"
class="btn btn-default navbar-btn theme-mode-toggle"
title="Toggle dark mode"
aria-label="Toggle dark mode"
aria-pressed="false">
<span class="theme-mode-toggle-icon" aria-hidden="true">
<svg viewBox="0 0 20 20" focusable="false">
<path d="M10 2.5v2.1"></path>
<path d="M10 15.4v2.1"></path>
<path d="M4.7 4.7l1.5 1.5"></path>
<path d="M13.8 13.8l1.5 1.5"></path>
<path d="M2.5 10h2.1"></path>
<path d="M15.4 10h2.1"></path>
<path d="M4.7 15.3l1.5-1.5"></path>
<path d="M13.8 6.2l1.5-1.5"></path>
<circle cx="10" cy="10" r="3.3"></circle>
</svg>
</span>
</button>
</div>
<div class="collapse navbar-collapse" id="main_navbar">
<form class="navbar-form navbar-left flip" role="search" id="global-search" style="border-style: none;">
</form>
<ul class="nav navbar-nav navbar-right flip">
<li id="user-preferences"></li>
<li id="user-logout">
<a href="#">
<span class="icon hidden-xs">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
</svg>
</span>
<span class="visible-xs">
</span>
</a>
</li>
</ul>
<ul id="tablist" class="nav navbar-nav nav-pills" role="tablist">
</ul>
</div>
</div>
</nav>
<div id="main" class="container-fluid" style="padding: 0px;">
<div id="tradon-full-menu" class="tradon-full-menu" aria-hidden="true">
<div class="tradon-full-menu-grid"></div>
</div>
<div class="row row-offcanvas row-offcanvas-left active"
style="height: calc(100vh - 50px - 1px - 20px); margin: 0px;">
<div class="col-xs-12 col-sm-4 col-lg-3 sidebar-offcanvas" id="menu" role="navigation"
style="overflow-y: auto; height: 100%;"></div>
<div class="col-xs-12 col-sm-8 col-lg-9 main-offcanvas" style="display:flex; overflow-y: auto; height: 100%;" id="tabs" role="tabpanel">
<div id="tabcontent" class="tab-content"></div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
if ((typeof jQuery == 'undefined') || (typeof Sao == 'undefined')) {
document.querySelector('body').innerHTML = '<strong>sao is not fully installed. Please refer to <a href="README.md">README</a></strong>';
} else {
jQuery(function() {
function applyThemeMode(mode) {
var isDark = mode === 'dark';
document.documentElement.setAttribute(
'data-theme-mode', isDark ? 'dark' : 'light');
try {
if (window.localStorage) {
localStorage.setItem(
'sao_theme_mode', isDark ? 'dark' : 'light');
}
} catch (error) {
}
var toggle = jQuery('#theme-mode-toggle');
toggle.attr('aria-pressed', isDark ? 'true' : 'false');
toggle.attr(
'title',
isDark ? 'Switch to light mode' :
'Switch to dark mode');
var logo = jQuery('#title-logo');
if (logo.length) {
logo.attr(
'src',
'dist/tradon_wbg.png');
logo.attr('title', 'v1.2');
}
}
jQuery('#theme-mode-toggle').click(function() {
var current = document.documentElement.getAttribute(
'data-theme-mode');
applyThemeMode(current === 'dark' ? 'light' : 'dark');
});
applyThemeMode(
document.documentElement.getAttribute('data-theme-mode'));
jQuery('.body').show();
function notifyLayoutResize() {
[0, 80, 180, 320].forEach(function(delay) {
window.setTimeout(function() {
window.dispatchEvent(new Event('resize'));
}, delay);
});
}
// Keep the full-screen menu code in place, but disable its
// loading and logo entry point while the layout is refined.
var enableFullMenu = false;
function setFullMenuVisible(visible) {
var main = jQuery('#main');
if (visible && !jQuery('#tradon-full-menu')
.find('.tradon-full-menu-card').length) {
visible = false;
}
jQuery('#tradon-full-menu').attr(
'aria-hidden', visible ? 'false' : 'true');
main.toggleClass('tradon-full-menu-active', visible);
if (visible) {
jQuery('.row-offcanvas').removeClass('active');
}
notifyLayoutResize();
}
function normalizeMenuValue(value) {
if (value === null || value === undefined) {
return '';
}
if (Array.isArray(value)) {
return value.length > 1 ? value[1] : value[0];
}
if (typeof value == 'object') {
return value.rec_name || value.name || '';
}
return value;
}
function menuField(record, name) {
if (name == 'rec_name' && record._values &&
record._values.rec_name) {
return record._values.rec_name;
}
try {
if (record.model && record.model.fields[name]) {
return normalizeMenuValue(
record.field_get_client(name));
}
} catch (error) {
}
return null;
}
function menuLabel(record) {
return menuField(record, 'name') ||
menuField(record, 'rec_name') ||
(record.id ? String(record.id) : '');
}
function prepareMenuRecord(record) {
var waits = [];
['name', 'icon'].forEach(function(name) {
if (record.model && record.model.fields[name] &&
!record.is_loaded(name)) {
waits.push(record.load(name, true, false));
}
});
if (!record._values || !record._values.rec_name) {
waits.push(record.rec_name().then(
function(name) {
record._values.rec_name = name;
},
function() {
}));
}
return jQuery.when.apply(jQuery, waits);
}
function activateMenuRecord(record) {
if (Sao.main_menu_screen) {
Sao.main_menu_screen.current_record = record;
setFullMenuVisible(false);
Sao.main_menu_row_activate();
}
}
function loadMenuChildren(record, childrenField) {
if (!childrenField) {
return jQuery.when([]);
}
var load = record.is_loaded &&
record.is_loaded(childrenField) ?
jQuery.when() :
record.load(childrenField, true, false);
return load.then(function() {
return record.field_get_client(childrenField) || [];
});
}
function buildMenuNode(record, childrenField, depth) {
return prepareMenuRecord(record).then(function() {
var node = {
label: menuLabel(record),
icon: menuField(record, 'icon'),
record: record,
children: []
};
if (depth > 4) {
return node;
}
return loadMenuChildren(record, childrenField)
.then(function(children) {
var waits = [];
children.forEach(function(child) {
waits.push(buildMenuNode(
child, childrenField, depth + 1));
});
return jQuery.when.apply(jQuery, waits)
.then(function() {
node.children = Array.prototype.slice
.call(arguments);
return node;
});
});
});
}
function appendMenuLink(container, node, level) {
var link = jQuery('<button/>', {
'type': 'button',
'class': 'tradon-full-menu-link level-' + level,
'title': node.label,
'text': node.label
}).click(function() {
activateMenuRecord(node.record);
});
container.append(link);
}
function appendMenuBranch(container, node, level) {
appendMenuLink(container, node, level);
if (level >= 3) {
return;
}
node.children.slice(0, 4).forEach(function(child) {
appendMenuBranch(container, child, level + 1);
});
}
function renderMenuCard(grid, node, index) {
if (!node || !node.label) {
return;
}
var card = jQuery('<section/>', {
'class': 'tradon-full-menu-card'
}).appendTo(grid);
var header = jQuery('<button/>', {
'type': 'button',
'class': 'tradon-full-menu-card-title',
'title': node.label
}).click(function() {
activateMenuRecord(node.record);
}).appendTo(card);
if (node.icon) {
Sao.common.ICONFACTORY.get_icon_url(node.icon)
.then(function(url) {
jQuery('<img/>', {
'class': 'tradon-full-menu-icon',
'src': url,
'alt': ''
}).prependTo(header);
});
}
jQuery('<span/>', {'text': node.label}).appendTo(header);
var content = jQuery('<div/>', {
'class': 'tradon-full-menu-card-content'
}).appendTo(card);
var children = node.children || [];
if (children.length > 5) {
var tabs = jQuery('<div/>', {
'class': 'tradon-full-menu-tabs'
}).appendTo(content);
children.slice(0, 4).forEach(function(child, tabIndex) {
var tab = jQuery('<button/>', {
'type': 'button',
'class': 'tradon-full-menu-tab' +
(tabIndex ? '' : ' active'),
'text': child.label,
'title': child.label
}).appendTo(tabs);
tab.click(function() {
tabs.children().removeClass('active');
tab.addClass('active');
links.empty();
appendMenuBranch(links, child, 1);
});
});
var links = jQuery('<div/>', {
'class': 'tradon-full-menu-links'
}).appendTo(content);
appendMenuBranch(links, children[0], 1);
} else {
var plainLinks = jQuery('<div/>', {
'class': 'tradon-full-menu-links'
}).appendTo(content);
children.forEach(function(child) {
appendMenuBranch(plainLinks, child, 1);
});
}
}
function rebuildFullMenu() {
var screen = Sao.main_menu_screen;
var view = screen && screen.current_view;
var grid = jQuery('#tradon-full-menu .tradon-full-menu-grid');
var records = [];
if (view && view.rows && view.rows.length) {
records = view.rows.map(function(row) {
return row.record;
});
} else if (screen && screen.group && screen.group.length) {
records = screen.group.slice(0);
}
if (!view || !records.length) {
return jQuery.when(false);
}
grid.empty();
var waits = records.map(function(record) {
return buildMenuNode(record, view.children_field, 0);
});
return jQuery.when.apply(jQuery, waits).then(function() {
var nodes = Array.prototype.slice.call(arguments);
grid.empty();
nodes.forEach(function(node, index) {
renderMenuCard(grid, node, index);
});
return Boolean(grid.children().length);
});
}
function showFullMenuWhenReady(attempt) {
if (!enableFullMenu) {
setFullMenuVisible(false);
return;
}
attempt = attempt || 0;
rebuildFullMenu().then(
function(ready) {
if (ready) {
setFullMenuVisible(true);
} else if (attempt < 8) {
window.setTimeout(function() {
showFullMenuWhenReady(attempt + 1);
}, 120);
} else {
setFullMenuVisible(false);
}
},
function() {
setFullMenuVisible(false);
});
}
jQuery(document).on('tradon:main-menu-ready', function() {
if (!enableFullMenu) {
setFullMenuVisible(false);
return;
}
showFullMenuWhenReady(0);
});
jQuery('[data-toggle="offcanvas"]').click(function(event) {
if (enableFullMenu &&
jQuery(event.target).closest('#title-logo').length) {
if (jQuery('#main').hasClass(
'tradon-full-menu-active')) {
setFullMenuVisible(false);
} else {
showFullMenuWhenReady(0);
}
return false;
}
setFullMenuVisible(false);
jQuery('.row-offcanvas').toggleClass('active');
notifyLayoutResize();
});
jQuery('#tabs').on('ready', function() {
var mq = window.matchMedia('(max-width: 991px)');
if (mq.matches) {
if (jQuery('#tablist').children().length) {
jQuery('.row-offcanvas').removeClass('active');
} else {
jQuery('.row-offcanvas').addClass('active');
}
}
});
});
}
</script>
</body>
</html>