Using placeholder in SortableTable while data is loading.

This commit is contained in:
Roland 2023-09-12 22:03:51 -06:00
parent 095634a4ea
commit 12850bdb8b
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
5 changed files with 33 additions and 35 deletions

View file

@ -228,7 +228,7 @@ function EditTagsModal() {
{key: 'delete', text: 'Delete', sortBy: null, width: 2},
{key: 'edit', text: 'Edit', sortBy: null, width: 2},
{key: 'name', text: 'Name', sortBy: 'name', width: 8},
{key: 'count', text: 'Count', sortBy: 'file_group_count', width: 2},
{key: 'count', text: 'Count', sortBy: i => i['file_group_count'] + i['zim_entry_count'], width: 2},
];
return <>
@ -267,6 +267,7 @@ function EditTagsModal() {
<Divider/>
<SortableTable
tableProps={{unstackable: true}}
data={tags}
rowFunc={(i, sortData) => <EditTagRow key={i['name']} tag={i} onDelete={localDeleteTag}
onEdit={localEditTag}/>}

View file

@ -561,23 +561,6 @@ export function ChannelsPages() {
<Message.Content><Link to='/videos/channel/new'>Create one.</Link></Message.Content>
</Message>
</>
} else if (channels === null) {
// Placeholders while fetching
return <>
{header}
<Table compact striped basic sortable size='large'>
<TableHeader>
<TableRow>
{headers.map(i => <TableHeaderCell key={i['key']}>{i['text']}</TableHeaderCell>)}
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell colSpan={5}><ChannelPlaceholder/></TableCell>
</TableRow>
</TableBody>
</Table>
</>
} else if (channels === undefined) {
return <>
{header}
@ -590,6 +573,7 @@ export function ChannelsPages() {
const re = new RegExp(_.escapeRegExp(searchStr), 'i');
filteredChannels = channels.filter(i => re.test(i['name']));
}
console.log(`filteredChannels=${filteredChannels === null}`);
return <>
{header}

View file

@ -1,7 +1,8 @@
import React from "react";
import _ from "lodash";
import {TableBody, TableHeader, TableHeaderCell, TableRow} from "semantic-ui-react";
import {TableBody, TableCell, TableHeader, TableHeaderCell, TableRow} from "semantic-ui-react";
import {Table} from "./Theme";
import {TextPlaceholder} from "./Placeholder";
export class SortableTable extends React.Component {
constructor(props) {
@ -41,7 +42,7 @@ export class SortableTable extends React.Component {
let {direction} = this.state;
rowKey = rowKey || 'key';
const data = this.sortData(this.props['data']);
const data = this.props['data'] ? this.sortData(this.props['data']) : null;
const tableHeader = (spec) => {
const {key, text, sortBy, width} = spec;
@ -59,7 +60,11 @@ export class SortableTable extends React.Component {
}
}
const rows = data.map(i => <React.Fragment key={i[rowKey]}>{rowFunc(i, this.sortData)}</React.Fragment>);
// Use placeholder while data is null.
let rows = <TableRow><TableCell colSpan={tableHeaders.length}><TextPlaceholder/></TableCell></TableRow>;
if (data !== null) {
rows = data.map(i => <React.Fragment key={i[rowKey]}>{rowFunc(i, this.sortData)}</React.Fragment>);
}
return <Table sortable {...this.props.tableProps}>
<TableHeader>

View file

@ -97,7 +97,7 @@ class Tag(ModelHelper, Base):
def has_relations(self) -> bool:
"""Returns True if this Tag has been used with any FileGroups or Zim Entries."""
return not self.tag_files and not self.tag_zim_entries
return bool(self.tag_files or self.tag_zim_entries)
class TagsConfig(ConfigFile):
@ -308,6 +308,7 @@ def import_tags_config(session: Session = None):
# Maintainer added a Tag to the config manually, or DB was wiped.
tag = Tag(name=name, color=attrs['color'])
new_tags.append(tag)
logger.info(f'Creating new {tag}')
tag.color = attrs['color']
if new_tags:
@ -391,6 +392,7 @@ def import_tags_config(session: Session = None):
elif not tag:
logger.warning(f'Cannot find Tag for {repr(str(zim_path))}')
# Delete missing Tags last in case they are used above.
if config.tags:
config_tag_names = set(config.tags.keys())
for tag in session.query(Tag):

View file

@ -141,7 +141,8 @@ def test_tags_crud(test_session, test_client, example_pdf, assert_tags_config):
request, response = test_client.post('/api/tag/1', content=json.dumps(content))
assert response.status_code == HTTPStatus.OK
request, response = test_client.get('/api/tag')
assert response.json['tags'] == [dict(name='ガーデン', color='#000000', id=1, file_group_count=1, zim_entry_count=0)]
assert response.json['tags'] == [
dict(name='ガーデン', color='#000000', id=1, file_group_count=1, zim_entry_count=0)]
assert_tags_config(tags={'ガーデン': {'color': '#000000'}})
# Conflicting names return an error.
@ -250,24 +251,29 @@ async def test_import_tags_delete_missing(test_session, test_directory, test_tag
assert tag3.name in test_tags_config.read_text()
assert tag4.name in test_tags_config.read_text()
# Delete tag1 and tag2 from the config.
# Delete all tags but tag3.
config = tags.get_tags_config()
config_dict = config.dict()
config_dict['tags'] = {k: v for k, v in config.tags.items() if k not in [tag1.name, tag2.name, tag4.name]}
config_dict['tags'] = {k: v for k, v in config.tags.items() if k == tag3.name}
config.update(config_dict)
config.initialize()
# Tag Files and Tag Zims are untouched. Only tag3 is left.
assert len(config.tag_files) == 1
assert len(config.tag_zims) == 1
assert len(config.tags) == 1
# Import the config. Only unused Tags not in the config are deleted.
tags.import_tags_config()
assert {
tag1.name, # tag1 is used by FileGroup.
# tag2.name, tag2 is unused, and deleted.
tag3.name, # tag3 was not deleted.
tag4.name, # tag4 is used by ZimEntry.
} == {i.name for i in test_session.query(tags.Tag)}
assert {i.name for i in test_session.query(tags.Tag)} == {
tag1.name, # tag1 is used by FileGroup.
# tag2.name, tag2 is unused, and deleted.
tag3.name, # tag3 was not deleted.
tag4.name, # tag4 is used by ZimEntry.
}
# DB is written to config with only those Tags left.
tags.schedule_save()
assert f'{tag1.name}:' in test_tags_config.read_text()
assert f'{tag2.name}:' not in test_tags_config.read_text()
assert f'{tag3.name}:' in test_tags_config.read_text()
assert f'{tag4.name}:' in test_tags_config.read_text()
# Tags that are used are restored.
assert len(config.tag_files) == 1
assert len(config.tag_zims) == 1
assert len(config.tags) == 3