diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 094f5ed..6a2e948 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -523,8 +523,8 @@ class SimpleStorageSpaceSerializer(serializers.ModelSerializer): class StorageSpaceSerializer(serializers.ModelSerializer): - member = serializers.SerializerMethodField() - member_id = serializers.IntegerField(write_only=True, required=False) + member_id = serializers.SerializerMethodField() + member_name = serializers.SerializerMethodField() class Meta: model = models.StorageSpace @@ -545,12 +545,15 @@ class StorageSpaceSerializer(serializers.ModelSerializer): return super().update(instance, validated_data) - def get_member(self, obj): - if obj.user: - serializer = OtherMemberSerializer(obj.user.member) - return serializer.data - else: - return None + def get_member_id(self, obj): + if not obj.user: return None + return obj.user.member.id + + def get_member_name(self, obj): + if not obj.user: return None + + member = obj.user.member + return member.preferred_name + ' ' + member.last_name class TrainingSerializer(serializers.ModelSerializer): diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index c80688a..47280d7 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -1787,7 +1787,7 @@ class HostingViewSet(Base): class StorageSpaceViewSet(Base, List, Retrieve, Update): permission_classes = [AllowMetadata | IsAdmin] - queryset = models.StorageSpace.objects.all() + queryset = models.StorageSpace.objects.all().order_by('id') serializer_class = serializers.StorageSpaceSerializer diff --git a/webclient/src/App.js b/webclient/src/App.js index 1d6d8ac..5126301 100644 --- a/webclient/src/App.js +++ b/webclient/src/App.js @@ -27,6 +27,7 @@ import { Subscribe } from './PayPal.js'; import { PasswordReset, ConfirmReset } from './PasswordReset.js'; import { NotFound, PleaseLogin } from './Misc.js'; import { Debug } from './Debug.js'; +import { StorageDetail } from './Storage.js'; import { Garden } from './Garden.js'; import { Footer } from './Footer.js'; import { LCARS1Display, LCARS2Display } from './Display.js'; @@ -239,6 +240,10 @@ function App() {
+ + + + diff --git a/webclient/src/Debug.js b/webclient/src/Debug.js index 350719f..ac74ff6 100644 --- a/webclient/src/Debug.js +++ b/webclient/src/Debug.js @@ -1,9 +1,14 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; +import React, { useState, useEffect } from 'react'; +import { Link, useParams, useHistory } from 'react-router-dom'; import './light.css'; -import { Button, Container, Header } from 'semantic-ui-react'; +import { MembersDropdown } from './Members.js'; +import { StorageList } from './Storage.js'; +import { isAdmin, BasicTable, requester } from './utils.js'; +import { Button, Container, Form, Grid, Header, Message, Segment, Table } from 'semantic-ui-react'; export function Debug(props) { + const { token } = props; + return (
Debug
@@ -30,6 +35,10 @@ export function Debug(props) {

LCARS2 Display

+
Storage
+ + +
); diff --git a/webclient/src/Storage.js b/webclient/src/Storage.js new file mode 100644 index 0000000..cde6733 --- /dev/null +++ b/webclient/src/Storage.js @@ -0,0 +1,242 @@ +import React, { useState, useEffect } from 'react'; +import { Link, useParams, useHistory } from 'react-router-dom'; +import './light.css'; +import { MembersDropdown } from './Members.js'; +import { isAdmin, BasicTable, requester } from './utils.js'; +import { Button, Container, Form, Grid, Header, Message, Segment, Table } from 'semantic-ui-react'; + +export function StorageEditor(props) { + const { token, input, setInput, error } = props; + + const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); + const handleChange = (e) => handleValues(e, e.currentTarget); + + const makeProps = (name) => ({ + name: name, + onChange: handleChange, + value: input[name] || '', + error: error[name], + }); + + const locationOptions = [ + { key: '0', text: 'Member Shelves', value: 'member_shelves' }, + { key: '1', text: 'Lockers', value: 'lockers' }, + { key: '2', text: 'Large Project Storage', value: 'large_project_storage' }, + ]; + + return ( +
+ + + + + + + + +
+ ); +}; + +function EditStorage(props) { + const { storage, setStorage, token, refreshUser } = props; + const [input, setInput] = useState(storage); + const [error, setError] = useState(false); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + const { id } = useParams(); + const history = useHistory(); + + const handleSubmit = (e) => { + if (loading) return; + setLoading(true); + setSuccess(false); + const data = { ...input }; + return requester('/storage/'+id+'/', 'PUT', token, data) + .then(res => { + setLoading(false); + setSuccess(true); + setError(false); + setInput(res); + setStorage(res); + }) + .catch(err => { + setLoading(false); + console.log(err); + setError(err.data); + }); + }; + + const saveAndNext = (e) => { + e.preventDefault(); + + handleSubmit(e) + .then(res => { + setStorage(false); + history.push('/storage/' + (parseInt(id) + 1)); + }); + }; + + return ( +
+
Edit Storage
+ +
+ + + + + Save + + + + Save and edit next + + + {success &&
Success!
} + +
+ ); +}; + +function StorageTable(props) { + const { storage, user } = props; + + return ( + + + + Shelf ID: + {storage.shelf_id} + + + Owner: + {isAdmin(user) && storage.member_id ? + + + {storage.member_name} + + + : + {storage.member_name} + } + + + Location: + {storage.location} + + + Memo: + {storage.memo} + + + + ); +} + +export function StorageDetail(props) { + const { token, user } = props; + const [storage, setStorage] = useState(false); + const [error, setError] = useState(false); + const { id } = useParams(); + + useEffect(() => { + requester('/storage/' + id + '/', 'GET', token) + .then(res => { + setStorage(res); + setError(false); + }) + .catch(err => { + console.log(err); + setError(true); + }); + }, [id]); + + return ( + + {!error ? + storage ? +
+
Storage Location
+ + + + + + + + {isAdmin(user) ? + + + + : + +
Report Storage
+ +

If there's anything wrong with this storage location please email the Protospace Directors:

+

directors@protospace.ca

+

Please include a link to this storage location and any relevant details.

+
+ } +
+
+ +
+ : +

Loading...

+ : +

Error loading.

+ } +
+ ); +}; + +export function StorageList(props) { + const { token } = props; + const [storageList, setStorageList] = useState(false); + const [error, setError] = useState(false); + + useEffect(() => { + requester('/storage/', 'GET', token) + .then(res => { + setStorageList(res.results); + setError(false); + }) + .catch(err => { + console.log(err); + setError(true); + }); + }, []); + + return ( +
+ {!error ? + storageList ? + storageList.map(x => +

{x.shelf_id} - {!!x.member_id && + {x.member_name} + }

+ ) + : +

Loading...

+ : +

Error loading.

+ } +
+ ); +};