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 (
@@ -30,6 +35,10 @@ export function Debug(props) {
LCARS2 Display
+
+
+
+
);
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 (
+
+
+
+
+
+ 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 ?
+
+
+
+
+
+
+
+
+
+ {isAdmin(user) ?
+
+
+
+ :
+
+
+
+ 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.
+ }
+
+ );
+};