Add address and minor fields to member in API and client
This commit is contained in:
parent
f50a3ca129
commit
bd3146f413
|
@ -7,26 +7,33 @@ from . import old_models
|
||||||
|
|
||||||
class Member(models.Model):
|
class Member(models.Model):
|
||||||
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
|
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
first_name = models.CharField(max_length=32)
|
|
||||||
last_name = models.CharField(max_length=32)
|
|
||||||
|
|
||||||
set_details = models.BooleanField(default=False)
|
|
||||||
is_director = models.BooleanField(default=False)
|
|
||||||
is_instructor = models.BooleanField(default=False)
|
|
||||||
preferred_name = models.CharField(max_length=32, blank=True)
|
|
||||||
status = models.CharField(max_length=32, blank=True)
|
|
||||||
phone = models.CharField(max_length=32, blank=True)
|
|
||||||
expire_date = models.DateField(default=date.today, blank=True, null=True)
|
|
||||||
current_start_date = models.DateField(default=date.today, blank=True, null=True)
|
|
||||||
application_date = models.DateField(default=date.today, blank=True, null=True)
|
|
||||||
vetted_date = models.DateField(blank=True, null=True)
|
|
||||||
monthly_fees = models.IntegerField(default=55, blank=True, null=True)
|
|
||||||
emergency_contact_name = models.CharField(max_length=64, blank=True)
|
|
||||||
emergency_contact_phone = models.CharField(max_length=32, blank=True)
|
|
||||||
photo_large = models.CharField(max_length=64, blank=True, null=True)
|
photo_large = models.CharField(max_length=64, blank=True, null=True)
|
||||||
photo_medium = models.CharField(max_length=64, blank=True, null=True)
|
photo_medium = models.CharField(max_length=64, blank=True, null=True)
|
||||||
photo_small = models.CharField(max_length=64, blank=True, null=True)
|
photo_small = models.CharField(max_length=64, blank=True, null=True)
|
||||||
|
|
||||||
|
set_details = models.BooleanField(default=False)
|
||||||
|
first_name = models.CharField(max_length=32)
|
||||||
|
last_name = models.CharField(max_length=32)
|
||||||
|
preferred_name = models.CharField(max_length=32)
|
||||||
|
phone = models.CharField(max_length=32, null=True)
|
||||||
|
emergency_contact_name = models.CharField(max_length=64, blank=True)
|
||||||
|
emergency_contact_phone = models.CharField(max_length=32, blank=True)
|
||||||
|
birthdate = models.DateField(blank=True, null=True)
|
||||||
|
is_minor = models.BooleanField(default=False)
|
||||||
|
guardian_name = models.CharField(max_length=32, blank=True, null=True)
|
||||||
|
street_address = models.CharField(max_length=32, null=True)
|
||||||
|
city = models.CharField(default='Calgary, AB', max_length=32)
|
||||||
|
postal_code = models.CharField(max_length=16, null=True)
|
||||||
|
|
||||||
|
is_director = models.BooleanField(default=False)
|
||||||
|
is_instructor = models.BooleanField(default=False)
|
||||||
|
status = models.CharField(max_length=32, blank=True, null=True)
|
||||||
|
expire_date = models.DateField(default=date.today, null=True)
|
||||||
|
current_start_date = models.DateField(default=date.today, null=True)
|
||||||
|
application_date = models.DateField(default=date.today, null=True)
|
||||||
|
vetted_date = models.DateField(blank=True, null=True)
|
||||||
|
monthly_fees = models.IntegerField(default=55, blank=True, null=True)
|
||||||
|
|
||||||
class Transaction(models.Model):
|
class Transaction(models.Model):
|
||||||
user = models.ForeignKey(User, related_name='transactions', blank=True, null=True, on_delete=models.SET_NULL)
|
user = models.ForeignKey(User, related_name='transactions', blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
recorder = models.ForeignKey(User, related_name='+', blank=True, null=True, on_delete=models.SET_NULL)
|
recorder = models.ForeignKey(User, related_name='+', blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
|
@ -44,22 +44,6 @@ def process_image(upload):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserTrainingSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = models.Training
|
|
||||||
exclude = ['user']
|
|
||||||
depth = 2
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
|
||||||
training = UserTrainingSerializer(many=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['id', 'username', 'member', 'transactions', 'cards', 'training']
|
|
||||||
depth = 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# member viewing member list or other member
|
# member viewing member list or other member
|
||||||
class OtherMemberSerializer(serializers.ModelSerializer):
|
class OtherMemberSerializer(serializers.ModelSerializer):
|
||||||
q = serializers.CharField(write_only=True, max_length=64)
|
q = serializers.CharField(write_only=True, max_length=64)
|
||||||
|
@ -79,6 +63,11 @@ class UserEmailField(serializers.ModelField):
|
||||||
class MemberSerializer(serializers.ModelSerializer):
|
class MemberSerializer(serializers.ModelSerializer):
|
||||||
photo = serializers.ImageField(write_only=True, required=False)
|
photo = serializers.ImageField(write_only=True, required=False)
|
||||||
email = UserEmailField(serializers.EmailField)
|
email = UserEmailField(serializers.EmailField)
|
||||||
|
phone = serializers.CharField()
|
||||||
|
street_address = serializers.CharField()
|
||||||
|
city = serializers.CharField()
|
||||||
|
postal_code = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Member
|
model = models.Member
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
@ -126,6 +115,23 @@ class AdminMemberSerializer(MemberSerializer):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UserTrainingSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = models.Training
|
||||||
|
exclude = ['user']
|
||||||
|
depth = 2
|
||||||
|
|
||||||
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
training = UserTrainingSerializer(many=True)
|
||||||
|
member = MemberSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'username', 'member', 'transactions', 'cards', 'training']
|
||||||
|
depth = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionSerializer(serializers.ModelSerializer):
|
class TransactionSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Transaction
|
model = models.Transaction
|
||||||
|
|
|
@ -19,6 +19,12 @@ MEMBER_FIELDS = [
|
||||||
'monthly_fees',
|
'monthly_fees',
|
||||||
'emergency_contact_name',
|
'emergency_contact_name',
|
||||||
'emergency_contact_phone',
|
'emergency_contact_phone',
|
||||||
|
# minor -> is_minor
|
||||||
|
'birthdate',
|
||||||
|
'guardian_name',
|
||||||
|
'street_address',
|
||||||
|
# city, provice -> city
|
||||||
|
'postal_code',
|
||||||
]
|
]
|
||||||
|
|
||||||
TRANSACTION_FIELDS = [
|
TRANSACTION_FIELDS = [
|
||||||
|
@ -83,6 +89,10 @@ for o in old:
|
||||||
for f in MEMBER_FIELDS:
|
for f in MEMBER_FIELDS:
|
||||||
new[f] = o.__dict__.get(f, None)
|
new[f] = o.__dict__.get(f, None)
|
||||||
|
|
||||||
|
if o.city and o.province:
|
||||||
|
new['city'] = '{}, {}'.format(o.city, o.province)
|
||||||
|
new['is_minor'] = o.minor
|
||||||
|
|
||||||
small, medium, large = None, None, None
|
small, medium, large = None, None, None
|
||||||
if str(o.id) in photo_folders:
|
if str(o.id) in photo_folders:
|
||||||
folder = 'old_photos/' + str(o.id)
|
folder = 'old_photos/' + str(o.id)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
|
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
|
||||||
import './light.css';
|
import './light.css';
|
||||||
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
import { Container, Checkbox, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
||||||
import { BasicTable, staticUrl, requester } from './utils.js';
|
import { BasicTable, staticUrl, requester } from './utils.js';
|
||||||
import { LoginForm, SignupForm } from './LoginSignup.js';
|
import { LoginForm, SignupForm } from './LoginSignup.js';
|
||||||
|
|
||||||
|
@ -63,9 +63,13 @@ function ChangePasswordForm(props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function AccountForm(props) {
|
export function AccountForm(props) {
|
||||||
const member = props.user.member;
|
const member = props.user.member;
|
||||||
const [input, setInput] = useState({ ...member });
|
const [input, setInput] = useState({
|
||||||
|
...member,
|
||||||
|
birthdate: member.birthdate || '',
|
||||||
|
set_details: true
|
||||||
|
});
|
||||||
const [error, setError] = useState({});
|
const [error, setError] = useState({});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -73,6 +77,7 @@ function AccountForm(props) {
|
||||||
const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value });
|
const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value });
|
||||||
const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] });
|
const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] });
|
||||||
const handleChange = (e) => handleValues(e, e.currentTarget);
|
const handleChange = (e) => handleValues(e, e.currentTarget);
|
||||||
|
const handleCheck = (e, v) => setInput({ ...input, [v.name]: v.checked });
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
@ -113,10 +118,44 @@ function AccountForm(props) {
|
||||||
{...makeProps('preferred_name')}
|
{...makeProps('preferred_name')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Form.Input
|
||||||
|
label='Email Address'
|
||||||
|
{...makeProps('email')}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Phone Number (999) 555-1234'
|
label='Phone Number (999) 555-1234'
|
||||||
{...makeProps('phone')}
|
{...makeProps('phone')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Form.Input
|
||||||
|
label='Street Address'
|
||||||
|
{...makeProps('street_address')}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='City, Province'
|
||||||
|
{...makeProps('city')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form.Field>
|
||||||
|
<label>Are you under 18 years old?</label>
|
||||||
|
<Checkbox
|
||||||
|
label='I am a minor'
|
||||||
|
{...makeProps('is_minor')}
|
||||||
|
onChange={handleCheck}
|
||||||
|
checked={input.is_minor}
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
|
||||||
|
{input.is_minor && <Form.Input
|
||||||
|
label='Birthdate YYYY-MM-DD'
|
||||||
|
{...makeProps('birthdate')}
|
||||||
|
/>}
|
||||||
|
{input.is_minor && <Form.Input
|
||||||
|
label="Parent's Name"
|
||||||
|
{...makeProps('guardian_name')}
|
||||||
|
/>}
|
||||||
|
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Emergency Contact Name'
|
label='Emergency Contact Name'
|
||||||
{...makeProps('emergency_contact_name')}
|
{...makeProps('emergency_contact_name')}
|
||||||
|
|
|
@ -114,6 +114,8 @@ function App() {
|
||||||
content='Logout'
|
content='Logout'
|
||||||
onClick={logout}
|
onClick={logout}
|
||||||
icon='cancel'
|
icon='cancel'
|
||||||
|
as={Link}
|
||||||
|
to='/'
|
||||||
/>
|
/>
|
||||||
<Menu.Item fitted content='' />
|
<Menu.Item fitted content='' />
|
||||||
</Menu.Menu>}
|
</Menu.Menu>}
|
||||||
|
@ -125,7 +127,7 @@ function App() {
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<div className='topPadding'>
|
<div className='topPadding'>
|
||||||
{user ?
|
{user && user.member.set_details ?
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path='/account'>
|
<Route path='/account'>
|
||||||
<Account token={token} user={user} setUserCache={setUserCache} />
|
<Account token={token} user={user} setUserCache={setUserCache} />
|
||||||
|
|
|
@ -4,64 +4,7 @@ import './light.css';
|
||||||
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
||||||
import { BasicTable, staticUrl, requester } from './utils.js';
|
import { BasicTable, staticUrl, requester } from './utils.js';
|
||||||
import { LoginForm, SignupForm } from './LoginSignup.js';
|
import { LoginForm, SignupForm } from './LoginSignup.js';
|
||||||
|
import { AccountForm } from './Account.js';
|
||||||
function SignupDetailsForm(props) {
|
|
||||||
const member = props.user.member;
|
|
||||||
const [input, setInput] = useState({ ...member, set_details: true });
|
|
||||||
const [error, setError] = useState({});
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value });
|
|
||||||
const handleChange = (e) => handleValues(e, e.currentTarget);
|
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
|
||||||
setLoading(true);
|
|
||||||
requester('/members/' + member.id + '/', 'PATCH', props.token, input)
|
|
||||||
.then(res => {
|
|
||||||
console.log(res);
|
|
||||||
setError({});
|
|
||||||
props.setUserCache({...props.user, member: res});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
setLoading(false);
|
|
||||||
console.log(err);
|
|
||||||
setError(err.data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeProps = (name) => ({
|
|
||||||
name: name,
|
|
||||||
onChange: handleChange,
|
|
||||||
value: input[name],
|
|
||||||
error: error[name],
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form onSubmit={handleSubmit}>
|
|
||||||
<Header size='medium'>Enter Member Details</Header>
|
|
||||||
<Form.Input
|
|
||||||
label='Preferred First Name'
|
|
||||||
{...makeProps('preferred_name')}
|
|
||||||
/>
|
|
||||||
<Form.Input
|
|
||||||
label='Phone Number (999) 555-1234'
|
|
||||||
{...makeProps('phone')}
|
|
||||||
/>
|
|
||||||
<Form.Input
|
|
||||||
label='Emergency Contact Name'
|
|
||||||
{...makeProps('emergency_contact_name')}
|
|
||||||
/>
|
|
||||||
<Form.Input
|
|
||||||
label='Emergency Contact Phone'
|
|
||||||
{...makeProps('emergency_contact_phone')}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Form.Button loading={loading} error={error.non_field_errors}>
|
|
||||||
Submit
|
|
||||||
</Form.Button>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function MemberInfo(props) {
|
function MemberInfo(props) {
|
||||||
const user = props.user;
|
const user = props.user;
|
||||||
|
@ -162,7 +105,7 @@ export function Home(props) {
|
||||||
user.member.set_details ?
|
user.member.set_details ?
|
||||||
<MemberInfo user={user} />
|
<MemberInfo user={user} />
|
||||||
:
|
:
|
||||||
<SignupDetailsForm token={token} user={user} setUserCache={setUserCache} />
|
<AccountForm token={token} user={user} setUserCache={setUserCache} />
|
||||||
:
|
:
|
||||||
<div>
|
<div>
|
||||||
<LoginForm setTokenCache={setTokenCache} />
|
<LoginForm setTokenCache={setTokenCache} />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user