Skip to content

Commit 08b3cab

Browse files
committed
UX: Improve data table and pagination styles
Signed-off-by: Michael Mayer <michael@photoprism.app>
1 parent 006ed32 commit 08b3cab

File tree

15 files changed

+107
-28
lines changed

15 files changed

+107
-28
lines changed

frontend/package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/component/photo/toolbar.vue

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
>
6969
<v-icon>mdi-view-comfy</v-icon>
7070
</v-btn>
71+
7172
<v-btn
7273
v-else-if="settings.view === 'cards' && listView"
7374
icon
@@ -77,6 +78,7 @@
7778
>
7879
<v-icon>mdi-view-list</v-icon>
7980
</v-btn>
81+
8082
<v-btn
8183
v-else-if="settings.view === 'cards'"
8284
icon
@@ -86,9 +88,11 @@
8688
>
8789
<v-icon>mdi-view-comfy</v-icon>
8890
</v-btn>
91+
8992
<v-btn v-else icon class="action-view-cards" :title="$gettext('Toggle View')" @click.stop="setView('cards')">
9093
<v-icon>mdi-view-column</v-icon>
9194
</v-btn>
95+
9296
<v-btn
9397
v-if="canDelete && context === 'archive' && config.count.archived > 0"
9498
icon
@@ -98,6 +102,7 @@
98102
>
99103
<v-icon>mdi-delete-sweep</v-icon>
100104
</v-btn>
105+
101106
<v-btn
102107
v-else-if="canUpload"
103108
icon
@@ -107,6 +112,7 @@
107112
>
108113
<v-icon>mdi-cloud-upload</v-icon>
109114
</v-btn>
115+
110116
<v-btn
111117
:icon="expanded ? 'mdi-chevron-up' : 'mdi-chevron-down'"
112118
:title="$gettext('Expand Search')"

frontend/src/css/pages.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
align-items: center;
2222
flex-direction: column;
2323
justify-content: center;
24-
padding: 34vh 24px;
24+
padding: 30vh 24px;
2525
}
2626

2727
.p-page-about {

frontend/src/css/vuetify.css

+28-12
Original file line numberDiff line numberDiff line change
@@ -361,22 +361,22 @@ div.v-dialog.v-dialog--fullscreen > div.v-card {
361361
.v-table thead tr th,
362362
.v-table tfoot tr td,
363363
.v-table tbody tr td,
364-
.v-table tr td .v-label,
365-
.v-table tr td .v-field__input {
364+
.v-table .v-table__wrapper tr td .v-label,
365+
.v-table .v-table__wrapper tr td .v-field__input {
366366
font-size: 0.825rem;
367367
}
368368

369369
.v-table.v-table--density-compact thead td,
370370
.v-table.v-table--density-compact thead th,
371371
.v-table.v-table--density-compact tfoot td,
372372
.v-table.v-table--density-compact tbody td,
373-
.v-table.v-table--density-compact tr td .v-label,
374-
.v-table.v-table--density-compact tr td .v-field__input {
373+
.v-table.v-table--density-compact .v-table__wrapper tr td .v-label,
374+
.v-table.v-table--density-compact .v-table__wrapper tr td .v-field__input {
375375
font-size: 0.75rem;
376376
}
377377

378-
.v-table tbody tr td .v-input,
379-
.v-table tbody tr td .v-input .v-field__input {
378+
.v-table .v-table__wrapper tbody tr td .v-input,
379+
.v-table .v-table__wrapper tbody tr td .v-input .v-field__input {
380380
min-height: auto;
381381
padding: 0;
382382
margin: 0;
@@ -439,22 +439,22 @@ div.v-dialog.v-dialog--fullscreen > div.v-card {
439439
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
440440
}
441441

442-
.v-table .v-input.v-select .v-field .v-field__input,
443-
.v-table .v-input.v-text-field .v-field .v-field__input {
442+
.v-table .v-table__wrapper .v-input.v-select .v-field .v-field__input,
443+
.v-table .v-table__wrapper .v-input.v-text-field .v-field .v-field__input {
444444
padding-left: 0;
445445
padding-right: 0;
446446
}
447447

448-
.v-table .v-input.v-select .v-field--appended {
448+
.v-table .v-table__wrapper .v-input.v-select .v-field--appended {
449449
padding-inline-end: 8px;
450450
}
451451

452-
.v-table .v-input.v-select .v-input__append > i,
453-
.v-table .v-input.v-select .v-input__append > .v-icon {
452+
.v-table .v-table__wrapper .v-input.v-select .v-input__append > i,
453+
.v-table .v-table__wrapper .v-input.v-select .v-input__append > .v-icon {
454454
font-size: 1.25em;
455455
}
456456

457-
.v-table .v-input.v-select .v-input__append {
457+
.v-table .v-table__wrapper .v-input.v-select .v-input__append {
458458
margin-inline-start: 0;
459459
}
460460

@@ -471,6 +471,22 @@ div.v-dialog.v-dialog--fullscreen > div.v-card {
471471
border-radius: 6px;
472472
}
473473

474+
.v-table .v-data-table-footer {
475+
padding-top: 6px;
476+
padding-bottom: 6px;
477+
}
478+
479+
.v-table .v-data-table-footer .v-data-table-footer__pagination {
480+
align-items: center;
481+
display: flex;
482+
justify-content: center;
483+
}
484+
485+
.v-table .v-data-table-footer .v-data-table-footer__pagination ul.v-pagination__list > li {
486+
margin-top: 4px;
487+
margin-bottom: 4px;
488+
}
489+
474490
/* Flex Grids */
475491

476492
.v-row {

frontend/src/locales.js

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const Messages = ($gettext) => {
3131
},
3232
dataTable: {
3333
itemsPerPageText: $gettext("Rows per page:"),
34+
itemsPerPageAll: $gettext("All"),
3435
ariaLabel: {
3536
sortDescending: $gettext("Sorted descending."),
3637
sortAscending: $gettext("Sorted ascending."),

frontend/src/model/session.js

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import memoizeOne from "memoize-one";
2929
import $util from "common/util";
3030
import { $gettext, T } from "common/gettext";
3131

32+
export let BatchSize = 99999;
33+
3234
export class Session extends RestModel {
3335
getDefaults() {
3436
return {
@@ -93,6 +95,10 @@ export class Session extends RestModel {
9395
return this.AuthScope;
9496
}
9597

98+
static batchSize() {
99+
return BatchSize;
100+
}
101+
96102
static getCollectionResource() {
97103
return "sessions";
98104
}

frontend/src/model/user.js

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { T, $gettext } from "common/gettext";
3232
import { Form } from "common/form";
3333
import { $config } from "app/session";
3434

35+
export let BatchSize = 99999;
36+
3537
export class User extends RestModel {
3638
getDefaults() {
3739
return {
@@ -347,6 +349,10 @@ export class User extends RestModel {
347349
.then((response) => Promise.resolve(response.data));
348350
}
349351

352+
static batchSize() {
353+
return BatchSize;
354+
}
355+
350356
static getCollectionResource() {
351357
return "users";
352358
}

frontend/src/options/options.js

+7
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ export const Languages = () => [
298298
},
299299
];
300300

301+
export const ItemsPerPage = () => [
302+
{ text: "10", title: "10", value: 10 },
303+
{ text: "20", title: "20", value: 20 },
304+
{ text: "50", title: "50", value: 50 },
305+
{ text: "100", title: "100", value: 100 },
306+
];
307+
301308
export const MapsAnimate = () => [
302309
{
303310
text: $gettext("None"),

frontend/src/page/albums.vue

+4
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@
4747
}
4848
"
4949
></v-text-field>
50+
5051
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
5152
<v-icon>mdi-refresh</v-icon>
5253
</v-btn>
54+
5355
<v-btn
5456
v-if="canUpload"
5557
icon
@@ -59,6 +61,7 @@
5961
>
6062
<v-icon>mdi-cloud-upload</v-icon>
6163
</v-btn>
64+
6265
<v-btn
6366
v-if="canManage && staticFilter.type === 'album'"
6467
icon
@@ -68,6 +71,7 @@
6871
>
6972
<v-icon>mdi-plus</v-icon>
7073
</v-btn>
74+
7175
<v-btn
7276
v-if="canManage && !staticFilter['order']"
7377
:icon="expanded ? 'mdi-chevron-up' : 'mdi-chevron-down'"

internal/entity/search/photos.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func searchPhotos(frm form.SearchPhotos, sess *entity.Session, resultCols string
175175
switch frm.Order {
176176
case sortby.Edited:
177177
s = s.Where("photos.edited_at IS NOT NULL").Order("photos.edited_at DESC, files.media_id")
178-
case sortby.Updated:
178+
case sortby.Updated, sortby.UpdatedAt:
179179
s = s.Where("photos.updated_at > photos.created_at").Order("photos.updated_at DESC, files.media_id")
180180
case sortby.Archived:
181181
s = s.Order("photos.deleted_at DESC, files.media_id")

internal/entity/search/sessions.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,12 @@ func Sessions(frm form.SearchSessions) (result entity.Sessions, err error) {
6262
stmt = stmt.Order("sess_expires DESC, user_name, client_name, id")
6363
case sortby.ClientName:
6464
stmt = stmt.Where("client_name <> '' AND client_name IS NOT NULL").Order("client_name, created_at, id")
65-
case sortby.CreatedAt:
65+
case sortby.Login, sortby.LoginAt:
66+
stmt = stmt.Order("login_at DESC, user_name, client_name, id")
67+
case sortby.Created, sortby.CreatedAt:
6668
stmt = stmt.Order("created_at ASC, user_name, client_name, id")
69+
case sortby.Updated, sortby.UpdatedAt:
70+
stmt = stmt.Order("updated_at DESC, user_name, client_name, id")
6771
default:
6872
return result, fmt.Errorf("invalid sort order %s", order)
6973
}

internal/entity/search/users.go

+21-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"strings"
55

66
"github.com/photoprism/photoprism/internal/entity"
7+
"github.com/photoprism/photoprism/internal/entity/sortby"
78
"github.com/photoprism/photoprism/internal/form"
89
"github.com/photoprism/photoprism/pkg/rnd"
910
"github.com/photoprism/photoprism/pkg/txt"
@@ -14,6 +15,10 @@ func Users(frm form.SearchUsers) (result entity.Users, err error) {
1415
result = entity.Users{}
1516
stmt := Db()
1617

18+
if frm.Deleted {
19+
stmt.Unscoped()
20+
}
21+
1722
search := strings.TrimSpace(frm.Query)
1823
sortOrder := frm.Order
1924
limit := frm.Count
@@ -31,7 +36,22 @@ func Users(frm form.SearchUsers) (result entity.Users, err error) {
3136
stmt = stmt.Where("id > 0")
3237
}
3338

34-
if sortOrder == "" {
39+
switch sortOrder {
40+
case sortby.Name:
41+
sortOrder = "user_name, id"
42+
case sortby.DisplayName:
43+
sortOrder = "display_name, id"
44+
case sortby.Login, sortby.LoginAt:
45+
sortOrder = "login_at DESC, id"
46+
case sortby.Created, sortby.CreatedAt:
47+
sortOrder = "created_at ASC, id"
48+
case sortby.Updated, sortby.UpdatedAt:
49+
sortOrder = "updated_at DESC, id"
50+
case sortby.Deleted, sortby.DeletedAt:
51+
sortOrder = "deleted_at DESC, created_at DESC, id"
52+
case sortby.Email:
53+
sortOrder = "user_email, id"
54+
default:
3555
sortOrder = "user_name, id"
3656
}
3757

internal/entity/sortby/const.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ const (
1212
Imported = "imported"
1313
Archived = "archived"
1414
Edited = "edited"
15+
Created = "created"
16+
CreatedAt = "created_at"
1517
Updated = "updated"
18+
UpdatedAt = "updated_at"
19+
Login = "login"
20+
LoginAt = "login_at"
21+
Deleted = "deleted"
22+
DeletedAt = "deleted_at"
1623
Place = "place"
1724
Moment = "moment"
1825
Favorites = "favorites"
1926
Name = "name"
27+
DisplayName = "display_name"
2028
NameReverse = "name_reverse"
29+
Email = "email"
2130
Title = "title"
2231
Path = "path"
2332
Slug = "slug"
@@ -28,5 +37,4 @@ const (
2837
LastActive = "last_active"
2938
SessExpires = "sess_expires"
3039
ClientName = "client_name"
31-
CreatedAt = "created_at"
3240
)

internal/form/search_sessions.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ type SearchSessions struct {
88
UID string `form:"uid"`
99
Provider string `form:"provider"`
1010
Method string `form:"method"`
11+
Order string `form:"order" serialize:"-"`
1112
Count int `form:"count" binding:"required" serialize:"-"`
1213
Offset int `form:"offset" serialize:"-"`
13-
Order string `form:"order" serialize:"-"`
1414
}
1515

1616
// AuthProviders returns the normalized authentication provider types.

internal/form/search_users.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package form
22

33
// SearchUsers represents a user search form.
44
type SearchUsers struct {
5-
User string `form:"user"`
6-
Query string `form:"q"`
7-
Name string `form:"name"`
8-
Email string `form:"email"`
9-
Count int `form:"count" binding:"required" serialize:"-"`
10-
Offset int `form:"offset" serialize:"-"`
11-
Order string `form:"order" serialize:"-"`
5+
Query string `form:"q"`
6+
User string `form:"user"`
7+
Name string `form:"name"`
8+
Email string `form:"email"`
9+
Deleted bool `form:"deleted"`
10+
Order string `form:"order" serialize:"-"`
11+
Count int `form:"count" binding:"required" serialize:"-"`
12+
Offset int `form:"offset" serialize:"-"`
1213
}
1314

1415
func (f *SearchUsers) GetQuery() string {

0 commit comments

Comments
 (0)