Compare commits
27 commits
developmen
...
v0.9.6
Author | SHA1 | Date | |
---|---|---|---|
|
6309d44076 | ||
|
d4ad360f38 | ||
|
76a4b2178c | ||
|
c41bbdc35f | ||
|
8855d64a6e | ||
|
b92ea1f7c6 | ||
|
832dab70cb | ||
|
29cd70ca1c | ||
|
d3a2410507 | ||
|
733107539d | ||
|
c3ae7a2615 | ||
|
baffe144f3 | ||
|
a11727ba1c | ||
|
9517b98c24 | ||
|
b3036c13ee | ||
|
91677034ca | ||
|
423c68aca2 | ||
|
9d07830e27 | ||
|
f0b1540f3e | ||
|
b873e63d55 | ||
|
08b5d25764 | ||
|
18d223743d | ||
|
af312ffbe3 | ||
|
6b2b61856f | ||
|
105c9093b8 | ||
|
049b4836c5 | ||
|
c4681ffc3a |
27 changed files with 696 additions and 96 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -2,11 +2,24 @@
|
|||
|
||||
# Next Release
|
||||
|
||||
# v0.9.6
|
||||
|
||||
* **Admin Feature** Merged organisation lists into one list
|
||||
* **Admin Feature** Paginated Organisation listings
|
||||
* **Admin Feature** Added flags to Organisations listings
|
||||
* **Admin Feature** Added `is_local` flag to Organisations to start categorising odd stores
|
||||
* **Admin Feature** Feedback items now word wrap
|
||||
* **Admin Feature** Rework transaction viewing
|
||||
* **Admin Feature** Implemented import method for importing previous data from csv
|
||||
* **Admin Feature** Added badges for various organisation flags eg. local, user, validated
|
||||
* **Admin Feature** Enabled merging of organisations to reduce duplicates
|
||||
* **Admin Feature** Added badges to user listing to show whether customer or organisation
|
||||
* **Admin Feature** Added pagination to user listings
|
||||
* Improved logging for debugging issues with login
|
||||
|
||||
# v0.9.5
|
||||
|
||||
* Added leaderboard api for web-app with pagination
|
||||
* Location is now updated on registration. Customers location is truncated to 2
|
||||
decimal places based on their postcode.
|
||||
* Location is also updated on changing a users postcode
|
||||
|
|
|
@ -39,6 +39,7 @@ sub startup {
|
|||
$self->plugin('Pear::LocalLoop::Plugin::BootstrapPagination', { bootstrap4 => 1 } );
|
||||
$self->plugin('Pear::LocalLoop::Plugin::Validators');
|
||||
$self->plugin('Pear::LocalLoop::Plugin::Datetime');
|
||||
$self->plugin('Pear::LocalLoop::Plugin::Currency');
|
||||
$self->plugin('Pear::LocalLoop::Plugin::Postcodes');
|
||||
$self->plugin('Pear::LocalLoop::Plugin::TemplateHelpers');
|
||||
|
||||
|
@ -148,6 +149,7 @@ sub startup {
|
|||
$api->post('/user-history')->to('api-user#post_user_history');
|
||||
$api->post('/stats')->to('api-stats#post_index');
|
||||
$api->post('/stats/leaderboard')->to('api-stats#post_leaderboards');
|
||||
$api->post('/stats/leaderboard/paged')->to('api-stats#post_leaderboards_paged');
|
||||
$api->post('/outgoing-transactions')->to('api-transactions#post_transaction_list_purchases');
|
||||
|
||||
|
||||
|
@ -188,6 +190,9 @@ sub startup {
|
|||
$admin_routes->post('/organisations/add')->to('admin-organisations#add_org_submit');
|
||||
$admin_routes->get('/organisations/:id')->to('admin-organisations#valid_read');
|
||||
$admin_routes->post('/organisations/:id')->to('admin-organisations#valid_edit');
|
||||
$admin_routes->get('/organisations/:id/merge')->to('admin-organisations#merge_list');
|
||||
$admin_routes->get('/organisations/:id/merge/:target_id')->to('admin-organisations#merge_detail');
|
||||
$admin_routes->post('/organisations/:id/merge/:target_id')->to('admin-organisations#merge_confirm');
|
||||
|
||||
$admin_routes->get('/feedback')->to('admin-feedback#index');
|
||||
$admin_routes->get('/feedback/:id')->to('admin-feedback#read');
|
||||
|
@ -206,10 +211,9 @@ sub startup {
|
|||
$admin_routes->get('/import/:set_id')->to('admin-import#list');
|
||||
$admin_routes->get('/import/:set_id/user')->to('admin-import#get_user');
|
||||
$admin_routes->get('/import/:set_id/org')->to('admin-import#get_org');
|
||||
$admin_routes->post('/import/:set_id/org')->to('admin-import#set_org');
|
||||
|
||||
$admin_routes->get('/import/:set_id/:value_id')->to('admin-import#get_value');
|
||||
$admin_routes->post('/import/:set_id/:value_id')->to('admin-import#post_value');
|
||||
$admin_routes->get('/import/:set_id/ignore/:value_id')->to('admin-import#ignore_value');
|
||||
$admin_routes->get('/import/:set_id/import')->to('admin-import#run_import');
|
||||
# my $user_routes = $r->under('/')->to('root#under');
|
||||
|
||||
# $user_routes->get('/home')->to('root#home');
|
||||
|
|
|
@ -38,9 +38,12 @@ sub home {
|
|||
sub auth_login {
|
||||
my $c = shift;
|
||||
|
||||
$c->app->log->debug( __PACKAGE__ . " admin login attempt for [" . $c->param('email') . "]" );
|
||||
|
||||
if ( $c->authenticate($c->param('email'), $c->param('password')) ) {
|
||||
$c->redirect_to('/admin/home');
|
||||
} else {
|
||||
$c->app->log->info( __PACKAGE__ . " failed admin login for [" . $c->param('email') . "]" );
|
||||
$c->redirect_to('/admin');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,13 @@ sub list {
|
|||
my $c = shift;
|
||||
my $set_id = $c->param('set_id');
|
||||
|
||||
my $include_ignored = $c->param('ignored');
|
||||
my $include_imported = $c->param('imported');
|
||||
|
||||
my $import_set = $c->result_set->find($set_id);
|
||||
my $import_value_rs = $c->result_set->get_values($set_id);
|
||||
my $import_users_rs = $c->result_set->get_users($set_id);
|
||||
my $import_org_rs = $c->result_set->get_orgs($set_id);
|
||||
my $import_value_rs = $c->result_set->get_values($set_id, $include_ignored, $include_imported);
|
||||
my $import_users_rs = $c->result_set->get_users($set_id, $include_ignored, $include_imported);
|
||||
my $import_org_rs = $c->result_set->get_orgs($set_id, $include_ignored, $include_imported);
|
||||
my $import_lookup_rs = $c->result_set->get_lookups($set_id);
|
||||
|
||||
$c->stash(
|
||||
|
@ -69,7 +72,7 @@ sub post_add {
|
|||
};
|
||||
|
||||
if ( defined $error ) {
|
||||
$c->flash( error => $error, csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( $error );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -78,7 +81,7 @@ sub post_add {
|
|||
my @required = grep {/^user$|^value$|^date$|^organisation$/} @csv_headers;
|
||||
|
||||
unless ( scalar( @required ) == 4 ) {
|
||||
$c->flash( error => 'Required columns not available', csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( 'Required columns not available' );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -86,7 +89,7 @@ sub post_add {
|
|||
my $csv_output = $csv->getline_hr_all( $fh );
|
||||
|
||||
unless ( scalar( @$csv_output ) ) {
|
||||
$c->flash( error => "No data found", csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( "No data found" );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -94,7 +97,7 @@ sub post_add {
|
|||
for my $data ( @$csv_output ) {
|
||||
for my $key ( qw/ user value organisation / ) {
|
||||
unless ( defined $data->{$key} ) {
|
||||
$c->flash( error => "Undefined [$key] data found", csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( "Undefined [$key] data found" );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -103,7 +106,7 @@ sub post_add {
|
|||
my $dtp = DateTime::Format::Strptime->new( pattern => $date_format );
|
||||
my $dt_obj = $dtp->parse_datetime($data->{date});
|
||||
unless ( defined $dt_obj ) {
|
||||
$c->flash( error => "Undefined or incorrect format for [date] data found", csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( "Undefined or incorrect format for [date] data found" );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -126,7 +129,7 @@ sub post_add {
|
|||
);
|
||||
|
||||
unless ( defined $value_set ) {
|
||||
$c->flash( error => 'Error creating new Value Set', csv_data => $csv_data, date_format => $date_format );
|
||||
$c->_csv_flash_error( 'Error creating new Value Set' );
|
||||
$c->redirect_to( '/admin/import/add' );
|
||||
return;
|
||||
}
|
||||
|
@ -135,6 +138,17 @@ sub post_add {
|
|||
$c->redirect_to( '/admin/import/' . $value_set->id );
|
||||
}
|
||||
|
||||
sub _csv_flash_error {
|
||||
my ( $c, $error ) = @_;
|
||||
$error //= "An error occurred";
|
||||
|
||||
$c->flash(
|
||||
error => $error,
|
||||
csv_data => $c->param('csv'),
|
||||
date_format => $c->param('date_format'),
|
||||
);
|
||||
}
|
||||
|
||||
sub get_user {
|
||||
my $c = shift;
|
||||
my $set_id = $c->param('set_id');
|
||||
|
@ -184,22 +198,128 @@ sub get_user {
|
|||
|
||||
sub get_org {
|
||||
my $c = shift;
|
||||
my $set_id = $c->param('set_id');
|
||||
my $org_name = $c->param('org');
|
||||
|
||||
my $values_rs = $c->result_set->find($set_id)->values->search(
|
||||
{
|
||||
org_name => $org_name,
|
||||
ignore_value => 0,
|
||||
}
|
||||
);
|
||||
|
||||
unless ( $values_rs->count > 0 ) {
|
||||
$c->flash( error => 'Organisation not found or all values are ignored' );
|
||||
return $c->redirect_to( '/admin/import/' . $set_id );
|
||||
}
|
||||
|
||||
my $lookup_result = $c->result_set->find($set_id)->lookups->find(
|
||||
{ name => $org_name },
|
||||
);
|
||||
|
||||
my $entity_id = $c->param('entity');
|
||||
|
||||
my $orgs_rs = $c->schema->resultset('Organisation');
|
||||
|
||||
if ( defined $entity_id && $orgs_rs->find({ entity_id => $entity_id }) ) {
|
||||
if ( defined $lookup_result ) {
|
||||
$lookup_result->update({ entity_id => $entity_id });
|
||||
} else {
|
||||
$lookup_result = $c->result_set->find($set_id)->lookups->create(
|
||||
{
|
||||
name => $org_name,
|
||||
entity_id => $entity_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
} elsif ( defined $entity_id ) {
|
||||
$c->stash( error => "Organisation does not exist" );
|
||||
}
|
||||
|
||||
$c->stash(
|
||||
orgs_rs => $orgs_rs,
|
||||
lookup => $lookup_result,
|
||||
org_name => $org_name,
|
||||
);
|
||||
}
|
||||
|
||||
sub set_org {
|
||||
my $c = shift;
|
||||
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
sub ignore_value {
|
||||
my $c = shift;
|
||||
my $set_id = $c->param('set_id');
|
||||
my $value_id = $c->param('value_id');
|
||||
|
||||
my $set_result = $c->result_set->find($set_id);
|
||||
unless ( defined $set_result ) {
|
||||
$c->flash( error => "Set does not exist" );
|
||||
return $c->redirect_to( '/admin/import' );
|
||||
}
|
||||
|
||||
my $value_result = $set_result->values->find($value_id);
|
||||
unless ( defined $value_result ) {
|
||||
$c->flash( error => "Value does not exist" );
|
||||
return $c->redirect_to( '/admin/import/' . $set_id );
|
||||
}
|
||||
|
||||
$value_result->update({ ignore_value => $value_result->ignore_value ? 0 : 1 });
|
||||
|
||||
$c->flash( success => "Updated value" );
|
||||
my $referer = $c->req->headers->header('Referer');
|
||||
return $c->redirect_to(
|
||||
defined $referer
|
||||
? $c->url_for($referer)->path_query
|
||||
: '/admin/import/' . $set_id
|
||||
);
|
||||
}
|
||||
|
||||
sub post_value {
|
||||
sub run_import {
|
||||
my $c = shift;
|
||||
my $set_id = $c->param('set_id');
|
||||
|
||||
my $set_result = $c->result_set->find($set_id);
|
||||
unless ( defined $set_result ) {
|
||||
$c->flash( error => "Set does not exist" );
|
||||
return $c->redirect_to( '/admin/import' );
|
||||
}
|
||||
|
||||
my $import_value_rs = $c->result_set->get_values($set_id, undef, undef);
|
||||
my $import_lookup = $c->result_set->get_lookups($set_id);
|
||||
my $entity_rs = $c->schema->resultset('Entity');
|
||||
|
||||
$c->schema->txn_do(
|
||||
sub {
|
||||
for my $value_result ( $import_value_rs->all ) {
|
||||
my $user_lookup = $import_lookup->{ $value_result->user_name };
|
||||
my $org_lookup = $import_lookup->{ $value_result->org_name };
|
||||
my $value_lookup = $c->parse_currency( $value_result->purchase_value );
|
||||
|
||||
if ( defined $user_lookup && defined $org_lookup && $value_lookup ) {
|
||||
my $user_entity = $entity_rs->find($user_lookup->{entity_id});
|
||||
my $org_entity = $entity_rs->find($org_lookup->{entity_id});
|
||||
my $distance = $c->get_distance_from_coords( $user_entity->type_object, $org_entity->type_object );
|
||||
my $transaction = $c->schema->resultset('Transaction')->create(
|
||||
{
|
||||
buyer => $user_entity,
|
||||
seller => $org_entity,
|
||||
value => $value_lookup * 100000,
|
||||
purchase_time => $value_result->purchase_date,
|
||||
distance => $distance,
|
||||
}
|
||||
);
|
||||
$value_result->update({transaction_id => $transaction->id });
|
||||
} else {
|
||||
$c->app->log->warn("Failed value import for value id [" . $value_result->id . "], ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$c->flash( success => "Import completed for ready values" );
|
||||
my $referer = $c->req->headers->header('Referer');
|
||||
return $c->redirect_to(
|
||||
defined $referer
|
||||
? $c->url_for($referer)->path_query
|
||||
: '/admin/import/' . $set_id
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -3,6 +3,11 @@ use Mojo::Base 'Mojolicious::Controller';
|
|||
|
||||
use Try::Tiny;
|
||||
|
||||
has result_set => sub {
|
||||
my $c = shift;
|
||||
return $c->schema->resultset('Organisation');
|
||||
};
|
||||
|
||||
sub list {
|
||||
my $c = shift;
|
||||
|
||||
|
@ -127,4 +132,104 @@ sub valid_edit {
|
|||
$c->redirect_to( '/admin/organisations/');
|
||||
}
|
||||
|
||||
sub merge_list {
|
||||
my $c = shift;
|
||||
|
||||
my $org_id = $c->param('id');
|
||||
my $org_result = $c->result_set->find($org_id);
|
||||
|
||||
if ( defined $org_result->entity->user ) {
|
||||
$c->flash( error => 'Cannot merge from user-owned organisation!' );
|
||||
$c->redirect_to( '/admin/organisations/' . $org_id );
|
||||
return;
|
||||
}
|
||||
|
||||
my $org_rs = $c->result_set->search(
|
||||
{
|
||||
id => { '!=' => $org_id },
|
||||
},
|
||||
{
|
||||
page => $c->param('page') || 1,
|
||||
rows => 10,
|
||||
order_by => { '-asc' => 'name' },
|
||||
}
|
||||
);
|
||||
|
||||
$c->stash(
|
||||
org_result => $org_result,
|
||||
org_rs => $org_rs,
|
||||
);
|
||||
}
|
||||
|
||||
sub merge_detail {
|
||||
my $c = shift;
|
||||
|
||||
my $org_id = $c->param('id');
|
||||
my $org_result = $c->result_set->find($org_id);
|
||||
|
||||
if ( defined $org_result->entity->user ) {
|
||||
$c->flash( error => 'Cannot merge from user-owned organisation!' );
|
||||
$c->redirect_to( '/admin/organisations/' . $org_id );
|
||||
return;
|
||||
}
|
||||
|
||||
my $target_id = $c->param('target_id');
|
||||
my $target_result = $c->result_set->find($target_id);
|
||||
|
||||
unless ( defined $target_result ) {
|
||||
$c->flash( error => 'Unknown target organisation' );
|
||||
$c->redirect_to( '/admin/organisations/' . $org_id . '/merge' );
|
||||
return;
|
||||
}
|
||||
|
||||
$c->stash(
|
||||
org_result => $org_result,
|
||||
target_result => $target_result,
|
||||
);
|
||||
}
|
||||
|
||||
sub merge_confirm {
|
||||
my $c = shift;
|
||||
|
||||
my $org_id = $c->param('id');
|
||||
my $org_result = $c->result_set->find($org_id);
|
||||
|
||||
if ( defined $org_result->entity->user ) {
|
||||
$c->flash( error => 'Cannot merge from user-owned organisation!' );
|
||||
$c->redirect_to( '/admin/organisations/' . $org_id );
|
||||
return;
|
||||
}
|
||||
|
||||
my $target_id = $c->param('target_id');
|
||||
my $target_result = $c->result_set->find($target_id);
|
||||
my $confirm = $c->param('confirm');
|
||||
|
||||
if ( $confirm eq 'checked' && defined $org_result && defined $target_result ) {
|
||||
try {
|
||||
$c->schema->txn_do( sub {
|
||||
# Done as an update, not update_all, so its damn fast - we're only
|
||||
# editing an id which is guaranteed to be an integer here, and this
|
||||
# makes it only one update statement.
|
||||
$org_result->entity->sales->update(
|
||||
{ seller_id => $target_result->entity->id }
|
||||
);
|
||||
my $count = $org_result->entity->sales->count;
|
||||
die "Failed to migrate all sales" if $count;
|
||||
$org_result->entity->delete;
|
||||
$c->schema->resultset('ImportLookup')->search({ entity_id => $org_result->entity->id })->delete;
|
||||
my $org_count = $c->result_set->search({id => $org_result->id })->count;
|
||||
my $entity_count = $c->schema->resultset('Entity')->search({id => $org_result->entity->id })->count;
|
||||
die "Failed to remove org" if $org_count;
|
||||
die "Failed to remove entity" if $entity_count;
|
||||
});
|
||||
} catch {
|
||||
$c->app->log->warn($_);
|
||||
};
|
||||
$c->flash( error => 'Engage' );
|
||||
} else {
|
||||
$c->flash( error => 'You must tick the confirmation box to proceed' );
|
||||
}
|
||||
$c->redirect_to( '/admin/organisations/' . $org_id . '/merge/' . $target_id );
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -22,9 +22,15 @@ has organisation_result_set => sub {
|
|||
sub index {
|
||||
my $c = shift;
|
||||
|
||||
my $user_rs = $c->user_result_set;
|
||||
$user_rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
|
||||
$c->stash( users => [ $user_rs->all ] );
|
||||
my $user_rs = $c->user_result_set->search(
|
||||
undef, {
|
||||
prefech => { entity => [ qw/ customer organisation / ] },
|
||||
page => $c->param('page') || 1,
|
||||
rows => 10,
|
||||
order_by => { -asc => 'email' },
|
||||
}
|
||||
);
|
||||
$c->stash( user_rs => $user_rs );
|
||||
}
|
||||
|
||||
sub read {
|
||||
|
|
|
@ -74,6 +74,8 @@ sub post_login {
|
|||
my $email = $validation->param('email');
|
||||
my $password = $validation->param('password');
|
||||
|
||||
$c->app->log->debug( __PACKAGE__ . " login attempt for [" . $email . "]" );
|
||||
|
||||
my $user_result = $c->schema->resultset('User')->find({ email => $email });
|
||||
|
||||
if ( defined $user_result ) {
|
||||
|
@ -86,6 +88,8 @@ sub post_login {
|
|||
display_name => $user_result->name,
|
||||
user_type => $user_result->type,
|
||||
});
|
||||
} else {
|
||||
$c->app->log->info( __PACKAGE__ . " failed login for [" . $email . "]" );
|
||||
}
|
||||
}
|
||||
return $c->render(
|
||||
|
|
|
@ -109,4 +109,69 @@ sub post_leaderboards {
|
|||
});
|
||||
}
|
||||
|
||||
sub post_leaderboards_paged {
|
||||
my $c = shift;
|
||||
|
||||
my $validation = $c->validation;
|
||||
$validation->input( $c->stash->{api_json} );
|
||||
|
||||
my $leaderboard_rs = $c->schema->resultset('Leaderboard');
|
||||
|
||||
$validation->required('type')->in_resultset( 'type', $leaderboard_rs );
|
||||
$validation->optional('page')->number;
|
||||
|
||||
return $c->api_validation_error if $validation->has_error;
|
||||
|
||||
my $page = 1;
|
||||
|
||||
my $today_board = $leaderboard_rs->get_latest( $validation->param('type') );
|
||||
|
||||
if ( !defined $validation->param('page') || $validation->param('page') < 1 ) {
|
||||
my $user_position = $today_board->values->find({ entity_id => $c->stash->{api_user}->entity->id });
|
||||
$page = int(defined $user_position ? $user_position->{position} : 0 / 10) + 1;
|
||||
} else {
|
||||
$page = $validation->param('page');
|
||||
}
|
||||
|
||||
my $today_values = $today_board->values->search(
|
||||
{},
|
||||
{
|
||||
page => $page,
|
||||
rows => 10,
|
||||
order_by => { -asc => 'me.position' },
|
||||
columns => [
|
||||
qw/
|
||||
me.value
|
||||
me.trend
|
||||
me.position
|
||||
/,
|
||||
{ display_name => 'customer.display_name' },
|
||||
],
|
||||
join => { entity => 'customer' },
|
||||
},
|
||||
);
|
||||
$today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' );
|
||||
|
||||
my @leaderboard_array = $today_values->all;
|
||||
|
||||
if ( $validation->param('type') =~ /total$/ ) {
|
||||
@leaderboard_array = (map {
|
||||
{
|
||||
%$_,
|
||||
value => $_->{value} / 100000,
|
||||
}
|
||||
} @leaderboard_array);
|
||||
}
|
||||
|
||||
my $current_user_position = $today_values->find({ entity_id => $c->stash->{api_user}->entity->id });
|
||||
|
||||
return $c->render( json => {
|
||||
success => Mojo::JSON->true,
|
||||
leaderboard => [ @leaderboard_array ],
|
||||
user_position => defined $current_user_position ? $current_user_position->{position} : 0,
|
||||
page => $page,
|
||||
count => $today_values->pager->total_entries,
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
22
lib/Pear/LocalLoop/Plugin/Currency.pm
Normal file
22
lib/Pear/LocalLoop/Plugin/Currency.pm
Normal file
|
@ -0,0 +1,22 @@
|
|||
package Pear::LocalLoop::Plugin::Currency;
|
||||
use Mojo::Base 'Mojolicious::Plugin';
|
||||
|
||||
sub register {
|
||||
my ( $plugin, $app, $cong ) = @_;
|
||||
|
||||
$app->helper( parse_currency => sub {
|
||||
my ( $c, $currency_string ) = @_;
|
||||
my $value;
|
||||
if ( $currency_string =~ /^£([\d.]+)/ ) {
|
||||
$value = $1 * 1;
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
|
||||
$app->helper( format_currency_from_db => sub {
|
||||
my ( $c, $value ) = @_;
|
||||
return sprintf( '£%.2f', $value / 100000 );
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
|
@ -39,7 +39,7 @@ __PACKAGE__->add_columns(
|
|||
size => 255,
|
||||
},
|
||||
transaction_id => {
|
||||
data_type => 'varchar',
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 1,
|
||||
},
|
||||
|
|
|
@ -123,4 +123,10 @@ sub to_bool {
|
|||
}
|
||||
}
|
||||
|
||||
sub user {
|
||||
my $self = shift;
|
||||
|
||||
return $self->entity->user;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -8,10 +8,15 @@ use base 'DBIx::Class::ResultSet';
|
|||
sub get_values {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $include_ignored = shift;
|
||||
my $include_imported = shift;
|
||||
|
||||
return $self->find($id)->search_related(
|
||||
'values',
|
||||
undef,
|
||||
{
|
||||
( $include_ignored ? () : ( ignore_value => 0 ) ),
|
||||
( $include_imported ? () : ( transaction_id => undef ) ),
|
||||
},
|
||||
{
|
||||
order_by => { '-asc' => 'id' },
|
||||
},
|
||||
|
@ -20,9 +25,8 @@ sub get_values {
|
|||
|
||||
sub get_users {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
|
||||
return $self->get_values($id)->search({},
|
||||
return $self->get_values(@_)->search({},
|
||||
{
|
||||
group_by => 'user_name',
|
||||
},
|
||||
|
@ -31,9 +35,8 @@ sub get_users {
|
|||
|
||||
sub get_orgs {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
|
||||
return $self->get_values($id)->search({},
|
||||
return $self->get_values(@_)->search({},
|
||||
{
|
||||
group_by => 'org_name',
|
||||
},
|
||||
|
@ -44,13 +47,23 @@ sub get_lookups {
|
|||
my $self = shift;
|
||||
my $id = shift;
|
||||
|
||||
return $self->find($id)->search_related(
|
||||
my $lookup_rs = $self->find($id)->search_related(
|
||||
'lookups',
|
||||
undef,
|
||||
{
|
||||
order_by => { '-asc' => 'id' },
|
||||
prefetch => { entity => [ qw/ organisation customer / ] },
|
||||
order_by => { '-asc' => 'me.id' },
|
||||
},
|
||||
);
|
||||
my $lookup_map = {
|
||||
map {
|
||||
$_->name => {
|
||||
entity_id => $_->entity->id,
|
||||
name => $_->entity->name,
|
||||
},
|
||||
} $lookup_rs->all
|
||||
};
|
||||
return $lookup_map;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -206,7 +206,7 @@ CREATE TABLE "import_values" (
|
|||
"purchase_date" timestamp NOT NULL,
|
||||
"purchase_value" character varying(255) NOT NULL,
|
||||
"org_name" character varying(255) NOT NULL,
|
||||
"transaction_id" character varying,
|
||||
"transaction_id" integer,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
CREATE INDEX "import_values_idx_set_id" on "import_values" ("set_id");
|
||||
|
|
|
@ -206,7 +206,7 @@ CREATE TABLE "import_values" (
|
|||
"purchase_date" timestamp NOT NULL,
|
||||
"purchase_value" character varying(255) NOT NULL,
|
||||
"org_name" character varying(255) NOT NULL,
|
||||
"transaction_id" character varying,
|
||||
"transaction_id" integer,
|
||||
"ignore_value" boolean DEFAULT false NOT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
|
|
@ -220,7 +220,7 @@ CREATE TABLE "import_values" (
|
|||
"purchase_date" timestamp NOT NULL,
|
||||
"purchase_value" character varying(255) NOT NULL,
|
||||
"org_name" character varying(255) NOT NULL,
|
||||
"transaction_id" character varying,
|
||||
"transaction_id" integer,
|
||||
"ignore_value" boolean DEFAULT false NOT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@ CREATE TABLE "import_values" (
|
|||
"purchase_date" timestamp NOT NULL,
|
||||
"purchase_value" character varying(255) NOT NULL,
|
||||
"org_name" character varying(255) NOT NULL,
|
||||
"transaction_id" character varying,
|
||||
"transaction_id" integer,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
CREATE INDEX "import_values_idx_set_id" on "import_values" ("set_id");
|
||||
|
|
|
@ -128,7 +128,7 @@ CREATE TABLE feedback (
|
|||
package_name varchar(255) NOT NULL,
|
||||
version_code varchar(255) NOT NULL,
|
||||
version_number varchar(255) NOT NULL,
|
||||
actioned boolean NOT NULL DEFAULT false,
|
||||
actioned boolean NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
);
|
||||
CREATE INDEX feedback_idx_user_id ON feedback (user_id);
|
||||
|
|
36
templates/admin/import/get_org.html.ep
Normal file
36
templates/admin/import/get_org.html.ep
Normal file
|
@ -0,0 +1,36 @@
|
|||
% layout 'admin_errors';
|
||||
% title 'Import';
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<h3><%= $org_name %></h3>
|
||||
</div>
|
||||
<div class="col-4 mb-3">
|
||||
<a href="<%= url_for '/admin/import/' . $c->param('set_id') %>"
|
||||
class="btn btn-success">
|
||||
Return to Import
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h4 class="card-header">
|
||||
Organisations
|
||||
</h4>
|
||||
<div class="card-body text-muted">
|
||||
Choose a user to assign to this name
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
% for my $org ( $orgs_rs->all ) {
|
||||
<a href="<%= url_with->query([ entity => $org->entity_id ]) %>"
|
||||
class="list-group-item list-group-item-action<%= defined $lookup && $lookup->entity_id == $org->entity_id ? ' list-group-item-success' : '' %>">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
%= $org->name
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -12,22 +12,31 @@
|
|||
</div>
|
||||
% }
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="card-header">
|
||||
CSV Import
|
||||
<a href="<%= url_for . '/add' %>" class="btn btn-success" style="float: right">Import Data</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<h3 class="float-left">CSV Import</h3>
|
||||
<a href="<%= url_for . '/add' %>" class="btn btn-success float-right">Import Data</a>
|
||||
</div>
|
||||
% for my $import ( $import_rs->all ) {
|
||||
% my $total = $import_rs->get_values( $import->id, 1, 1 )->count;
|
||||
% my $unimported = $import_rs->get_values( $import->id, undef, undef )->count;
|
||||
% my $with_ignored = $import_rs->get_values( $import->id, 1, undef )->count;
|
||||
% my $with_imported = $import_rs->get_values( $import->id, undef, 1 )->count;
|
||||
% my $ignored_total = $with_ignored - $unimported;
|
||||
% my $imported_total = $with_imported - $unimported;
|
||||
<div class="col col-md-4 mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="font-bold"><%= $import->id %></span>
|
||||
%= format_human_datetime $import->date;
|
||||
<div class="card-header text-white <%= $unimported ? 'bg-danger' : 'bg-success' %>">
|
||||
<span><%= format_human_datetime $import->date %></span>
|
||||
<span class=" float-right font-bold"><%= $import->id %></span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span>Unimported: <%= $unimported %></span>
|
||||
<br>
|
||||
<span>Ignored: <%= $ignored_total %></span>
|
||||
<br>
|
||||
<span>Imported: <%= $imported_total %></span>
|
||||
<br>
|
||||
<span>Total: <%= $total %></span>
|
||||
</div>
|
||||
<div class="card-footer text-right">
|
||||
<a href="<%= url_for . '/' . $import->id %>" class="card-link">
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
%= $user->user_name
|
||||
</div>
|
||||
<div class="col-4">
|
||||
% if ( my $lookup = $import_lookup_rs->find({ name => $user->user_name }) ) {
|
||||
<span class="text-muted"><%= $lookup->entity->name %></span>
|
||||
% if ( defined $import_lookup_rs->{ $user->user_name } ) {
|
||||
<span class="text-muted"><%= $import_lookup_rs->{ $user->user_name }->{name} %></span>
|
||||
% } else {
|
||||
<span class="text-muted font-italic">Unassigned</span>
|
||||
% }
|
||||
|
@ -59,10 +59,80 @@
|
|||
%= $org->org_name
|
||||
</div>
|
||||
<div class="col-4">
|
||||
% if ( defined $import_lookup_rs->{ $org->org_name } ) {
|
||||
<span class="text-muted"><%= $import_lookup_rs->{ $org->org_name }->{name} %></span>
|
||||
% } else {
|
||||
<span class="text-muted font-italic">Unassigned</span>
|
||||
% }
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a class="btn btn-primary">Select</a>
|
||||
<a href="<%= url_for(url_for . '/org')->query([ org => $org->org_name ]) %>" class="btn btn-primary">Select</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<div class="card">
|
||||
<h3 class="card-header">
|
||||
%= format_human_datetime $import_set->date;
|
||||
<a href="<%= url_for->query({ignored => $c->param('ignored') ? 0 : 1 }) %>"
|
||||
class="btn btn-primary float-right">
|
||||
<%= $c->param('ignored') ? 'Hide' : 'Show' %> Ignored
|
||||
</a>
|
||||
<a href="<%= url_for->query({imported => $c->param('imported') ? 0 : 1 }) %>"
|
||||
class="btn btn-secondary float-right">
|
||||
<%= $c->param('imported') ? 'Hide' : 'Show' %> Imported
|
||||
</a>
|
||||
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
Content listed in original order of import
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
% for my $import_value ( $import_value_rs->all ) {
|
||||
% my $user_lookup = $import_lookup_rs->{ $import_value->user_name };
|
||||
% my $purchase_lookup = parse_currency $import_value->purchase_value;
|
||||
% my $org_lookup = $import_lookup_rs->{ $import_value->org_name };
|
||||
<div class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<%= $import_value->user_name %>
|
||||
% if ( defined $user_lookup ) {
|
||||
<br>
|
||||
<span class="text-muted"><%= $user_lookup->{name} %></span>
|
||||
% }
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<%= format_human_datetime $import_value->purchase_date %>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<%= $import_value->purchase_value %>
|
||||
<br>
|
||||
<span class="text-muted"><%= $purchase_lookup %></span>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<%= $import_value->org_name %>
|
||||
% if ( defined $org_lookup ) {
|
||||
<br>
|
||||
<span class="text-muted"><%= $org_lookup->{name} %></span>
|
||||
% }
|
||||
</div>
|
||||
<div class="col-2">
|
||||
% if ( defined $import_value->transaction_id ) {
|
||||
<button class="btn btn-primary">Imported</button>
|
||||
% } else {
|
||||
% if ( defined $user_lookup && defined $org_lookup && $purchase_lookup ) {
|
||||
<button class="btn btn-success">Ready</button>
|
||||
% }
|
||||
% if ( $import_value->ignore_value ) {
|
||||
<a href="<%= url_for . '/ignore/' . $import_value->id %>" class="btn btn-success">Un Ignore</a>
|
||||
% } else {
|
||||
<a href="<%= url_for . '/ignore/' . $import_value->id %>" class="btn btn-danger">Ignore</a>
|
||||
% }
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,36 +141,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="card-header">
|
||||
%= format_human_datetime $import_set->date;
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
Content listed in original order of import
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
% for my $import_value ( $import_value_rs->all ) {
|
||||
<div class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<%= $import_value->user_name %>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<%= format_human_datetime $import_value->purchase_date %>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<%= $import_value->purchase_value %>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<%= $import_value->org_name %>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<a href="#" class="btn btn-danger">Ignore</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
<a href="<%= url_for . '/import' %>" class="btn btn-info float-right">Import Ready Items</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -28,12 +28,15 @@
|
|||
% if ( $org_result->pending ) {
|
||||
<span class="badge badge-warning">Unvalidated</span>
|
||||
% }
|
||||
% if ( defined $org_result->user ) {
|
||||
<span class="badge badge-info">User</span>
|
||||
% }
|
||||
% if ( !defined $org_result->is_local ) {
|
||||
<span class="badge badge-danger">Locality Not Set</span>
|
||||
% } elsif ( $org_result->is_local ) {
|
||||
<span class="badge badge-success">Local Org</span>
|
||||
% } else {
|
||||
<span class="badge badge-warning">Non Local Org</span>
|
||||
<span class="badge badge-secondary">Non Local Org</span>
|
||||
% }
|
||||
</div>
|
||||
</a>
|
||||
|
|
81
templates/admin/organisations/merge_detail.html.ep
Normal file
81
templates/admin/organisations/merge_detail.html.ep
Normal file
|
@ -0,0 +1,81 @@
|
|||
% layout 'admin_errors';
|
||||
% title 'Organisations';
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3 class="float-left">Merging <%= $org_result->name %> into <%= $target_result->name %></h3>
|
||||
<a href="<%= url_for '/admin/organisations/' . $org_result->id . '/merge' %>" class="btn btn-success float-right">Back</a>
|
||||
</div>
|
||||
% for my $org ( $org_result, $target_result ) {
|
||||
<div class="col-6">
|
||||
<div class="card">
|
||||
<h3 class="card-header">
|
||||
<%= $org->name %>
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
Street Name
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->street_name
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Town/City
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->town
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Sector
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->sector
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Postcode
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->postcode
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Validated
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->pending ? 'no' : 'yes'
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Is Local
|
||||
</div>
|
||||
<div class="col-6">
|
||||
%= $org->is_local ? 'yes' : 'no'
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item">
|
||||
Transaction Count: <%= $org->entity->sales->count %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">
|
||||
Warning: Cannot be undone!
|
||||
</h1>
|
||||
<p>
|
||||
This will discard all basic information about this organisation, and
|
||||
merge all transactions into the target organisation. This process has
|
||||
no way of being undone.
|
||||
</p>
|
||||
<form action="<%= url_for %>" method="POST">
|
||||
<input type="checkbox" name="confirm" value="checked">
|
||||
<label>I confirm that I want this to happen</label>
|
||||
<button type="submit" class="btn btn-danger">Confirm Merge</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
25
templates/admin/organisations/merge_list.html.ep
Normal file
25
templates/admin/organisations/merge_list.html.ep
Normal file
|
@ -0,0 +1,25 @@
|
|||
% layout 'admin_errors';
|
||||
% title 'Organisations';
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3>Target to merge into for <%= $org_result->name %></h3>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h3 class="card-header">
|
||||
Organisations
|
||||
<a href="<%= url_for '/admin/organisations/' . $org_result->id %>" class="btn btn-success float-right">Back</a>
|
||||
</h3>
|
||||
<div class="list-group list-group-flush">
|
||||
% for my $org ( $org_rs->all ) {
|
||||
<a href="<%= url_for . '/' . $org->id %>" class="list-group-item list-group-item-action">
|
||||
%= $org->name
|
||||
</a>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
%= bootstrap_pagination( $c->param('page') || 1, $org_rs->pager->last_page, { class => 'justify-content-center' } );
|
||||
</div>
|
||||
</div>
|
|
@ -41,19 +41,34 @@ function initMap() {
|
|||
<div class="form-group row">
|
||||
<label for="name" class="col-md-4 col-form-label">Organisation Name</label>
|
||||
<div class="col-md-8">
|
||||
<input id="name" type="text" class="form-control" placeholder="Organisation Name" name="name" value="<%= $valid_org->name %>">
|
||||
<input id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Organisation Name"
|
||||
name="name"
|
||||
value="<%= $valid_org->name %>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-md-4 col-form-label">Street Name</label>
|
||||
<div class="col-md-8">
|
||||
<input id="street_name" type="text" class="form-control" placeholder="Street Name" name="street_name" value="<%= $valid_org->street_name %>">
|
||||
<input id="street_name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Street Name"
|
||||
name="street_name"
|
||||
value="<%= $valid_org->street_name %>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="name" class="col-md-4 col-form-label">Town/City</label>
|
||||
<div class="col-md-8">
|
||||
<input id="town" type="text" class="form-control" placeholder="Town" name="town" value="<%= $valid_org->town %>">
|
||||
<input id="town"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Town"
|
||||
name="town"
|
||||
value="<%= $valid_org->town %>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
@ -67,13 +82,21 @@ function initMap() {
|
|||
<div class="form-group row">
|
||||
<label for="postcode" class="col-md-4 col-form-label">Postcode</label>
|
||||
<div class="col-md-8">
|
||||
<input id="postcode" type="text" class="form-control" placeholder="Postcode" name="postcode" value="<%= $valid_org->postcode %>">
|
||||
<input id="postcode"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Postcode"
|
||||
name="postcode"
|
||||
value="<%= $valid_org->postcode %>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="pending" class="col-md-4 col-form-label">Validated</label>
|
||||
<div class="col-md-8">
|
||||
<input id="pending" type="checkbox" name="pending" value="0"<%= $valid_org->pending ? '' : ' checked' %>>
|
||||
<input id="pending"
|
||||
type="checkbox"
|
||||
name="pending"
|
||||
value="0"<%= $valid_org->pending ? '' : ' checked' %>>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
@ -92,6 +115,9 @@ function initMap() {
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="<%= url_for . '/merge' %>" class="btn btn-warning">Merge Org</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
|
@ -117,9 +143,9 @@ function initMap() {
|
|||
<div class="row text-center">
|
||||
<div class="col">From: <%= $transaction->buyer->name %></div>
|
||||
<div class="col">To: <%= $transaction->seller->name %></div>
|
||||
<div class="col">Value: <%= $transaction->value %></div>
|
||||
<div class="col">Submitted At: <%= $transaction->submitted_at %></div>
|
||||
<div class="col">Purchase Time: <%= $transaction->purchase_time %></div>
|
||||
<div class="col">Value: <%= format_currency_from_db $transaction->value %></div>
|
||||
<div class="col">Submitted At: <%= format_human_datetime $transaction->submitted_at %></div>
|
||||
<div class="col">Purchase Time: <%= format_human_datetime $transaction->purchase_time %></div>
|
||||
</div>
|
||||
</a>
|
||||
% }
|
||||
|
|
|
@ -12,11 +12,30 @@
|
|||
</div>
|
||||
% }
|
||||
<div class="list-group">
|
||||
% for my $user (@$users) {
|
||||
<a href="<%= url_for . '/' . $user->{id} %>" class="list-group-item list-group-item-action">
|
||||
<div>
|
||||
%= $user->{email}
|
||||
% for my $user ($user_rs->all) {
|
||||
<a href="<%= url_for . '/' . $user->id %>" class="list-group-item list-group-item-action">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
%= $user->name
|
||||
</div>
|
||||
<div class="col-4 text-center">
|
||||
%= $user->email
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
% if ( $user->type eq 'customer' ) {
|
||||
<span class="badge badge-success">Customer</span>
|
||||
% } elsif ( $user->type eq 'organisation' ) {
|
||||
<span class="badge badge-info">Organisation</span>
|
||||
% } else {
|
||||
<span class="badge badge-danger">Unknown</span>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
% }
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
%= bootstrap_pagination( $c->param('page') || 1, $user_rs->pager->last_page, { class => 'justify-content-center' } );
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,13 +60,13 @@
|
|||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
% if ( my $error = flash 'error' ) {
|
||||
% if ( my $f_error = flash 'error' ) {
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error!</strong> <%= $error %>
|
||||
<strong>Error!</strong> <%= $f_error %>
|
||||
</div>
|
||||
% } elsif ( my $error = stash 'error' ) {
|
||||
% } elsif ( my $s_error = stash 'error' ) {
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error!</strong> <%= $error %>
|
||||
<strong>Error!</strong> <%= $s_error %>
|
||||
</div>
|
||||
% } elsif ( my $success = flash 'success' ) {
|
||||
<div class="alert alert-success" role="alert">
|
||||
|
|
Reference in a new issue