mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-21 15:36:22 +08:00
Merge pull request #3218 from aignatov-bio/ai-sci-5548-new-render-index-page-layout
New report index page layout, new pdf and docx columns [SCI-5548][SCI-5549][SCI-5550]
This commit is contained in:
commit
6393337bc7
|
@ -1,3 +1,5 @@
|
||||||
|
/* global I18n DataTableHelpers */
|
||||||
|
|
||||||
(function(global) {
|
(function(global) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -91,6 +93,54 @@
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDocxFile(data) {
|
||||||
|
if (data.error) {
|
||||||
|
return `<span class="processing-error">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
${I18n.t('projects.reports.index.error')}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.processing) {
|
||||||
|
return `<span class="processing docx">
|
||||||
|
${I18n.t('projects.reports.index.generating')}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.file) {
|
||||||
|
return `<a href="${data.file}">
|
||||||
|
<i class="fas fa-file-word"></i>
|
||||||
|
${I18n.t('projects.reports.index.docx')}
|
||||||
|
</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<a href="#" class="generate-docx">${I18n.t('projects.reports.index.generate')}</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPdfFile(data) {
|
||||||
|
if (data.error) {
|
||||||
|
return `<span class="processing-error">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
${I18n.t('projects.reports.index.error')}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.processing) {
|
||||||
|
return `<span class="processing pdf">
|
||||||
|
${I18n.t('projects.reports.index.generating')}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.file) {
|
||||||
|
return `<a href="${data.file}">
|
||||||
|
<i class="fas fa-file-pdf"></i>
|
||||||
|
${I18n.t('projects.reports.index.pdf')}
|
||||||
|
</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
function appendEditPathToRow(row, data) {
|
function appendEditPathToRow(row, data) {
|
||||||
$(row).addClass('report-row')
|
$(row).addClass('report-row')
|
||||||
.attr('data-edit-path', data['edit'])
|
.attr('data-edit-path', data['edit'])
|
||||||
|
@ -115,17 +165,38 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateButtons() {
|
function updateButtons() {
|
||||||
var editReportButton = $('#edit-report-btn');
|
|
||||||
var deleteReportsButton = $('#delete-reports-btn');
|
|
||||||
if (CHECKED_REPORTS.length === 0) {
|
if (CHECKED_REPORTS.length === 0) {
|
||||||
editReportButton.addClass('disabled hidden');
|
$('.single-object-action, .multiple-object-action').addClass('disabled hidden');
|
||||||
deleteReportsButton.addClass('disabled hidden');
|
|
||||||
} else if (CHECKED_REPORTS.length === 1) {
|
} else if (CHECKED_REPORTS.length === 1) {
|
||||||
editReportButton.removeClass('disabled hidden');
|
$('.single-object-action, .multiple-object-action').removeClass('disabled hidden');
|
||||||
deleteReportsButton.removeClass('disabled hidden');
|
|
||||||
|
let $row = $(`.report-row[data-id=${CHECKED_REPORTS[0]}]`);
|
||||||
|
let pdfProcessing = $row.has('.processing.pdf').length > 0;
|
||||||
|
let docxProcessing = $row.has('.processing.docx').length > 0;
|
||||||
|
let docxGenerate = $row.has('.generate-docx').length > 0;
|
||||||
|
|
||||||
|
if (pdfProcessing) {
|
||||||
|
$('#updatePdf').addClass('disabled');
|
||||||
|
} else {
|
||||||
|
$('#updatePdf').removeClass('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (docxGenerate) {
|
||||||
|
$('#requestDocx').removeClass('hidden');
|
||||||
|
$('#updateDocx').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$('#requestDocx').addClass('hidden');
|
||||||
|
$('#updateDocx').removeClass('hidden');
|
||||||
|
|
||||||
|
if (docxProcessing) {
|
||||||
|
$('#updateDocx').addClass('disabled');
|
||||||
|
} else {
|
||||||
|
$('#updateDocx').removeClass('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
editReportButton.removeClass('hidden').addClass('disabled');
|
$('.single-object-action').removeClass('hidden').addClass('disabled');
|
||||||
deleteReportsButton.removeClass('disabled hidden');
|
$('.multiple-object-action').removeClass('disabled hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,27 +205,46 @@
|
||||||
function initDatatable() {
|
function initDatatable() {
|
||||||
var $table = $('#reports-table')
|
var $table = $('#reports-table')
|
||||||
DATATABLE = $table.dataTable({
|
DATATABLE = $table.dataTable({
|
||||||
'order': [[2, 'desc']],
|
dom: "Rt<'pagination-row hidden'<'pagination-info'li><'pagination-actions'p>>",
|
||||||
'processing': true,
|
order: [[2, 'desc']],
|
||||||
'serverSide': true,
|
sScrollX: '100%',
|
||||||
'ajax': $table.data('source'),
|
sScrollXInner: '100%',
|
||||||
'pagingType': 'simple_numbers',
|
processing: true,
|
||||||
'colReorder': {
|
serverSide: true,
|
||||||
'fixedColumnsLeft': 1000000 // Disable reordering
|
ajax: $table.data('source'),
|
||||||
|
pagingType: 'simple_numbers',
|
||||||
|
colReorder: {
|
||||||
|
fixedColumnsLeft: 1000000 // Disable reordering
|
||||||
},
|
},
|
||||||
'columnDefs': [{
|
columnDefs: [{
|
||||||
'targets': 0,
|
targets: 0,
|
||||||
'searchable': false,
|
searchable: false,
|
||||||
'orderable': false,
|
orderable: false,
|
||||||
'className': 'dt-body-center',
|
className: 'dt-body-center',
|
||||||
'sWidth': '1%',
|
sWidth: '1%',
|
||||||
'render': renderCheckboxHTML
|
render: renderCheckboxHTML
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: 3,
|
||||||
|
searchable: false,
|
||||||
|
sWidth: '60',
|
||||||
|
render: renderPdfFile
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: 4,
|
||||||
|
searchable: false,
|
||||||
|
sWidth: '60',
|
||||||
|
render: renderDocxFile
|
||||||
}],
|
}],
|
||||||
'oLanguage': {
|
oLanguage: {
|
||||||
'sSearch': I18n.t('general.filter')
|
sSearch: I18n.t('general.filter')
|
||||||
},
|
},
|
||||||
'fnDrawCallback': tableDrowCallback,
|
fnDrawCallback: tableDrowCallback,
|
||||||
'createdRow': appendEditPathToRow
|
createdRow: appendEditPathToRow,
|
||||||
|
fnInitComplete: function() {
|
||||||
|
DataTableHelpers.initLengthApearance($table.closest('.dataTables_wrapper'));
|
||||||
|
$('.pagination-row').removeClass('hidden');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
63
app/assets/stylesheets/report_index.scss
Normal file
63
app/assets/stylesheets/report_index.scss
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// scss-lint:disable SelectorDepth NestingDepth IdSelector SelectorFormat
|
||||||
|
|
||||||
|
.reports-index {
|
||||||
|
.report-actions-dropdown {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.report-actions-menu {
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding: .5em 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reports-datatable {
|
||||||
|
height: calc(100vh - var(--navbar-height) - var(--content-header-size));
|
||||||
|
|
||||||
|
#reports-table_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.dataTables_scroll {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
height: calc(100% - var(--datatable-pagination-row));
|
||||||
|
|
||||||
|
.dataTables_scrollHead {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-row {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-row {
|
||||||
|
.processing-error {
|
||||||
|
color: $brand-danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-docx {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.generate-docx {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// scss-lint:disable NestingDepth QualifyingElement
|
// scss-lint:disable NestingDepth QualifyingElement
|
||||||
|
|
||||||
.dataTables_wrapper {
|
.dataTables_wrapper {
|
||||||
|
--datatable-pagination-row: 68px;
|
||||||
.main-actions {
|
.main-actions {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
min-height: 68px;
|
min-height: var(--datatable-pagination-row);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.pagination-info,
|
.pagination-info,
|
||||||
|
|
|
@ -2,10 +2,13 @@
|
||||||
|
|
||||||
class ReportDatatable < CustomDatatable
|
class ReportDatatable < CustomDatatable
|
||||||
include InputSanitizeHelper
|
include InputSanitizeHelper
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
TABLE_COLUMNS = %w(
|
TABLE_COLUMNS = %w(
|
||||||
Report.project_name
|
Report.project_name
|
||||||
Report.name
|
Report.name
|
||||||
|
Report.pdf_file
|
||||||
|
Report.docx_file
|
||||||
Report.created_by
|
Report.created_by
|
||||||
Report.modified_by
|
Report.modified_by
|
||||||
Report.created_at
|
Report.created_at
|
||||||
|
@ -27,6 +30,22 @@ class ReportDatatable < CustomDatatable
|
||||||
@searchable_columns ||= TABLE_COLUMNS
|
@searchable_columns ||= TABLE_COLUMNS
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sort_records(records)
|
||||||
|
case sort_column(order_params)
|
||||||
|
when 'reports.docx_file'
|
||||||
|
records.left_joins(:docx_file_attachment)
|
||||||
|
.order(active_storage_attachments: sort_direction(order_params))
|
||||||
|
.order(docx_file_processing: sort_direction(order_params) == 'ASC' ? :desc : :asc)
|
||||||
|
when 'reports.pdf_file'
|
||||||
|
records.left_joins(:pdf_file_attachment)
|
||||||
|
.order(active_storage_attachments: sort_direction(order_params))
|
||||||
|
.order(pdf_file_processing: sort_direction(order_params) == 'ASC' ? :desc : :asc)
|
||||||
|
else
|
||||||
|
sort_by = "#{sort_column(order_params)} #{sort_direction(order_params)}"
|
||||||
|
records.order(sort_by)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def data
|
def data
|
||||||
|
@ -35,15 +54,35 @@ class ReportDatatable < CustomDatatable
|
||||||
'0' => record.id,
|
'0' => record.id,
|
||||||
'1' => sanitize_input(record.project_name),
|
'1' => sanitize_input(record.project_name),
|
||||||
'2' => sanitize_input(record.name),
|
'2' => sanitize_input(record.name),
|
||||||
'3' => sanitize_input(record.created_by),
|
'3' => pdf_file(record),
|
||||||
'4' => sanitize_input(record.modified_by),
|
'4' => docx_file(record),
|
||||||
'5' => I18n.l(record.created_at, format: :full),
|
'5' => sanitize_input(record.created_by),
|
||||||
'6' => I18n.l(record.updated_at, format: :full),
|
'6' => sanitize_input(record.modified_by),
|
||||||
|
'7' => I18n.l(record.created_at, format: :full),
|
||||||
|
'8' => I18n.l(record.updated_at, format: :full),
|
||||||
'edit' => edit_project_report_path(record.project_id, record.id)
|
'edit' => edit_project_report_path(record.project_id, record.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def docx_file(report)
|
||||||
|
docx = rails_blob_path(report.docx_file, disposition: 'attachment') if report.docx_file.attached?
|
||||||
|
{
|
||||||
|
processing: report.docx_file_processing,
|
||||||
|
file: docx,
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def pdf_file(report)
|
||||||
|
pdf = rails_blob_path(report.pdf_file, disposition: 'attachment') if report.pdf_file.attached?
|
||||||
|
{
|
||||||
|
processing: report.pdf_file_processing,
|
||||||
|
file: pdf,
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def get_raw_records
|
def get_raw_records
|
||||||
res = @reports.joins(:project)
|
res = @reports.joins(:project)
|
||||||
.joins(
|
.joins(
|
||||||
|
|
10
app/views/reports/_index_header.html.erb
Normal file
10
app/views/reports/_index_header.html.erb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="content-header sticky-header">
|
||||||
|
<div class="title-row">
|
||||||
|
<h1>
|
||||||
|
<%= t('projects.reports.index.head_title') %>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div id="toolbarWrapper" class="toolbar-row">
|
||||||
|
<%= render partial: 'reports/index_toolbar' %>
|
||||||
|
</div>
|
||||||
|
</div>
|
49
app/views/reports/_index_toolbar.html.erb
Normal file
49
app/views/reports/_index_toolbar.html.erb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<% if can_manage_reports?(current_team) %>
|
||||||
|
<div class="sci-btn-group">
|
||||||
|
<%= link_to '#', remote: true, class: 'btn btn-primary', id: 'new-report-btn' do %>
|
||||||
|
<span class="fas fa-plus" aria-hidden="true"></span>
|
||||||
|
<span class="hidden-xs"><%= t("projects.reports.index.new") %></span>
|
||||||
|
<% end %>
|
||||||
|
<div class="report-actions-dropdown sci-dropdown">
|
||||||
|
<button class="btn btn-light dropdown-toggle single-object-action disabled hidden" type="button" id="reportMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
<i class="fas fa-file"></i>
|
||||||
|
<%= t("projects.reports.index.report_button") %>
|
||||||
|
<span class="caret pull-right"></span>
|
||||||
|
</button>
|
||||||
|
<ul id="reportMenuDropdown" class="dropdown-menu report-actions-menu" aria-labelledby="reportMenu">
|
||||||
|
<li>
|
||||||
|
<%= link_to '#', remote: true, id: 'updatePdf' do %>
|
||||||
|
<i class="fas fa-file-pdf"></i>
|
||||||
|
<%= t("projects.reports.index.update_pdf") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to '#', remote: true, id: 'savePdfToInventory' do %>
|
||||||
|
<i class="fas fa-save"></i>
|
||||||
|
<%= t("projects.reports.index.save_pdf_to_inventory") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to '#', remote: true, id: 'requestDocx' do %>
|
||||||
|
<i class="fas fa-file-word"></i>
|
||||||
|
<%= t("projects.reports.index.request_docx") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to '#', remote: true, id: 'updateDocx' do %>
|
||||||
|
<i class="fas fa-file-word"></i>
|
||||||
|
<%= t("projects.reports.index.update_docx") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<%= link_to "", remote: true, class: "btn btn-light disabled hidden single-object-action", id: "edit-report-btn" do %>
|
||||||
|
<span class="fas fa-pencil-alt" aria-hidden="true"></span>
|
||||||
|
<span class="hidden-xs"><%=t "projects.reports.index.edit" %></span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to "", remote: true, class: "btn btn-light disabled hidden multiple-object-action", id: "delete-reports-btn" do %>
|
||||||
|
<span class="fas fa-times" aria-hidden="true"></span>
|
||||||
|
<span class="hidden-xs"><%=t "projects.reports.index.delete" %></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -2,30 +2,11 @@
|
||||||
<meta name="turbolinks-cache-control" content="no-cache">
|
<meta name="turbolinks-cache-control" content="no-cache">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% provide(:head_title, t('projects.reports.index.head_title').html_safe) %>
|
|
||||||
<%= stylesheet_link_tag 'datatables' %>
|
<%= stylesheet_link_tag 'datatables' %>
|
||||||
|
|
||||||
<div class="content-pane">
|
<div class="content-pane flexible reports-index">
|
||||||
|
<%= render partial: 'reports/index_header' %>
|
||||||
<div id="content-reports-index">
|
<div id="content-reports-index">
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<% if can_manage_reports?(current_team) %>
|
|
||||||
<%= link_to '#', remote: true, class: 'btn btn-primary', id: 'new-report-btn' do %>
|
|
||||||
<span class="fas fa-plus" aria-hidden="true"></span>
|
|
||||||
<span class="hidden-xs"><%=t "projects.reports.index.new" %></span>
|
|
||||||
<% end %>
|
|
||||||
<%= link_to "", remote: true, class: "btn btn-light disabled hidden", id: "edit-report-btn" do %>
|
|
||||||
<span class="fas fa-pencil-alt" aria-hidden="true"></span>
|
|
||||||
<span class="hidden-xs"><%=t "projects.reports.index.edit" %></span>
|
|
||||||
<% end %>
|
|
||||||
<%= link_to "", remote: true, class: "btn btn-light disabled hidden", id: "delete-reports-btn" do %>
|
|
||||||
<span class="fas fa-times" aria-hidden="true"></span>
|
|
||||||
<span class="hidden-xs"><%=t "projects.reports.index.delete" %></span>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="reports-datatable">
|
<div class="reports-datatable">
|
||||||
|
@ -40,12 +21,14 @@
|
||||||
<span class="sci-checkbox-label"></span>
|
<span class="sci-checkbox-label"></span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th id="project-name"><%=t 'projects.reports.index.thead_project_name' %></th>
|
<th id="project-name"><%= t('projects.reports.index.thead_project_name') %></th>
|
||||||
<th id="report-name"><%=t 'projects.reports.index.thead_name' %></th>
|
<th id="report-name"><%= t('projects.reports.index.thead_name') %></th>
|
||||||
<th id="report-created-by"><%=t 'projects.reports.index.thead_created_by' %></th>
|
<th id="report-pdf"><%= t('projects.reports.index.pdf') %></th>
|
||||||
<th id="report-last-modified-by"><%=t 'projects.reports.index.thead_last_modified_by' %></th>
|
<th id="report-docx"><%= t('projects.reports.index.docx') %></th>
|
||||||
<th id="report-created-at"><%=t 'projects.reports.index.thead_created_at' %></th>
|
<th id="report-created-by"><%= t('projects.reports.index.thead_created_by') %></th>
|
||||||
<th id="report-updated-at"><%=t 'projects.reports.index.thead_updated_at' %></th>
|
<th id="report-last-modified-by"><%= t('projects.reports.index.thead_last_modified_by') %></th>
|
||||||
|
<th id="report-created-at"><%= t('projects.reports.index.thead_created_at') %></th>
|
||||||
|
<th id="report-updated-at"><%= t('projects.reports.index.thead_updated_at') %></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
|
|
|
@ -490,8 +490,13 @@ en:
|
||||||
reports:
|
reports:
|
||||||
print_title: "%{project} | Report"
|
print_title: "%{project} | Report"
|
||||||
index:
|
index:
|
||||||
head_title: " Reports"
|
head_title: "Reports"
|
||||||
new: "New report"
|
new: "New report"
|
||||||
|
report_button: "Report"
|
||||||
|
update_pdf: "Update PDF"
|
||||||
|
save_pdf_to_inventory: "Save PDF to Inventory"
|
||||||
|
request_docx: "Request DOCX"
|
||||||
|
update_docx: "Update DOCX"
|
||||||
edit: "Edit"
|
edit: "Edit"
|
||||||
delete: "Delete"
|
delete: "Delete"
|
||||||
deleted: "(Deleted)"
|
deleted: "(Deleted)"
|
||||||
|
@ -502,6 +507,11 @@ en:
|
||||||
thead_created_at: "Created at"
|
thead_created_at: "Created at"
|
||||||
thead_updated_at: "Last updated at"
|
thead_updated_at: "Last updated at"
|
||||||
no_reports: "No reports!"
|
no_reports: "No reports!"
|
||||||
|
pdf: "PDF"
|
||||||
|
docx: "DOCX"
|
||||||
|
error: "Error"
|
||||||
|
generating: "Generating"
|
||||||
|
generate: "Generate"
|
||||||
modal_new:
|
modal_new:
|
||||||
projects: "Projects"
|
projects: "Projects"
|
||||||
head_title: "Create new report"
|
head_title: "Create new report"
|
||||||
|
|
Loading…
Reference in a new issue