Allow sorting by Last Scanned

This commit is contained in:
Tanner Collin 2021-11-12 04:16:19 +00:00
parent 66784c457a
commit e511723c07
5 changed files with 60 additions and 39 deletions

View File

@ -59,6 +59,7 @@ class Member(models.Model):
monthly_fees = models.IntegerField(default=55, blank=True, null=True) monthly_fees = models.IntegerField(default=55, blank=True, null=True)
is_allowed_entry = models.BooleanField(default=True) is_allowed_entry = models.BooleanField(default=True)
discourse_username = models.CharField(default='', max_length=40, blank=True, null=True) discourse_username = models.CharField(default='', max_length=40, blank=True, null=True)
allow_last_scanned = models.BooleanField(default=True)
history = HistoricalRecords(excluded_fields=['member_forms']) history = HistoricalRecords(excluded_fields=['member_forms'])

View File

@ -119,6 +119,12 @@ class SearchViewSet(Base, Retrieve):
elif self.action == 'create' and sort == 'overdue': elif self.action == 'create' and sort == 'overdue':
queryset = queryset.filter(status='Overdue') queryset = queryset.filter(status='Overdue')
queryset = queryset.order_by('expire_date') queryset = queryset.order_by('expire_date')
elif self.action == 'create' and sort == 'last_scanned':
if self.request.user.member.allow_last_scanned:
queryset = queryset.filter(allow_last_scanned=True)
queryset = queryset.order_by('-user__cards__last_seen_at')
else:
queryset = []
elif self.action == 'create' and sort == 'best_looking': elif self.action == 'create' and sort == 'best_looking':
queryset = [] queryset = []

View File

@ -264,6 +264,16 @@ export function AccountForm(props) {
{...makeProps('discourse_username')} {...makeProps('discourse_username')}
/>} />}
<Form.Field>
<label>Participate in "Last Scanned" member list?</label>
<Checkbox
label='Yes, show me'
name='allow_last_scanned'
onChange={handleCheck}
checked={input.allow_last_scanned}
/>
</Form.Field>
<Form.Input <Form.Input
label='Member Photo' label='Member Photo'
name='photo' name='photo'

View File

@ -274,7 +274,7 @@ function App() {
<MemberDetail token={token} user={user} /> <MemberDetail token={token} user={user} />
</Route> </Route>
<Route path='/members'> <Route path='/members'>
<Members token={token} /> <Members token={token} user={user} />
</Route> </Route>
{user && isAdmin(user) && {user && isAdmin(user) &&

View File

@ -11,6 +11,7 @@ import AbortController from 'abort-controller';
const memberSorts = { const memberSorts = {
recently_vetted: 'Recently Vetted', recently_vetted: 'Recently Vetted',
last_scanned: 'Last Scanned',
newest_active: 'Newest', newest_active: 'Newest',
//newest_overall: 'Newest Overall', //newest_overall: 'Newest Overall',
oldest_active: 'Oldest', oldest_active: 'Oldest',
@ -76,7 +77,7 @@ export function Members(props) {
const [response, setResponse] = useState(false); const [response, setResponse] = useState(false);
const [numShow, setNumShow] = useState(numShowCache); const [numShow, setNumShow] = useState(numShowCache);
const [controller, setController] = useState(false); const [controller, setController] = useState(false);
const { token } = props; const { token, user } = props;
const doSearch = (q) => { const doSearch = (q) => {
console.log('doing search', q); console.log('doing search', q);
@ -153,44 +154,47 @@ export function Members(props) {
{search.length ? 'Search Results' : memberSorts[sort]} {search.length ? 'Search Results' : memberSorts[sort]}
</Header> </Header>
{sort === 'best_looking' ? {sort === 'last_scanned' &&
<center> (user.member.allow_last_scanned ?
<img className='bean' src='/mr-bean.jpg' /> <p>Hide yourself from this list on the <Link to='/account'>Account Settings</Link> page.</p>
</center>
:
response ?
<>
<Item.Group unstackable divided>
{response.results.length ?
response.results.slice(0, numShow).map((x, i) =>
<Item key={x.member.id} as={Link} to={'/members/'+x.member.id}>
<div className='list-num'>{i+1}</div>
<Item.Image size='tiny' src={x.member.photo_small ? staticUrl + '/' + x.member.photo_small : '/nophoto.png'} />
<Item.Content verticalAlign='top'>
<Item.Header>
<Icon name='circle' color={statusColor[x.member.status]} />
{x.member.preferred_name} {x.member.last_name}
</Item.Header>
<Item.Description>Status: {x.member.status || 'Unknown'}</Item.Description>
<Item.Description>Joined: {x.member.application_date || 'Unknown'}</Item.Description>
<Item.Description>ID: {x.member.id}</Item.Description>
</Item.Content>
</Item>
)
:
<p>No Results</p>
}
</Item.Group>
{response.results.length > 20 && numShow !== 100 ?
<Button
content='Load More'
onClick={() => {setNumShow(100); numShowCache = 100;}}
/> : ''
}
</>
: :
<p>Loading...</p> <p>Participate in this list on the <Link to='/account'>Account Settings</Link> page.</p>
)
}
{response ?
<>
<Item.Group unstackable divided>
{response.results.length ?
response.results.slice(0, numShow).map((x, i) =>
<Item key={x.member.id} as={Link} to={'/members/'+x.member.id}>
<div className='list-num'>{i+1}</div>
<Item.Image size='tiny' src={x.member.photo_small ? staticUrl + '/' + x.member.photo_small : '/nophoto.png'} />
<Item.Content verticalAlign='top'>
<Item.Header>
<Icon name='circle' color={statusColor[x.member.status]} />
{x.member.preferred_name} {x.member.last_name}
</Item.Header>
<Item.Description>Status: {x.member.status || 'Unknown'}</Item.Description>
<Item.Description>Joined: {x.member.application_date || 'Unknown'}</Item.Description>
<Item.Description>ID: {x.member.id}</Item.Description>
</Item.Content>
</Item>
)
:
<p>No Results</p>
}
</Item.Group>
{response.results.length > 20 && numShow !== 100 ?
<Button
content='Load More'
onClick={() => {setNumShow(100); numShowCache = 100;}}
/> : ''
}
</>
:
<p>Loading...</p>
} }
</Container> </Container>