diff --git a/App.js b/App.js
index c2ad025..bc57ba7 100644
--- a/App.js
+++ b/App.js
@@ -4,6 +4,11 @@ import ModuleListScreen from "./src/components/screens/ModuleListScreen";
import ModuleModifyScreen from "./src/components/screens/ModuleModifyScreen";
import ModuleViewScreen from "./src/components/screens/ModuleViewScreen";
import ModuleAddScreen from "./src/components/screens/ModuleAddScreen";
+import UserModifyScreen from "./src/components/screens/UserModifyScreen";
+import UserViewScreen from "./src/components/screens/UserViewScreen";
+import UserAddScreen from "./src/components/screens/UserAddScreen";
+import UserListScreen from "./src/components/screens/UserListScreen";
+
const Stack = createNativeStackNavigator();
@@ -13,46 +18,69 @@ export const App = () => {
// Handlers -------------------
// View -----------------------
return (
-
-
-
-
-
-
-
+
+
+
-
+
-
+
-
+
-
-
-
- );
+ {/*USERS SECTION BELOW*/}
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ ;
};
export default App;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 8b44a2a..5f3c9d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,10 @@
"expo-status-bar": "~3.0.8",
"react": "19.1.0",
"react-native": "^0.81.5",
- "react-native-fullwidth-image": "^0.1.3"
+ "react-native-fullwidth-image": "^0.1.3",
+ "react-native-gesture-handler": "^2.29.1",
+ "react-native-reanimated": "^4.2.0",
+ "react-native-worklets": "^0.7.1"
},
"devDependencies": {
"baseline-browser-mapping": "^2.9.0"
@@ -1309,7 +1312,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
"integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1507,7 +1509,6 @@
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
- "peer": true,
"dependencies": {
"@types/hammerjs": "^2.0.36"
},
@@ -3535,8 +3536,7 @@
"node_modules/@types/hammerjs": {
"version": "2.0.46",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz",
- "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==",
- "peer": true
+ "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw=="
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
@@ -5665,7 +5665,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "peer": true,
"dependencies": {
"react-is": "^16.7.0"
}
@@ -5673,8 +5672,7 @@
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "peer": true
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/hosted-git-info": {
"version": "7.0.2",
@@ -7858,7 +7856,6 @@
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.29.1.tgz",
"integrity": "sha512-du3qmv0e3Sm7qsd9SfmHps+AggLiylcBBQ8ztz7WUtd8ZjKs5V3kekAbi9R2W9bRLSg47Ntp4GGMYZOhikQdZA==",
- "peer": true,
"dependencies": {
"@egjs/hammerjs": "^2.0.17",
"hoist-non-react-statics": "^3.3.0",
@@ -7882,7 +7879,6 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.2.0.tgz",
"integrity": "sha512-frhu5b8/m/VvaMWz48V8RxcsXnE3hrlErQ5chr21MzAeDCpY4X14sQjvm+jvu3aOI+7Cz2atdRpyhhIuqxVaXg==",
- "peer": true,
"dependencies": {
"react-native-is-edge-to-edge": "1.2.1",
"semver": "7.7.3"
@@ -7923,7 +7919,6 @@
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.1.tgz",
"integrity": "sha512-KNsvR48ULg73QhTlmwPbdJLPsWcyBotrGPsrDRDswb5FYpQaJEThUKc2ncXE4UM5dn/ewLoQHjSjLaKUVPxPhA==",
- "peer": true,
"dependencies": {
"@babel/plugin-transform-arrow-functions": "7.27.1",
"@babel/plugin-transform-class-properties": "7.27.1",
@@ -7947,7 +7942,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
@@ -7963,7 +7957,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
"integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-validator-option": "^7.27.1",
diff --git a/package.json b/package.json
index 35739b3..1de3d08 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,10 @@
"expo-status-bar": "~3.0.8",
"react": "19.1.0",
"react-native": "^0.81.5",
- "react-native-fullwidth-image": "^0.1.3"
+ "react-native-fullwidth-image": "^0.1.3",
+ "react-native-gesture-handler": "^2.29.1",
+ "react-native-reanimated": "^4.2.0",
+ "react-native-worklets": "^0.7.1"
},
"private": true,
"devDependencies": {
diff --git a/src/components/entity/users/UserForm.js b/src/components/entity/users/UserForm.js
new file mode 100644
index 0000000..4a6773c
--- /dev/null
+++ b/src/components/entity/users/UserForm.js
@@ -0,0 +1,128 @@
+import { Text } from "react-native";
+import { useState } from "react";
+import Icons from "../../UI/Icons";
+import Form from "../../UI/Form";
+import useLoad from "../../API/useLoad";
+
+const defaultUser = {
+ UserID: null,
+ UserFirstname: null,
+ UserLastname: null,
+ UserEmail: null,
+ UserRegistered: null,
+ UserLevel: null,
+ UserYearID: null,
+ UserUsertypeID: null,
+ UserImageURL: null,
+ UserUsertypeName: null,
+ UserYearName: null,
+};
+
+const UserForm = ({ originalUser, onSubmit, onCancel }) => {
+ // Initialisation -------------
+
+ const yearsEndpoint = 'https://softwarehub.uk/unibase/api/years';
+ const usertypesEndpoint = 'https://softwarehub.uk/unibase/api/usertypes';
+
+ const [years, , isYearsLoading] = useLoad(yearsEndpoint);
+ const [usertypes, , isUsertypesLoading] = useLoad(usertypesEndpoint);
+
+ // State ----------------------
+
+ const [user, setUser] = useState(originalUser || {
+ ...defaultUser,
+ UserID: Math.floor(100000 + Math.random() * 900000),
+ UserImageURL: 'https://images.freeimages.com/images/small-preview/cf5/cellphone-1313194.jpg',
+ UserRegistered: new Date().toISOString()
+ });
+
+ // Handlers -------------------
+
+ const handleChange = (field, value) => {
+ setUser({ ...user, [field]: value });
+ };
+
+ const handleSubmit = () => onSubmit(user);
+
+ // View -----------------------
+
+ const submitLabel = originalUser ? 'Modify' : 'Add';
+ const submitIcon = originalUser ? : ;
+
+ const levels = [
+ { value: '4', label: 'Level 4' },
+ { value: '5', label: 'Level 5' },
+ { value: '6', label: 'Level 6' },
+ { value: '7', label: 'Level 7' }
+ ];
+
+ return (
+
handleChange('UserFirstname', value)}
+ />
+
+ handleChange('UserLastname', value)}
+ />
+
+ handleChange('UserEmail', value)}
+ />
+
+ {isYearsLoading ? (
+ Loading Levels...
+ ) : (
+ handleChange('UserLevel', value)}
+ isLoading={isYearsLoading}
+ />
+ )}
+
+ handleChange('UserYearID', value)}
+ prompt='Select year/cohort...'
+ options={years}
+ isLoading={isYearsLoading}
+ />
+
+ {isUsertypesLoading ? (
+ Loading User Types...
+ ) : (
+ handleChange('UserUsertypeID', value)}
+ prompt='Select user type...'
+ options={usertypes}
+ isLoading={isUsertypesLoading}
+ />
+ )}
+
+ handleChange('UserImageURL', value)}
+ />
+
+ );
+};
+
+export default UserForm;
diff --git a/src/components/entity/users/UserItem.js b/src/components/entity/users/UserItem.js
new file mode 100644
index 0000000..90878df
--- /dev/null
+++ b/src/components/entity/users/UserItem.js
@@ -0,0 +1,32 @@
+import {StyleSheet, Text, View, Pressable} from "react-native";
+
+const UserItem = ({user, onSelect}) => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+ // View -----------------------
+
+ return (
+ onSelect(user)}>
+
+
+ {user.UserCode} {user.UserName}
+
+
+
+ );
+};
+
+
+const styles = StyleSheet.create({
+ usersItem: {
+ paddingVertical: 15,
+ borderBottomWidth: 1,
+ borderBottomColor: "lightgray",
+ },
+ text: {
+ fontSize: 18,
+ },
+});
+
+export default UserItem;
\ No newline at end of file
diff --git a/src/components/entity/users/UserList.js b/src/components/entity/users/UserList.js
new file mode 100644
index 0000000..1bf6d8f
--- /dev/null
+++ b/src/components/entity/users/UserList.js
@@ -0,0 +1,32 @@
+import {StyleSheet, ScrollView} from "react-native";
+import UserItem from "./UserItem";
+import React from "react";
+
+const UserList = ({modules, onSelect}) => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+ // View -----------------------
+
+ return (
+
+ {modules.map((module) => {
+ return (
+
+ )
+ })
+ }
+
+ );
+};
+
+
+const styles = StyleSheet.create({
+ modulesContainer: {},
+});
+
+export default UserList;
\ No newline at end of file
diff --git a/src/components/entity/users/UserView.js b/src/components/entity/users/UserView.js
new file mode 100644
index 0000000..1557d06
--- /dev/null
+++ b/src/components/entity/users/UserView.js
@@ -0,0 +1,74 @@
+import {Alert, StyleSheet, Text, View} from "react-native";
+import FullWidthImage from "react-native-fullwidth-image";
+import {Button, ButtonTray} from "../../UI/Button";
+import Icons from "../../UI/Icons.js"
+
+const UserView = ({module, onDelete, onModify}) => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+
+ const handleDelete = () => onDelete(module);
+
+ const requestDelete = () => {
+ Alert.alert(
+ 'Delete Warning',
+ `Are you sure you want to delete module ${module.ModuleCode} ${module.ModuleName}?`,
+ [
+ {text: 'cancel'},
+ {text: 'Delete', onPress: handleDelete}
+ ]
+ );
+ }
+
+ // View -----------------------
+
+ return (
+
+
+
+ View {module.ModuleCode} {module.ModuleName}
+ Level {module.ModuleLevel}
+ cohort {module.ModuleYearID}
+
+ {module.ModuleLeaderID} (Module Leader)
+
+
+
+
+
+
+ );
+};
+
+
+const styles = StyleSheet.create({
+ container: {
+ gap: 15,
+ }, infoTray: {
+ gap: 5,
+ }, image: {
+ borderRadius: 3,
+ }, text: {
+ fontSize: 16,
+ }, boldText: {
+ fontSize: 16, fontWeight: 'bold',
+ }, dimText: {
+ color: "grey",
+ }
+
+});
+
+export default UserView;
\ No newline at end of file
diff --git a/src/components/screens/UserListScreen.js b/src/components/screens/UserListScreen.js
index fbd9aa7..c536f97 100644
--- a/src/components/screens/UserListScreen.js
+++ b/src/components/screens/UserListScreen.js
@@ -1,10 +1,12 @@
// import {useState, useEffect} from "react";
import {LogBox, Text} from "react-native";
import Screen from "../layout/Screen";
-import ModuleList from "../entity/modules/ModuleList";
import {Button, ButtonTray} from "../UI/Button";
import Icons from "../UI/Icons";
import useLoad from "../API/useLoad";
+import {createDrawerNavigator} from "@react-navigation/drawer";
+import ModuleViewScreen from "./ModuleViewScreen";
+import UserViewScreen from "./UserViewScreen";
const UserListScreen = ({navigation}) => {
@@ -12,6 +14,7 @@ const UserListScreen = ({navigation}) => {
// let modules = initialModules;
LogBox.ignoreLogs(['Non-serializable values were found in the navigation state']);
+ const Drawer = createDrawerNavigator();
const usersEndpoint = 'https://softwarehub.uk/unibase/api/users';
@@ -52,21 +55,32 @@ const UserListScreen = ({navigation}) => {
const goToAddScreen = () =>
navigation.navigate('UserAddScreen', {onAdd});
+ const handleSwitchToModules = () => {
+ navigation.navigate("UserListScreen");
+
// View -----------------------
- return (
-
- {/**/}
-
- } onClick={goToAddScreen}/>
-
+ return (
- {isLoading && Loading Records...}
+
+
+
-
-
- );
+
+
+ {/**/}
+
+ } onClick={goToAddScreen}/>
+
+
+ {isLoading && Loading Records...}
+
+
+
+
+ );
+ };
};
// const styles = StyleSheet.create({});