Bug 32030: ERM - Vue version

First, `yarn install`.
Then use `yarn build_js` or `yarn watch_js` to regenerate the dist/main.js file

Signed-off-by: Jonathan Field <jonathan.field@ptfs-europe.com>

Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Jonathan Druart 2022-03-16 16:49:00 +01:00 committed by Tomas Cohen Arazi
parent 1d9a6a8969
commit e5042cc5c1
No known key found for this signature in database
GPG Key ID: 0A272EA1B2F3C15F
19 changed files with 1081 additions and 665 deletions

View File

@ -41,7 +41,20 @@ Returns the periods for this agreement
=cut
sub periods {
my ( $self ) = @_;
my ( $self, $periods ) = @_;
if ( $periods ) {
my $schema = $self->_result->result_source->schema;
$schema->txn_do(
sub {
$self->periods->delete;
for my $period (@$periods) {
$self->_result->add_to_erm_agreement_periods($period);
}
}
);
}
my $periods_rs = $self->_result->erm_agreement_periods;
return Koha::ERM::Agreement::Periods->_new_from_dbic($periods_rs);
@ -54,8 +67,20 @@ Returns the user roles for this agreement
=cut
sub user_roles {
my ( $self ) = @_;
my ( $self, $user_roles ) = @_;
if ( $user_roles ) {
my $schema = $self->_result->result_source->schema;
$schema->txn_do(
sub {
$self->user_roles->delete;
for my $user_role (@$user_roles) {
$self->_result->add_to_erm_agreement_user_roles($user_role);
}
}
);
}
my $user_roles_rs = $self->_result->erm_agreement_user_roles;
return Koha::ERM::Agreement::UserRoles->_new_from_dbic($user_roles_rs);
}

View File

@ -26,9 +26,6 @@ use Koha::Acquisition::Booksellers;
use Koha::ERM::Agreements;
my $input = CGI->new;
my $agreement_id = $input->param('agreement_id');
my $op = $input->param('op') || 'list';
my @messages;
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{
@ -39,142 +36,8 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
}
);
my $dbh = C4::Context->dbh;
if ( $op eq 'add_form' ) {
my $agreement;
if ($agreement_id) {
$agreement = Koha::ERM::Agreements->find($agreement_id);
}
$template->param( agreement => $agreement, );
}
elsif ( $op eq 'add_validate' ) {
my $vendor_id = $input->param('vendor_id');
my $name = $input->param('name');
my $description = $input->param('description');
my $status = $input->param('status');
my $closure_reason = $input->param('closure_reason');
my $is_perpetual = $input->param('is_perpetual');
my $renewal_priority = $input->param('renewal_priority');
my $license_info = $input->param('license_info');
my $schema = Koha::Database->new->schema;
$schema->txn_do(sub{
my ( $stored, $agreement );
if ($agreement_id) {
$agreement = Koha::ERM::Agreements->find($agreement_id);
$agreement->vendor_id($vendor_id);
$agreement->name($name);
$agreement->description($description);
$agreement->status($status);
$agreement->closure_reason($closure_reason);
$agreement->is_perpetual($is_perpetual);
$agreement->renewal_priority($renewal_priority);
$agreement->license_info($license_info);
eval { $agreement->store; };
if ($@) {
push @messages, { type => 'error', code => 'error_on_update' };
}
else {
$stored = 1;
push @messages, { type => 'message', code => 'success_on_update' };
}
}
else {
$agreement = Koha::ERM::Agreement->new(
{
vendor_id => $vendor_id,
name => $name,
description => $description,
status => $status,
closure_reason => $closure_reason,
is_perpetual => $is_perpetual,
renewal_priority => $renewal_priority,
license_info => $license_info,
}
);
eval { $agreement->store; };
if ($@) {
push @messages, { type => 'error', code => 'error_on_insert' };
}
else {
$stored = 1;
push @messages, { type => 'message', code => 'success_on_insert' };
}
}
if ( $stored ) {
if ( $agreement_id ) {
$agreement->periods->delete;
$agreement->user_roles->delete;
}
for my $unique_id ( $input->multi_param('period_unique_id') ) {
my $started_on = $input->param( 'started_on_' . $unique_id );
next unless $started_on;
my $ended_on = $input->param( 'ended_on_' . $unique_id );
my $cancellation_deadline = $input->param( 'cancellation_deadline_' . $unique_id );
my $notes = $input->param( 'notes_' . $unique_id );
$started_on = dt_from_string($started_on);
$ended_on &&= dt_from_string($ended_on);
$cancellation_deadline &&= dt_from_string($cancellation_deadline);
Koha::ERM::Agreement::Period->new(
{
agreement_id => $agreement->agreement_id,
started_on => $started_on,
ended_on => $ended_on,
cancellation_deadline => $cancellation_deadline,
notes => $notes,
}
)->store;
}
for my $unique_id ( $input->multi_param('user_unique_id') ) {
my $user_id = $input->param('user_id_' . $unique_id);
next unless $user_id;
my $role = $input->param('user_role_' . $unique_id);
Koha::ERM::Agreement::UserRole->new(
{
agreement_id => $agreement->agreement_id,
user_id => $user_id,
role => $role,
}
)->store;
}
}
});
$op = 'list';
}
elsif ( $op eq 'delete_confirm' ) {
my $agreement = Koha::ERM::Agreements->find($agreement_id);
$template->param( agreement => $agreement, );
}
elsif ( $op eq 'delete_confirmed' ) {
my $agreement = Koha::ERM::Agreements->find($agreement_id);
my $deleted = eval { $agreement->delete; };
if ( $@ or not $deleted ) {
push @messages, { type => 'error', code => 'error_on_delete' };
}
else {
push @messages, { type => 'message', code => 'success_on_delete' };
}
$op = 'list';
}
if ( $op eq 'list' ) {
$template->param(
agreements_count => Koha::ERM::Agreements->search->count );
}
$template->param(
vendors => Koha::Acquisition::Booksellers->search,
agreement_id => $agreement_id,
messages => \@messages,
op => $op,
vendors => Koha::Acquisition::Booksellers->search,
);
output_html_with_http_headers $input, $cookie, $template->output;

View File

@ -33,7 +33,7 @@
<script>
flatpickr.l10ns.default.weekdays = flatpickr_weekdays;
flatpickr.l10ns.default.months = flatpickr_months;
flatpickr.setDefaults({
let flatpickr_defaults = {
allowInput: true,
dateFormat: "Y-m-d",
altInput: true,
@ -120,7 +120,10 @@
}
})
]
});
};
flatpickr.setDefaults(flatpickr_defaults);
$(document).ready(function(){
$(".flatpickr").each(function(){
let options = {};

View File

@ -67,6 +67,12 @@
return m.format(get_time_pattern(timeformat)+(dateformat=='rfc3339'?':ss'+(!m.isUTC()?'Z':''):''))+(dateformat=='rfc3339' && m.isUTC()?'Z':'');
}
window.$date_to_rfc3339 = function(value, options) {
var dateformat = (options&&options.dateformat)||def_date_format;
let m = moment(value, get_date_pattern(dateformat));
return m.format("YYYY-MM-DD");
}
})();
</script>
<!-- / js-date-format.inc -->

View File

@ -843,11 +843,15 @@
}
function select_user(borrowernumber, data) {
var p = window.opener;
[% IF callback %]
p.[% callback | html %](borrowernumber, data);
[% ELSE %]
p.select_user(borrowernumber, data);
[% END %]
if ( p.document.getElementById("selected_patron_id") ) {
p.document.getElementById("selected_patron_id").value = borrowernumber;
} else {
[% IF callback %]
p.[% callback | html %](borrowernumber, data);
[% ELSE %]
p.select_user(borrowernumber, data);
[% END %]
}
window.close();
}
</script>

View File

@ -34,37 +34,9 @@
<a href="/cgi-bin/koha/erm/erm-home.pl">Electronic resources management</a>
</li>
[% IF op == 'add_form' %]
<li>
<a href="/cgi-bin/koha/erm/agreements.pl">Agreements</a>
</li>
<li>
<a href="#" aria-current="page">
[% IF agreement.agreement_id %]
Modify
[% ELSE %]
New
[% END %] Agreement
</a>
</li>
[% ELSIF op == 'delete_confirm' %]
<li>
<a href="/cgi-bin/koha/erm/agreements.pl">Agreements</a>
</li>
<li>
<a href="#" aria-current="page">
Confirm deletion of agreement
</a>
</li>
[% ELSE %]
<li>
<a href="#" aria-current="page">
Agreements
</a>
</li>
[% END %]
<li>
<a href="/cgi-bin/koha/erm/agreements.pl">Agreements</a>
</li>
</ol>
</nav>
@ -73,284 +45,7 @@
<div class="col-sm-10 col-sm-push-2">
<main>
[% FOR m IN messages %]
<div class="dialog [% m.type | html %]">
[% SWITCH m.code %]
[% CASE 'error_on_update' %]
An error occurred when updating this agreement. Perhaps it already exists.
[% CASE 'error_on_insert' %]
An error occurred when adding this agreement. The agreement id might already exist.
[% CASE 'error_on_delete' %]
An error occurred when deleting this agreement. Check the logs.
[% CASE 'success_on_update' %]
Agreement updated successfully.
[% CASE 'success_on_insert' %]
Agreement added successfully.
[% CASE 'success_on_delete' %]
Agreement deleted successfully.
[% CASE 'already_exists' %]
This agreement already exists.
[% CASE %]
[% m.code | html %]
[% END %]
</div>
[% END %]
[% IF op == 'add_form' %]
[% IF agreement %]
<h1>Modify a agreement</h1>
[% ELSE %]
<h1>New agreement</h1>
[% END %]
<form action="/cgi-bin/koha/erm/agreements.pl" name="Aform" method="post" class="validated">
<input type="hidden" name="op" value="add_validate" />
<input type="hidden" name="agreement_id" value="[% agreement.agreement_id | html %]" />
<fieldset class="rows">
<ol>
[% IF agreement %]
<li><span class="label">Agreement ID: </span>[% agreement.agreement_id | html %]</li>
[% END %]
<li>
<label for="vendor_id">Vendor: </label>
<select name="vendor_id">
<option value=""></option>
[% FOR v IN vendors %]
[% IF v.id == agreement.vendor_id %]
<option value="[% v.id %]" selected="selected">[% v.name %]</option>
[% ELSE %]
<option value="[% v.id %]">[% v.name %]</option>
[% END %]
[% END %]
</select>
</li>
<li>
<label for="name" class="required">Name: </label>
<input type="text" name="name" id="name" size="80" value="[% agreement.name | html %]" required="required" class="required" /> <span class="required">Required</span>
<li>
<label for="description">Description: </label>
<input type="text" name="description" id="description" size="80" value="[% agreement.description | html %]" />
<li>
<label for="status" class="required">Status: </label>
<select name="status" required="required" required="required">
<option value=""></option>
[% PROCESS options_for_authorised_values authorised_values => AuthorisedValues.GetAuthValueDropbox( 'ERM_AGREEMENT_STATUS' ), selected_av => agreement.status %]
</select>
<span class="required">Required</span>
</li>
<li>
<label for="closure_reason">Closure reason: </label>
<select name="closure_reason">
<option value=""></option>
[% PROCESS options_for_authorised_values authorised_values => AuthorisedValues.GetAuthValueDropbox( 'ERM_AGREEMENT_CLOSURE_REASON' ), selected_av => agreement.closure_reason %]
</select>
</li>
<li>
<label for="is_perpetual">Is perpetual: </label>
<label for="is_perpetual_yes"> Yes
[% IF agreement.is_perpetual %]
<input type="radio" id="is_perpetual_yes" name="is_perpetual" value="1" checked="checked">
[% ELSE %]
<input type="radio" id="is_perpetual_yes" name="is_perpetual" value="1">
[% END %]
</label>
<label for="is_perpetual_no"> No
[% IF agreement.is_perpetual %]
<input type="radio" id="is_perpetual_no" name="is_perpetual" value="0">
[% ELSE %]
<input type="radio" id="is_perpetual_no" name="is_perpetual" value="0" checked="checked">
[% END %]
</label>
</li>
<li>
<label for="renewal_priority">Renewal priority: </label>
<select name="renewal_priority">
<option value=""></option>
[% PROCESS options_for_authorised_values authorised_values => AuthorisedValues.GetAuthValueDropbox( 'ERM_AGREEMENT_RENEWAL_PRIORITY' ), selected_av => agreement.renewal_priority %]
</select>
<li>
<label for="license_info">License info: </label>
<input type="text" name="license_info" id="license_info" size="80" value="[% agreement.license_info | html %]" />
</li>
</ol>
</fieldset>
[% BLOCK agreement_period %]
<fieldset class="agreement_period">
<legend>
Agreement period <span class="period_count">[% id | html %]</span>
<a href="#" class="remove_period"><i class="fa fa-trash"></i> Remove this period</a>
</legend>
<input type="hidden" name="period_unique_id" value="[% id | html %]" />
<ol>
<li>
<label for="started_on" class="required">Start date: </label>
<input type="text" id="started_on_[% id | html %]" class="started_on" name="started_on_[% id | html %]" value="[% p.started_on | $KohaDates %]" class="flatpickr" data-date_to="ended_on_[% id | html %]" />
<span class="required">Required</span>
<div class="hint">[% INCLUDE 'date-format.inc' %]</div>
</li>
<li>
<label for="ended_on">End date: </label>
<input type="text" id="ended_on_[% id | html %]" class="ended_on" name="ended_on_[% id | html %]" value="[% p.ended_on | $KohaDates %]" class="flatpickr" />
<div class="hint">[% INCLUDE 'date-format.inc' %]</div>
</li>
<li>
<label>Cancellation deadline: </label>
<input type="text" class="cancellation_deadline" name="cancellation_deadline_[% id | html %]" value="[% p.cancellation_deadline | $KohaDates %]" class="flatpickr" />
<div class="hint">[% INCLUDE 'date-format.inc' %]</div>
</li>
<li>
<label>Notes: </label>
<input type="text" class="notes" name="notes_[% id | html %]" value="[% p.notes | html %]" />
</li>
</ol>
</fieldset>
[% END %]
<fieldset class="rows" id="agreement_periods">
<legend>Periods</legend>
[% IF agreement.periods.count %]
[% FOR p IN agreement.periods %]
[% PROCESS agreement_period period => p, id => loop.count %]
[% END %]
[% ELSE %]
[% PROCESS agreement_period id => 1 %]
[% END %]
<button class="add_new_period" type="button" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i> Add new period</button>
</fieldset>
[% BLOCK agreement_user %]
<fieldset class="agreement_user">
<legend>
User <span class="user_count">[% id | html %]</span>
<a href="#" class="remove_user"><i class="fa fa-trash"></i> Remove this user</a>
</legend>
<input type="hidden" name="user_unique_id" value="[% id | html %]" />
<ol>
<li>
<label for="user" class="required">User: </label>
<span class="user">
[% IF u %]
<a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% u.user_id| uri %]">
[% INCLUDE 'patron-title.inc' patron = u.patron %]
</a>
<input type="hidden" class="user_id" name="user_id_[% id | html %]" value="[% u.user_id %]" />
[% END %]
</span>
(<a href="#" class="pick_user" class="btn btn-default">Select user</a>)
</li>
<li>
<label>Role: </label>
<select class="user_role" name="user_role_[% id | html %]">
<option value=""></option>
[% PROCESS options_for_authorised_values authorised_values => AuthorisedValues.GetAuthValueDropbox( 'ERM_AGREEMENT_USER_ROLES' ), selected_av => u.role %]
</select>
</li>
</ol>
</fieldset>
[% END %]
<fieldset class="rows">
<legend>Users</legend>
[% IF agreement.user_roles.count %]
[% FOR u IN agreement.user_roles %]
[% PROCESS agreement_user user => u, id => loop.count %]
[% END %]
[% ELSE %]
[% PROCESS agreement_user, id => 1 %]
[% END %]
<button class="add_new_user_block" type="button" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i> Add new user</button>
</fieldset>
<fieldset class="action">
<input type="submit" value="Submit" />
<a class="cancel" href="/cgi-bin/koha/erm/agreements.pl">Cancel</a>
</fieldset>
</form>
[% END %]
[% IF op == 'delete_confirm' %]
<div class="dialog alert">
<h3>Delete agreement "[% agreement.agreement_id | html %]?"</h3>
<table>
<tr><th>Agreement id</th>
<td>[% agreement.agreement_id| html %]</td>
</tr>
<tr><th>Vendor</th>
<td>[% agreement.vendor_id| html %]</td>
</tr>
<tr><th>Name</th>
<td>[% agreement.name| html %]</td>
</tr>
<tr><th>Description</th>
<td>[% agreement.description| html %]</td>
</tr>
<tr><th>Status</th>
<td>[% agreement.status| html %]</td>
</tr>
<tr><th>Closure_reason</th>
<td>[% agreement.closure_reason| html %]</td>
</tr>
<tr><th>Is perpetual</th>
<td>[% agreement.is_perpetual| html %]</td>
</tr>
<tr><th>Renewal priority</th>
<td>[% agreement.renewal_priority| html %]</td>
</tr>
<tr><th>License info</th>
<td>[% agreement.license_info| html %]</td>
</tr>
</table>
<form action="/cgi-bin/koha/erm/agreements.pl" method="post">
<input type="hidden" name="op" value="delete_confirmed" />
<input type="hidden" name="agreement_id" value="[% agreement.agreement_id | html %]" />
<button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> Yes, delete</button>
</form>
<form action="/cgi-bin/koha/erm/agreements.pl" method="get">
<button type="submit" class="deny"><i class="fa fa-fw fa-remove"></i> No, do not delete</button>
</form>
</div>
[% END %]
[% IF op == 'list' %]
<div id="toolbar" class="btn-toolbar">
<a class="btn btn-default" id="newagreement" href="/cgi-bin/koha/erm/agreements.pl?op=add_form"><i class="fa fa-plus"></i> New agreement</a>
</div>
<h2>Agreements</h2>
[% IF agreement_name_filter %]
Searching: [% agreement_name_filter | html %]
[% END %]
[% IF agreements_count > 0 %]
<div class="table_agreements_table_controls"></div>
<table id="table_agreements">
<thead>
<tr>
<th>ID</th>
<th>Vendor</th>
<th>Name</th>
<th>Description</th>
<th>Status</th>
<th>Closure reason</th>
<th>Is perpetual</th>
<th>Renewal priority</th>
<th data-class-name="actions noExport">Actions</th>
</tr>
</thead>
</table>
[% ELSE %]
<div class="dialog message">
There are no agreements defined. <a href="/cgi-bin/koha/erm/agreements.pl?op=add_form">Create a new agreement</a>.
</div>
[% END %]
[% END %]
<div id="agreements"></div>
</main>
</div> <!-- /.col-sm-10.col-sm-push-2 -->
@ -368,29 +63,18 @@
[% INCLUDE 'datatables.inc' %]
[% INCLUDE 'columns_settings.inc' %]
[% INCLUDE 'js-patron-format.inc' %]
[% INCLUDE 'js-date-format.inc' %]
<script>
const agreement_statuses = [% To.json(AuthorisedValues.Get('ERM_AGREEMENT_STATUS')) | $raw %];
let agreement_statuses_map = agreement_statuses.reduce((map, e) => {
map[e.authorised_value] = e;
return map;
}, {});
const agreement_closure_reasons = [% To.json(AuthorisedValues.Get('ERM_AGREEMENT_CLOSURE_REASON')) | $raw %];
let agreement_closure_reasons_map = agreement_closure_reasons.reduce((map, e) => {
map[e.authorised_value] = e;
return map;
}, {});
const agreement_renewal_priorities = [% To.json(AuthorisedValues.Get('ERM_AGREEMENT_RENEWAL_PRIORITY')) | $raw %];
let agreement_renewal_priorities_map = agreement_renewal_priorities.reduce((map, e) => {
map[e.authorised_value] = e;
return map;
}, {});
const agreement_user_roles = [% To.json(AuthorisedValues.Get('ERM_AGREEMENT_USER_ROLES')) | $raw %];
let current_user_node;
var columns_settings = [% TablesSettings.GetColumns( 'erm', 'agreements', 'table_agreements', 'json' ) | $raw %];
$(document).ready(function() {
var agreements_table_url = '/api/v1/erm/agreements?';
var agreements_table_url = '/api/v1/erm/agreements?';
[% IF agreement_name_filter %]
var agreement_name_filter = {
@ -400,196 +84,10 @@
};
agreements_table_url += 'q='+ encodeURIComponent(JSON.stringify(agreement_name_filter));
[% END %]
var agreements_table = $("#table_agreements").kohaTable({
"ajax": {
"url": agreements_table_url
},
"order": [[ 1, "asc" ]],
"columns": [
{
"data": "agreement_id",
"searchable": true,
"orderable": true
},
{
"data": "vendor_id",
"searchable": true,
"orderable": true
},
{
"data": "name",
"searchable": true,
"orderable": true
},
{
"data": "description",
"searchable": true,
"orderable": true
},
{
"data": "status",
"searchable": true,
"orderable": true,
"render": function( data, type, row, meta ) {
return escape_str(agreement_statuses_map[row.status].lib);
}
},
{
"data": "closure_reason",
"searchable": true,
"orderable": true,
"render": function( data, type, row, meta ) {
return row.closure_reason != undefined && row.closure_reason != "" ? escape_str(agreement_closure_reasons_map[row.closure_reason].lib) : "";
}
},
{
"data": "is_perpetual",
"searchable": true,
"orderable": true,
"render": function( data, type, row, meta ) {
return escape_str(row.is_perpetual ? _("Yes") : _("No"));
}
},
{
"data": "renewal_priority",
"searchable": true,
"orderable": true,
"render": function( data, type, row, meta ) {
return row.renewal_priority != undefined && row.renewal_priority != "" ? escape_str(agreement_renewal_priorities_map[row.renewal_priority].lib) : "";
}
},
{
"data": function( row, type, val, meta ) {
var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/erm/agreements.pl?op=add_form&amp;agreement_id='+ encodeURIComponent(row.agreement_id) +'"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
result += '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/erm/agreements.pl?op=delete_confirm&amp;agreement_id='+ encodeURIComponent(row.agreement_id) +'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Delete")+'</a>';
return result;
},
"searchable": false,
"orderable": false
}
]
}, columns_settings, 1);
$(".add_new_period").on("click", function(e){
e.preventDefault();
let first_period_block = $("fieldset.agreement_period:first");
if ( first_period_block.is(":visible") ) {
let new_period_block = first_period_block.clone(1);
new_period_block.find("input").val("");
$(new_period_block).insertBefore(this);
} else {
first_period_block.show();
}
update_period_count();
});
$(".remove_period").on("click", function(e){
e.preventDefault();
let fieldset = $(this).parent().parent();
if ( $("fieldset.agreement_period").length == 1 ) {
clear_block(fieldset);
fieldset.hide();
} else {
fieldset.remove();
}
update_period_count();
});
$(".add_new_user_block").on("click",function(e){
e.preventDefault();
let first_user_block = $("fieldset.agreement_user:first");
if ( first_user_block.is(":visible") ) {
let new_user_block = $("fieldset.agreement_user:first").clone(1);
new_user_block.find("span.user").empty();
clear_block(new_user_block);
$(new_user_block).insertBefore(this);
} else {
first_user_block.show();
}
update_user_count();
});
$(".remove_user").on("click", function(e){
e.preventDefault();
let fieldset = $(this).parent().parent();
if ( $("fieldset.agreement_user").length == 1 ) {
fieldset.find("span.user").empty();
clear_block(fieldset);
fieldset.hide();
} else {
fieldset.remove();
}
update_user_count();
});
$(".pick_user").on("click", function(e){
e.preventDefault();
current_user_node = $(this).closest("fieldset");
window.open("/cgi-bin/koha/members/search.pl?columns=cardnumber,name,category,branch,action&selection_type=select&filter=erm_users",
'PatronPopup',
'width=740,height=450,location=yes,toolbar=no,'
+ 'scrollbars=yes,resize=yes'
);
});
update_period_count();
update_user_count();
});
function clear_block(block){
$(block).find('input').val("");
$(block).find("select option:first-child").attr("selected", "selected");
}
function update_period_count(){
$("fieldset.agreement_period").each(function(i, period){
let id = i + 1;
$(period).find(".period_count").text(id);
$(period).find("input[name='period_unique_id']").val(id);
let ended_on_input = $(period).find("input.ended_on");
$(ended_on_input).attr("id", "ended_on_" + id);
$(ended_on_input).attr("name", "ended_on_" + id);
$(ended_on_input).flatpickr();
let started_on_input = $(period).find("input.started_on");
$(started_on_input).attr("name", "started_on_" + id);
$(started_on_input).data("date_to", "ended_on_" + id);
$(started_on_input).flatpickr();
let cancellation_deadline_input = $(period).find("input.cancellation_deadline");
$(cancellation_deadline_input).attr("name", "cancellation_deadline_" + id);
$(cancellation_deadline_input).flatpickr();
$(period).find(".notes").attr("name", "notes_" + id);
$(period).attr('id', 'agreement_period_' + id);
});
}
function update_user_count(){
$("fieldset.agreement_user").each(function(i, user){
let id = i + 1;
let remove_user_link = $(this).find(".remove_user");
$(user).find(".user_count").text(id);
$(user).find("input[name='user_unique_id']").val(id);
$(user).find(".user_id").attr("name", "user_id_" + id);
$(user).find(".user_role").attr("name", "user_role_" + id);
});
}
function select_user(borrowernumber, patron) {
patron['patron_id'] = borrowernumber;
let unique_id = $(current_user_node).find("input[name='user_unique_id']").val();
let a = '<a href="/cgi-bin/koha/members/moremember.pl?borrowernumber='
+ borrowernumber + '">' + $patron_to_html(patron) + '</a> '
+ '<input type="hidden" class="user_id" name="user_id_' + unique_id + '" value="' + borrowernumber + '" />';
$(current_user_node).find("span.user").html(a);
}
</script>
[% Asset.js("js/vue/dist/main.js") %]
[% Asset.js("js/vue/dist/runtime.js") %]
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]

View File

@ -0,0 +1,116 @@
<template>
<Toolbar v-if="op == 'list'" @switch-view="switchView" />
<b-alert v-if="message" variant="success" show>{{ message }}</b-alert>
<b-alert v-if="error" variant="warning" show>{{ error }}</b-alert>
<List
v-if="op == 'list'"
:vendors="vendors"
:av_statuses="statuses"
:av_closure_reasons="closure_reasons"
:av_renewal_priorities="renewal_priorities"
@set-current-agreement-id="setCurrentAgreementID"
@switch-view="switchView"
@set-error="setError"
/>
<AddForm
v-if="op == 'add-form'"
:agreement_id="agreement_id"
:vendors="vendors"
:av_statuses="statuses"
:av_closure_reasons="closure_reasons"
:av_renewal_priorities="renewal_priorities"
:av_user_roles="user_roles"
@agreement-created="agreementCreated"
@agreement-updated="agreementUpdated"
@set-error="setError"
@switch-view="switchView"
/>
<ConfirmDeleteForm
v-if="op == 'confirm-delete-form'"
:agreement_id="agreement_id"
@agreement-deleted="agreementDeleted"
@set-error="setError"
@switch-view="switchView"
/>
</template>
<script>
import Toolbar from "./components/ERM/AgreementsToolbar.vue"
import List from "./components/ERM/AgreementsList.vue"
import AddForm from "./components/ERM/AgreementsFormAdd.vue"
import ConfirmDeleteForm from "./components/ERM/AgreementsFormConfirmDelete.vue"
import { reactive, computed } from "vue"
export default {
data() {
return {
agreement_id: null,
op: "list",
message: null,
error: null,
vendors: [],
statuses: agreement_statuses,
closure_reasons: agreement_closure_reasons,
renewal_priorities: agreement_renewal_priorities,
user_roles: agreement_user_roles,
}
},
beforeCreate() {
// FIXME it's not only called on setup, but setup() does not have 'this'.
const apiUrl = "/api/v1/acquisitions/vendors"
fetch(apiUrl)
.then((res) => res.json())
.then(
(result) => {
this.vendors = result
}).catch(
(error) => {
this.$emit('set-error', error)
}
)
},
methods: {
switchView(view) {
this.message = null
this.error = null
this.op = view
if (view == "list") this.agreement_id = null
},
agreementCreated() {
this.message = "Agreement created"
this.error = null
this.agreement_id = null
this.op = "list"
},
agreementUpdated() {
this.message = "Agreement updated"
this.error = null
this.agreement_id = null
this.op = "list"
},
agreementDeleted() {
this.message = "Agreement deleted"
this.error = null
this.agreement_id = null
this.op = "list"
},
setCurrentAgreementID(agreement_id) {
this.agreement_id = agreement_id
},
setError(error) {
this.message = null
this.error = "Something went wrong: " + error
},
},
components: {
Toolbar,
List,
AddForm,
ConfirmDeleteForm,
},
emits: ["set-error"],
};
</script>

View File

@ -0,0 +1,98 @@
<template>
<fieldset class="rows" id="agreement_periods">
<legend>Periods</legend>
<fieldset
class="rows"
v-for="(period, counter) in periods"
v-bind:key="counter"
>
<legend>
Agreement period {{ counter + 1 }}
<a href="#" @click.prevent="deletePeriod(counter)"
><i class="fa fa-trash"></i> Remove this period</a
>
</legend>
<ol>
<li>
<label :for="`started_on_${counter}`" class="required"
>Start date:
</label>
<flat-pickr
v-model="period.started_on"
required
:config="fp_config"
:data-date_to="`ended_on_${counter}`"
/>
<span class="required">Required</span>
</li>
<li>
<label :for="`ended_on_${counter}`">End date: </label>
<flat-pickr
:id="`ended_on_${counter}`"
v-model="period.ended_on"
:config="fp_config"
/>
</li>
<li>
<label :for="`cancellation_deadline_${counter}`"
>Cancellation deadline:
</label>
<flat-pickr
:id="`cancellation_deadline_${counter}`"
v-model="period.cancellation_deadline"
:config="fp_config"
/>
</li>
<li>
<label :for="`notes_${counter}`">Notes: </label>
<input
type="text"
class="notes"
:name="`notes_${counter}`"
v-model="period.notes"
/>
</li>
</ol>
</fieldset>
<button @click="addPeriod" type="button" class="btn btn-primary">
<i class="fa fa-plus" aria-hidden="true"></i> Add new period
</button>
</fieldset>
</template>
<script>
import flatPickr from 'vue-flatpickr-component'
export default {
name: 'AgreementPeriods',
data() {
return { fp_config: flatpickr_defaults, dates_fixed: 0 }
},
props: {
periods: Array
},
beforeUpdate() {
if (!this.dates_fixed) {
this.periods.forEach(p => {
p.started_on = $date(p.started_on)
})
this.dates_fixed = 1
}
},
methods: {
addPeriod() {
this.periods.push({
started_on: null,
ended_on: null,
cancellation_deadline: null,
notes: null,
})
},
deletePeriod(counter) {
this.periods.splice(counter, 1)
}
},
components: {
flatPickr
}
}
</script>

View File

@ -0,0 +1,112 @@
<template>
<fieldset class="rows" id="agreement_user_roles">
<legend>Users</legend>
<fieldset
class="rows"
v-for="(user_role, counter) in user_roles"
v-bind:key="counter"
>
<legend>
Agreement user {{ counter + 1 }}
<a href="#" @click.prevent="deleteUser(counter)"
><i class="fa fa-trash"></i> Remove this user</a
>
</legend>
<ol>
<li>
<label :for="`user_id_${counter}`">User: </label>
<span class="user">
{{ user_role.patron_str }}
</span>
(<a
href="#"
@click="selectUser(counter)"
class="btn btn-default"
>Select user</a
>)
</li>
<li>
<label :for="`user_role_${counter}`">Role: </label>
<b-form-select v-model="user_role.role" required>
<b-form-select-option value=""></b-form-select-option>
<b-form-select-option
v-for="r in av_user_roles"
:key="r.authorised_values"
:value="r.authorised_value"
:selected="
r.authorised_value == user_role.role
? true
: false
"
>{{ r.lib }}</b-form-select-option
>
</b-form-select>
<span class="required">Required</span>
</li>
</ol>
</fieldset>
<input
type="hidden"
name="selected_patron_id"
id="selected_patron_id"
/>
<button @click="addUser" type="button" class="btn btn-primary">
<i class="fa fa-plus" aria-hidden="true"></i> Add new user
</button>
</fieldset>
</template>
<script>
export default {
name: 'AgreementUserRoles',
props: {
av_user_roles: Array,
user_roles: Array,
},
beforeUpdate() {
this.user_roles.forEach(u => {
u.patron_str = $patron_to_html(u.patron)
})
},
methods: {
addUser() {
this.user_roles.push({
user_id: null,
role: null,
patron_str: '',
})
},
deleteUser(counter) {
this.user_roles.splice(counter, 1)
},
selectUser(counter) {
let select_user_window = window.open("/cgi-bin/koha/members/search.pl?columns=cardnumber,name,category,branch,action&selection_type=select&filter=erm_users",
'PatronPopup',
'width=740,height=450,location=yes,toolbar=no,'
+ 'scrollbars=yes,resize=yes'
)
// This is a bit dirty, the "select user" window should be rewritten and be a Vue component
// but that's not for now...
select_user_window.addEventListener('beforeunload', this.newUserSelected, false)
select_user_window.counter = counter
},
newUserSelected(e) {
let c = e.currentTarget.counter
let selected_patron_id = document.getElementById("selected_patron_id").value
fetch('/api/v1/patrons/' + selected_patron_id)
.then(res => res.json())
.then(
(result) => {
this.user_roles[c].patron = result
this.user_roles[c].patron_str = $patron_to_html(result)
this.user_roles[c].user_id = result.patron_id
}).catch(
(error) => {
this.$emit('set-error', error)
}
)
}
},
}
</script>

View File

@ -0,0 +1,9 @@
<template>
<button variant="default" size="sm">Delete</button>
</template>
<script>
export default {
name: "AgreementsButtonDelete"
}
</script>

View File

@ -0,0 +1,9 @@
<template>
<button variant="default" size="sm">Edit</button>
</template>
<script>
export default {
name: "AgreementsButtonEdit",
}
</script>

View File

@ -0,0 +1,283 @@
<template>
<h2 v-if="agreement.agreement_id">Edit agreement</h2>
<h2 v-else>New agreement</h2>
<div>
<b-form @submit="onSubmit">
<b-form-group
id="agreement_name"
label="Agreement name:"
label-for="agreement_name"
label-cols="4"
label-cols-lg="2"
>
<b-form-input
id="agreement_name"
v-model="agreement.name"
placeholder="Agreement name"
required
></b-form-input>
<span class="required">Required</span>
</b-form-group>
<b-form-group
id="agreement_vendor_id"
label="Vendor:"
label-for="agreement_vendor_id"
label-cols="4"
label-cols-lg="2"
>
<b-form-select v-model="agreement.vendor_id">
<b-form-select-option value=""></b-form-select-option>
<b-form-select-option
v-for="vendor in vendors"
:key="vendor.vendor_id"
:value="vendor.id"
:selected="
vendor.id == agreement.vendor_id ? true : false
"
>{{ vendor.name }}</b-form-select-option
>
</b-form-select>
</b-form-group>
<b-form-group
id="agreement_description"
label="Description"
label-for="agreement_description"
label-cols="4"
label-cols-lg="2"
>
<b-form-input
id="agreement_description"
v-model="agreement.description"
placeholder="Description"
required
></b-form-input>
<span class="required">Required</span>
</b-form-group>
<b-form-group
id="agreement_status"
label="Status:"
label-for="agreement_status"
label-cols="4"
label-cols-lg="2"
>
<b-form-select
id="agreement_status"
v-model="agreement.status"
@change="onStatusChange($event)"
required
>
<b-form-select-option value=""></b-form-select-option>
<b-form-select-option
v-for="status in av_statuses"
:key="status.authorised_values"
:value="status.authorised_value"
:selected="
status.authorised_value == agreement.status
? true
: false
"
>{{ status.lib }}</b-form-select-option
>
</b-form-select>
<span class="required">Required</span>
</b-form-group>
<b-form-group
id="agreement_closure_reason"
label="Closure reason:"
label-for="agreement_closure_reason"
label-cols="4"
label-cols-lg="2"
>
<b-form-select
id="agreement_closure_reason"
v-model="agreement.closure_reason"
:disabled="agreement.status == 'closed' ? true : false"
>
<b-form-select-option value=""></b-form-select-option>
<b-form-select-option
v-for="r in av_closure_reasons"
:key="r.authorised_values"
:value="r.authorised_value"
:selected="
r.authorised_value == agreement.closure_reason
? true
: false
"
>{{ r.lib }}</b-form-select-option
>
</b-form-select>
</b-form-group>
<b-form-group
label="Is perpetual:"
label-for="agreement_is_perpetual"
label-cols="4"
label-cols-lg="2"
>
<b-form-radio-group
id="agreement_is_perpetual"
label="Is perpetual:"
label-for="agreement_is_perpetual"
label-cols="4"
label-cols-lg="2"
v-model="agreement.is_perpetual"
:options="is_perpetual_options"
>
</b-form-radio-group>
</b-form-group>
<b-form-group
id="agreement_renewal_priority"
label="Renewal priority:"
label-for="agreement_renewal_priority"
label-cols="4"
label-cols-lg="2"
>
<b-form-select v-model="agreement.renewal_priority">
<b-form-select-option value=""></b-form-select-option>
<b-form-select-option
v-for="p in av_renewal_priorities"
:key="p.authorised_values"
:value="p.authorised_value"
:selected="
p.authorised_value == agreement.renewal_priority
? true
: false
"
>{{ p.lib }}</b-form-select-option
>
</b-form-select>
</b-form-group>
<b-form-group
id="agreement_license_info"
label="License info:"
label-for="agreement_license_info"
label-cols="4"
label-cols-lg="2"
>
<b-form-input
id="agreement_license_info"
v-model="agreement.license_info"
placeholder="License info"
></b-form-input>
</b-form-group>
<AgreementPeriods :periods="agreement.periods" />
<AgreementUserRoles
:user_roles="agreement.user_roles"
:av_user_roles="av_user_roles"
/>
<b-button type="submit" variant="primary">Submit</b-button>
<a href="#" @click="$emit('switch-view', 'list')">Cancel</a>
</b-form>
</div>
</template>
<script>
import AgreementPeriods from './AgreementPeriods.vue'
import AgreementUserRoles from './AgreementUserRoles.vue'
export default {
data() {
return {
is_perpetual_options: [{ text: "Yes", value: true }, { text: "No", value: false }],
agreement: {
agreement_id: null,
name: '',
vendor_id: null,
description: '',
status: '',
closure_reason: '',
is_perpetual: false,
renewal_priority: '',
license_info: '',
periods: [],
user_roles: [],
}
}
},
created() {
if (!this.agreement_id) return
const apiUrl = '/api/v1/erm/agreements/' + this.agreement_id
fetch(apiUrl, {
headers: {
'x-koha-embed': 'periods,user_roles,user_roles.patron'
}
})
.then(res => res.json())
.then(
(result) => {
this.agreement = result
}
)
},
methods: {
onSubmit() {
//let agreement= Object.assign( {} ,this.agreement); // copy
let agreement = JSON.parse(JSON.stringify(this.agreement)) // copy
let apiUrl = '/api/v1/erm/agreements'
const myHeaders = new Headers()
myHeaders.append('Content-Type', 'application/json')
let method = 'POST'
if (agreement.agreement_id) {
method = 'PUT'
apiUrl += '/' + agreement.agreement_id
}
delete agreement.agreement_id
agreement.periods.forEach(p => {
p.started_on = $date_to_rfc3339(p.started_on)
p.ended_on = p.ended_on ? $date_to_rfc3339(p.ended_on) : null
p.cancellation_deadline = p.cancellation_deadline ? $date_to_rfc3339(p.cancellation_deadline) : null
})
agreement.periods = agreement.periods.map(({ agreement_id, agreement_period_id, ...keepAttrs }) => keepAttrs)
agreement.user_roles = agreement.user_roles.map(({ patron, patron_str, ...keepAttrs }) => keepAttrs)
const options = {
method: method,
body: JSON.stringify(agreement),
myHeaders
}
fetch(apiUrl, options)
.then(response => {
if (response.status == 200) {
this.$emit('agreement-updated')
} else if (response.status == 201) {
this.$emit('agreement-created')
} else {
this.$emit('set-error', response.message || response.statusText)
}
}).catch(
(error) => {
this.$emit('set-error', error)
}
)
},
onStatusChange(status) {
if (status == 'closed') {
this.agreement.closure_reason = ''
}
}
},
emits: ['agreement-created', 'agreement-updated', 'set-error', 'switch-view'],
props: {
agreement_id: Number,
vendors: Array,
av_statuses: Array,
av_closure_reasons: Array,
av_renewal_priorities: Array,
av_user_roles: Array,
},
components: {
AgreementPeriods,
AgreementUserRoles
},
name: "AgreementsFormAdd",
}
</script>

View File

@ -0,0 +1,96 @@
<template>
<h2>Delete agreement</h2>
<div>
<b-form @submit="onSubmit">
<b-form-group
id="agreement_name"
label="Agreement name:"
label-for="agreement_name"
label-cols="4"
label-cols-lg="2"
>
{{ agreement.name }}
</b-form-group>
<b-form-group
id="agreement_vendor"
label="Vendor:"
label-for="agreement_vendor"
label-cols="4"
label-cols-lg="2"
>
{{ agreement.vendor_id }}
</b-form-group>
<b-form-group
id="agreement_description"
label="Description:"
label-for="agreement_description"
label-cols="4"
label-cols-lg="2"
>
{{ agreement.description }}
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
<a href="#" @click="$emit('switch-view', 'list')">Cancel</a>
</b-form>
</div>
</template>
<script>
export default {
data() {
return {
agreement: {},
}
},
created() {
const apiUrl = '/api/v1/erm/agreements/' + this.agreement_id
fetch(apiUrl)
.then(res => res.json())
.then(
(result) => {
this.agreement = result
},
).catch(
(error) => {
this.$emit('set-error', error)
}
)
},
methods: {
onSubmit() {
let apiUrl = '/api/v1/erm/agreements/' + this.agreement_id
const myHeaders = new Headers()
myHeaders.append('Content-Type', 'application/json')
const options = {
method: 'DELETE',
myHeaders
}
fetch(apiUrl, options)
.then(
(response) => {
if (response.status == 204) {
this.$emit('agreement-deleted')
} else {
this.$emit('set-error', response.message || response.statusText)
}
}
).catch(
(error) => {
this.$emit('set-error', error)
}
)
}
},
emits: ['agreement-deleted', 'set-error', 'switch-view'],
props: {
agreement_id: Number
},
name: "AgreementsFormConfirmDelete",
}
</script>

View File

@ -0,0 +1,192 @@
<template>
<div>
<table v-if="agreements.length" id="my_table"></table>
<div v-else-if="this.initialized" class="dialog message">
There are no agreements defined.
</div>
<div v-else>Loading...</div>
</div>
</template>
<script>
import AgreementsButtonEdit from "./AgreementsButtonEdit.vue"
import AgreementsButtonDelete from "./AgreementsButtonDelete.vue"
import { createVNode, defineComponent, render, resolveComponent } from 'vue'
export default {
created() {
const apiUrl = '/api/v1/erm/agreements'
fetch(apiUrl)
.then(res => res.json())
.then(
(result) => {
this.agreements = result
this.initialized = true
},
).catch(
(error) => {
this.$emit('set-error', error)
}
)
},
updated() {
let edit_agreement = this.edit_agreement
let delete_agreement = this.delete_agreement
let vendors_map = this.vendors.reduce((map, e) => {
map[e.id] = e
return map
}, {})
let statuses_map = this.av_statuses.reduce((map, e) => {
map[e.authorised_value] = e
return map
}, {})
let closure_reasons_map = this.av_closure_reasons.reduce((map, e) => {
map[e.authorised_value] = e
return map
}, {})
let renewal_priorities_map = this.av_renewal_priorities.reduce((map, e) => {
map[e.authorised_value] = e
return map
}, {})
$('#my_table').kohaTable({
"ajax": {
"url": agreements_table_url,
},
"order": [[1, "asc"]],
"columnDefs": [{
"targets": [0, 1, 2, 3, 4],
"render": function (data, type, row, meta) {
if (type == 'display') {
return escape_str(data)
}
return data
}
}],
"columns": [
{
"title": __("Agrement ID"),
"data": "agreement_id",
"searchable": true,
"orderable": true
},
{
"title": __("Vendor"),
"data": "vendor_id",
"searchable": true,
"orderable": true,
"render": function (data, type, row, meta) {
return row.vendor_id != undefined ? escape_str(vendors_map[row.vendor_id].name) : ""
}
},
{
"title": __("Name"),
"data": "name",
"searchable": true,
"orderable": true
},
{
"title": __("description"),
"data": "description",
"searchable": true,
"orderable": true
},
{
"title": __("Status"),
"data": "status",
"searchable": true,
"orderable": true,
"render": function (data, type, row, meta) {
return escape_str(statuses_map[row.status].lib)
}
},
{
"title": __("Closure reason"),
"data": "closure_reason",
"searchable": true,
"orderable": true,
"render": function (data, type, row, meta) {
return row.closure_reason != undefined && row.closure_reason != "" ? escape_str(closure_reasons_map[row.closure_reason].lib) : ""
}
},
{
"title": __("Is perpetual"),
"data": "is_perpetual",
"searchable": true,
"orderable": true,
"render": function (data, type, row, meta) {
return escape_str(row.is_perpetual ? _("Yes") : _("No"))
}
},
{
"title": __("Renewal priority"),
"data": "renewal_priority",
"searchable": true,
"orderable": true,
"render": function (data, type, row, meta) {
return row.renewal_priority != undefined && row.renewal_priority != "" ? escape_str(renewal_priorities_map[row.renewal_priority].lib) : ""
}
},
{
"title": __("Actions"),
"data": function (row, type, val, meta) {
return '<div class="actions" data-agreement_id="' + row.agreement_id + '"></div>'
},
"searchable": false,
"orderable": false
}
],
drawCallback: function (settings) {
$.each($(this).find(".actions"), function (index, e) {
let agreement_id = $(e).data('agreement_id')
let editButton = createVNode(AgreementsButtonEdit, {
onClick: () => {
edit_agreement(agreement_id)
}
})
let deleteButton = createVNode(AgreementsButtonDelete, {
onClick: () => {
delete_agreement(agreement_id)
}
})
let n = createVNode('span', {}, [editButton, deleteButton])
render(n, e)
})
},
preDrawCallback: function(settings){
var table_id = settings.nTable.id
$("#"+table_id).find("thead th").eq(1).attr('data-filter', 'vendors');
}
}, columns_settings, 1)
},
beforeUnmount() {
$('#my_table')
.DataTable()
.destroy(true)
},
data: function () {
return {
agreements: [],
initialized: false,
}
},
methods: {
edit_agreement: function (agreement_id) {
this.$emit('set-current-agreement-id', agreement_id)
this.$emit('switch-view', 'add-form')
},
delete_agreement: function (agreement_id) {
this.$emit('set-current-agreement-id', agreement_id)
this.$emit('switch-view', 'confirm-delete-form')
},
},
props: {
vendors: Array,
av_statuses: Array,
av_closure_reasons: Array,
av_renewal_priorities: Array,
},
name: "AgreementsList",
}
</script>

View File

@ -0,0 +1,15 @@
<template>
<b-button
@click="$emit('switch-view', 'add-form')"
id="new_agreement"
variant="default"
><font-awesome-icon icon="plus" /> New agreement</b-button
>
</template>
<script>
export default {
name: "AgreementsToolbar",
emits: ['switch-view'],
}
</script>

View File

@ -0,0 +1,18 @@
import {createApp} from 'vue'
import BootstrapVue3 from 'bootstrap-vue-3'
//import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css'
import {library} from "@fortawesome/fontawesome-svg-core"
import {faPlus, faPencil, faTrash} from "@fortawesome/free-solid-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/vue-fontawesome"
library.add(faPlus, faPencil, faTrash)
import App from './Agreements.vue'
createApp(App)
.component("font-awesome-icon", FontAwesomeIcon)
.use(BootstrapVue3)
.mount('#agreements')

View File

@ -7,7 +7,14 @@
"test": "test"
},
"dependencies": {
"bootstrap": "^4.5.2",
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.0.0",
"@fortawesome/vue-fontawesome": "^3.0.0-5",
"@popperjs/core": "^2.11.2",
"babel-core": "^7.0.0-beta.3",
"bootstrap": "^5.1.3",
"bootstrap-vue-3": "^0.1.7",
"css-loader": "^6.6.0",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^4.0.0",
"gulp-concat-po": "^1.0.0",
@ -20,10 +27,15 @@
"js-yaml": "^3.13.1",
"lodash": "^4.17.12",
"merge-stream": "^2.0.0",
"minimist": "^1.2.5"
"minimist": "^1.2.5",
"style-loader": "^3.3.1",
"vue": "^3.2.31",
"vue-flatpickr-component": "^9"
},
"scripts": {
"build": "node_modules/.bin/gulp build",
"build_js": "webpack --mode production",
"watch_js": "webpack --mode development --watch",
"css": "node_modules/.bin/gulp css",
"watch": "node_modules/.bin/gulp watch"
},
@ -43,6 +55,23 @@
"postcss": "^8.4.14",
"stylelint-config-standard-scss": "^5.0.0",
"stylelint-order": "^5.0.0",
"stylelint": "^14.9.1"
"stylelint": "^14.9.1",
"@babel/core": "^7.17.5",
"@babel/preset-env": "^7.16.11",
"@vue/compiler-sfc": "^3.2.31",
"babel-loader": "^8.2.3",
"babelify": "^10.0.0",
"browserify": "^17.0.0",
"clean-webpack-plugin": "^4.0.0",
"gulp-tap": "^1.0.1",
"html-webpack-plugin": "^5.5.0",
"ts-loader": "^9.2.7",
"typescript": "^4.6.2",
"vinyl-source-stream": "^2.0.0",
"vue-loader": "^17.0.0",
"watchify": "^4.0.0",
"webpack": "^5.69.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
}
}

4
tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
"compilerOptions": {
}
}

36
webpack.config.js Normal file
View File

@ -0,0 +1,36 @@
const { VueLoaderPlugin } = require("vue-loader");
const autoprefixer = require("autoprefixer");
const path = require("path");
module.exports = {
entry: {
main: "./koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "koha-tmpl/intranet-tmpl/prog/js/vue/dist/"),
chunkFilename: "[name].js",
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/]
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
],
},
plugins: [
new VueLoaderPlugin(),
],
};