Merge pull request #9 from mudabbir-ahmad/UserBranchPOST

UserBranchPOST
This commit is contained in:
bobbert authored and GitHub committed 2025-12-07 21:01:08 +00:00
commit f060cb19e8
16 files changed
+1169 -964

No files matched your search

+75 -33
View File
@@ -4,51 +4,93 @@ import ModuleListScreen from "./src/components/screens/ModuleListScreen";
import ModuleModifyScreen from "./src/components/screens/ModuleModifyScreen"; import ModuleModifyScreen from "./src/components/screens/ModuleModifyScreen";
import ModuleViewScreen from "./src/components/screens/ModuleViewScreen"; import ModuleViewScreen from "./src/components/screens/ModuleViewScreen";
import ModuleAddScreen from "./src/components/screens/ModuleAddScreen"; 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";
import {createDrawerNavigator} from '@react-navigation/drawer';
import {StyleSheet} from "react-native";
const Drawer = createDrawerNavigator();
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
const ModuleStack = () => (
<Stack.Navigator
initialRouteName='ModuleListScreen'
screenOptions={{
headerStyle: {backgroundColor: "black"},
headerTintColor: "white"
}}
>
<Stack.Screen
name="ModuleListScreen"
component={ModuleListScreen}
options={{headerShown: true}}
/>
<Stack.Screen
name="ModuleAddScreen"
component={ModuleAddScreen}
/>
<Stack.Screen
name="ModuleViewScreen"
component={ModuleViewScreen}
/>
<Stack.Screen
name="ModuleModifyScreen"
component={ModuleModifyScreen}
/>
</Stack.Navigator>
);
const UserStack = () => (
<Stack.Navigator
screenOptions={{
headerStyle: {backgroundColor: "black"},
headerTintColor: "white",
drawerType: 'slide',
}}
>
<Stack.Screen
name="UserListScreen"
component={UserListScreen}
options={{headerShown: true}}
/>
<Stack.Screen
name="UserAddScreen"
component={UserAddScreen}
/>
<Stack.Screen
name="UserViewScreen"
component={UserViewScreen}
/>
<Stack.Screen
name="UserModifyScreen"
component={UserModifyScreen}
/>
</Stack.Navigator>
);
export const App = () => { export const App = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return ( return (
<NavigationContainer> <NavigationContainer>
<Stack.Navigator <Drawer.Navigator
initialRouteName='ModuleListScreen'
screenOptions={{ screenOptions={{
headerStyle: {backgroundColor: "black"}, headerBackgroundColor: "black",
headerTintColor: "white" headerShown: false,
swipeEnabled: true
}} }}
> >
<Stack.Screen <Drawer.Screen
name="ModuleListScreen" name="Module Crudler"
component={ModuleListScreen} component={ModuleStack}
options={{title: 'List Modules'}}
/> />
<Drawer.Screen
<Stack.Screen name="User Crudler"
name="ModuleAddScreen" component={UserStack}
component={ModuleAddScreen}
options={{title: 'Add Modules'}}
/> />
</Drawer.Navigator>
<Stack.Screen
name="ModuleViewScreen"
component={ModuleViewScreen}
options={{title: 'View Modules'}}
/>
<Stack.Screen
name="ModuleModifyScreen"
component={ModuleModifyScreen}
options={{title: 'Modify Modules'}}
/>
</Stack.Navigator>
</NavigationContainer> </NavigationContainer>
); );
}; };
export default App; export default App;
+504 -889
View File
File diff suppressed because it is too large. Load diff
+10 -6
View File
@@ -10,14 +10,18 @@
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^15.0.3", "@expo/vector-icons": "^15.0.3",
"@react-native-picker/picker": "^2.11.4", "@react-native-picker/picker": "^2.11.1",
"@react-navigation/native": "^7.1.18", "@react-navigation/drawer": "^7.7.8",
"@react-navigation/native-stack": "^7.5.1", "@react-navigation/native": "^7.1.24",
"expo": "^54.0.20", "@react-navigation/native-stack": "^7.8.5",
"expo-status-bar": "~3.0.8", "expo": "~54.0.27",
"expo-status-bar": "~3.0.9",
"react": "19.1.0", "react": "19.1.0",
"react-native": "^0.81.5", "react-native": "^0.81.5",
"react-native-fullwidth-image": "^0.1.3" "react-native-fullwidth-image": "^0.1.3",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "~4.1.1",
"react-native-worklets": "^0.5.1"
}, },
"private": true, "private": true,
"devDependencies": { "devDependencies": {
+2 -1
View File
@@ -11,9 +11,10 @@ const useLoad = (loadEndpoint) => {
// Loader ---------------------- // Loader ----------------------
const loadRecords = async (endpoint) => { const loadRecords = async (endpoint) => {
setIsLoading(true);
const response = await API.get(endpoint); const response = await API.get(endpoint);
setIsLoading(false);
if (response.isSuccess) setRecords(response.result); if (response.isSuccess) setRecords(response.result);
setIsLoading(false);
}; };
useEffect(() => { useEffect(() => {
+36 -30
View File
@@ -23,49 +23,55 @@ const ModuleView = ({module, onDelete, onModify}) => {
// View ----------------------- // View -----------------------
return (<View style={styles.container}> return (
<FullWidthImage source={{uri: module.ModuleImageURL}} style={styles.image}/> <View style={styles.container}>
<FullWidthImage source={{uri: module.ModuleImageURL}} style={styles.image}/>
<View style={styles.infoTray}> <View style={styles.infoTray}>
<Text style={styles.boldText}>View {module.ModuleCode} {module.ModuleName}</Text> <Text style={styles.boldText}>View {module.ModuleCode} {module.ModuleName}</Text>
<Text style={styles.text}>Level {module.ModuleLevel}</Text> <Text style={styles.text}>Level {module.ModuleLevel}</Text>
<Text style={styles.text}>cohort {module.ModuleYearID}</Text> <Text style={styles.text}>cohort {module.ModuleYearID}</Text>
<Text style={styles.Text}> <Text style={styles.Text}>
{module.ModuleLeaderID} <Text style={styles.dimText}>(Module Leader)</Text> {module.ModuleLeaderID} <Text style={styles.dimText}>(Module Leader)</Text>
</Text> </Text>
</View> </View>
<ButtonTray> <ButtonTray>
<Button <Button
icon={<Icons.Edit/>} icon={<Icons.Edit/>}
label={"Modify"} label={"Modify"}
onClick={onModify} onClick={onModify}
/> />
<Button <Button
icon={<Icons.Delete/>} icon={<Icons.Delete/>}
label={"Delete"} label={"Delete"}
// styleButton={{backgroundColor: 'mistyrose'}} // styleButton={{backgroundColor: 'mistyrose'}}
// styleLabel={{color: 'red'}} // styleLabel={{color: 'red'}}
onClick={requestDelete} onClick={requestDelete}
/> />
</ButtonTray> </ButtonTray>
</View>); </View>);
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
gap: 15, gap: 15,
}, infoTray: { },
infoTray: {
gap: 5, gap: 5,
}, image: { },
image: {
borderRadius: 3, borderRadius: 3,
}, text: { },
text: {
fontSize: 16, fontSize: 16,
}, boldText: { },
boldText: {
fontSize: 16, fontWeight: 'bold', fontSize: 16, fontWeight: 'bold',
}, dimText: { },
dimText: {
color: "grey", color: "grey",
} }
+125
View File
@@ -0,0 +1,125 @@
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,
UserUsertypeID: null,
UserYearID: null,
UserLevel: null,
UserImageURL: null,
};
const UserForm = ({originalUser, onSubmit, onCancel}) => {
// Initialisation -------------
defaultUser.UserID = Math.floor(100000 + Math.random() * 900000);
defaultUser.UserImageURL = 'https://images.generated.photos/tZmcC6WnzFmgAwRMsBnGYcmaj_NSktxhYzBfTqej_Lo/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/MDE3MjYzLmpwZw.jpg';
const userTypesEndpoint = 'https://softwarehub.uk/unibase/api/usertypes';
const yearsEndpoint = 'https://softwarehub.uk/unibase/api/years';
const levels = [
{value: 3, label: '3 (Foundation)'},
{value: 4, label: '4 (First Year)'},
{value: 5, label: '5 (Second Year)'},
{value: 6, label: '6 (Third Year)'},
{value: 7, label: '7 (Masters)'},
];
// State ----------------------
const [user, setUser] = useState(originalUser || defaultUser);
const [userTypes, , isUsertypesLoading] = useLoad(userTypesEndpoint);
const [years, , isYearsLoading] = useLoad(yearsEndpoint);
// Handlers -------------------
const handleChange = (field, value) => {
setUser({...user, [field]: value});
};
const handleSubmit = () => onSubmit(user);
// View -----------------------
const submitLabel = originalUser ? 'Modify' : 'Add';
const submitIcon = originalUser ? <Icons.Edit/> : <Icons.Add/>;
const usertypeOptions = userTypes.map((usertype) => ({
value: usertype.UsertypeID,
label: usertype.UsertypeName
}));
const yearOptions = years.map((year) => ({
value: year.YearID,
label: year.YearName
}));
return (
<Form
onSubmit={handleSubmit}
onCancel={onCancel}
submitLabel={submitLabel}
submitIcon={submitIcon}
>
<Form.InputText
label='First Name'
value={user.UserFirstname}
onChange={(value) => handleChange('UserFirstname', value)}
/>
<Form.InputText
label='Last Name'
value={user.UserLastname}
onChange={(value) => handleChange('UserLastname', value)}
/>
<Form.InputText
label='Email'
value={user.UserEmail}
onChange={(value) => handleChange('UserEmail', value)}
/>
<Form.InputSelect
label='User Type'
value={user.UserUsertypeID}
onChange={(value) => handleChange('UserUsertypeID', value)}
prompt={'Select user type...'}
options={usertypeOptions}
isLoading={isUsertypesLoading}
/>
<Form.InputSelect
label='Level'
value={user.UserLevel}
onChange={(value) => handleChange('UserLevel', value)}
prompt={'Select level...'}
options={levels}
/>
<Form.InputSelect
label='Cohort Year'
value={user.UserYearID}
onChange={(value) => handleChange('UserYearID', value)}
prompt={'Select cohort year...'}
options={yearOptions}
isLoading={isYearsLoading}
/>
<Form.InputText
label='User Image URL'
value={user.UserImageURL}
onChange={(value) => handleChange('UserImageURL', value)}
/>
</Form>
);
};
export default UserForm;
+43
View File
@@ -0,0 +1,43 @@
import {StyleSheet, Text, View, Pressable} from "react-native";
import useLoad from "../../API/useLoad";
const UserItem = ({user, onSelect}) => {
// Initialisation -------------
const usertypesEndpoint = 'https://softwarehub.uk/unibase/api/usertypes';
const [usertypes] = useLoad(usertypesEndpoint);
const getUserTypeName = (usertypeID) => {
const usertype = usertypes.find(type => type.UsertypeID === usertypeID);
return usertype ? usertype.UsertypeName : "Unknown";
};
const usertypeName = getUserTypeName(user.UserUsertypeID);
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Pressable key={user.UserID} onPress={() => onSelect(user)}>
<View style={styles.userItem}>
<Text style={styles.text}>
{user.UserFirstname} {user.UserLastname} ({usertypeName})
</Text>
</View>
</Pressable>
);
};
const styles = StyleSheet.create({
userItem: {
paddingVertical: 15,
borderBottomWidth: 1,
borderBottomColor: "lightgray",
},
text: {
fontSize: 18,
},
});
export default UserItem;
+30
View File
@@ -0,0 +1,30 @@
import {StyleSheet, ScrollView} from "react-native";
import UserItem from "./UserItem";
import React from "react";
const UserList = ({users, onSelect}) => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<ScrollView style={styles.container}>
{
users.map((user) => (
<UserItem
key={user.UserID}
user={user}
onSelect={onSelect}
/>
))
}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {},
});
export default UserList;
+116
View File
@@ -0,0 +1,116 @@
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";
import useLoad from "../../API/useLoad";
const UserView = ({user, onDelete, onModify}) => {
// Initialisation -------------
const fullName = `${user.UserFirstname} ${user.UserLastname}`;
const userTypesEndpoint = 'https://softwarehub.uk/unibase/api/usertypes';
const yearsEndpoint = 'https://softwarehub.uk/unibase/api/years';
const levels = [
{value: 3, label: '3 (Foundation)'},
{value: 4, label: '4 (First Year)'},
{value: 5, label: '5 (Second Year)'},
{value: 6, label: '6 (Third Year)'},
{value: 7, label: '7 (Masters)'},
];
// State ----------------------
const [userTypes] = useLoad(userTypesEndpoint);
const [years] = useLoad(yearsEndpoint);
// Handlers -------------------
const handleDelete = () => onDelete(user);
const requestDelete = () => {
Alert.alert(
'Delete Warning',
`Are you sure you want to delete user ${fullName}?`,
[
{text: 'Cancel'},
{text: 'Delete', onPress: handleDelete}
]
);
};
const userLevelLabel = levels.find(level => level.value === user.UserLevel)?.label || 'Unknown Level';
const getUserType = (usertypeID) => {
if (!usertypeID) return "Unknown";
const usertype = userTypes.find(type => type.UsertypeID === usertypeID);
return usertype ? usertype.UsertypeName : usertypeID;
};
const getYear = (yearID) => {
if (!yearID) return "Unknown";
const year = years.find(y => y.YearID === yearID);
return year ? year.YearName : yearID;
};
const yearName = getYear(user.UserYearID);
const userTypeName = getUserType(user.UserUsertypeID);
// View -----------------------
return (
<View style={styles.container}>
<FullWidthImage source={{uri: user.UserImageURL}} style={styles.image}/>
<View style={styles.infoTray}>
<Text style={styles.boldText}>{fullName}</Text>
<Text style={styles.text}>{user.UserEmail}</Text>
<Text style={styles.text}>Level {userLevelLabel}</Text>
<Text style={styles.text}>
{user.UserUsertypeName || userTypeName} <Text style={styles.dimText}>(User Type)</Text>
</Text>
<Text style={styles.text}>Cohort {yearName}</Text>
</View>
<ButtonTray>
<Button
icon={<Icons.Edit/>}
label={"Modify"}
onClick={onModify}
/>
<Button
icon={<Icons.Delete/>}
label={"Delete"}
onClick={requestDelete}
/>
</ButtonTray>
</View>
);
};
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;
@@ -2,9 +2,6 @@
import {LogBox, Text} from "react-native"; import {LogBox, Text} from "react-native";
import Screen from "../layout/Screen"; import Screen from "../layout/Screen";
import ModuleList from "../entity/modules/ModuleList"; import ModuleList from "../entity/modules/ModuleList";
// import API from "../API/API";
// import initialModules from "../../data/modules.js";
// import RenderCount from "../UI/RenderCount";
import {Button, ButtonTray} from "../UI/Button"; import {Button, ButtonTray} from "../UI/Button";
import Icons from "../UI/Icons"; import Icons from "../UI/Icons";
import useLoad from "../API/useLoad"; import useLoad from "../API/useLoad";
@@ -15,7 +12,6 @@ const ModuleListScreen = ({navigation}) => {
// let modules = initialModules; // let modules = initialModules;
LogBox.ignoreLogs(['Non-serializable values were found in the navigation state']); LogBox.ignoreLogs(['Non-serializable values were found in the navigation state']);
const modulesEndpoint = 'https://softwarehub.uk/unibase/api/modules'; const modulesEndpoint = 'https://softwarehub.uk/unibase/api/modules';
// State ---------------------- // State ----------------------
+1 -1
View File
@@ -11,7 +11,7 @@ export const ModuleViewScreen = ({navigation, route}) => {
// State ---------------------- // State ----------------------
// Handlers ------------------- // Handlers -------------------
const goToModifyScreen = () => navigation.replace("ModuleModifyScreen", { module, onModify }); const goToModifyScreen = () => navigation.replace("ModuleModifyScreen", {module, onModify});
// View ----------------------- // View -----------------------
return (<Screen> return (<Screen>
+24
View File
@@ -0,0 +1,24 @@
import {StyleSheet, Text, TextInput, View} from "react-native";
import Screen from "../layout/Screen";
import UserForm from "../entity/users/UserForm";
export const UserAddScreen = ({navigation, route}) => {
// Initialisation -------------
const {onAdd} = route.params;
// State ----------------------
// Handlers -------------------
const handleCancel = navigation.goBack;
// View -----------------------
return (
<Screen style={styles.container}>
<UserForm onSubmit={onAdd} onCancel={handleCancel}/>
</Screen>
);
};
const styles = StyleSheet.create({});
export default UserAddScreen;
+66
View File
@@ -0,0 +1,66 @@
import {LogBox, Text} from "react-native";
import Screen from "../layout/Screen";
import UserList from "../entity/users/UserList";
import {Button, ButtonTray} from "../UI/Button";
import Icons from "../UI/Icons";
import useLoad from "../API/useLoad";
const UserListScreen = ({navigation}) => {
// Initialisation -------------
LogBox.ignoreLogs(['Non-serializable values were found in the navigation state']);
const usersEndpoint = 'https://softwarehub.uk/unibase/api/users';
// State ----------------------
const [users, setUsers, isLoading, loadUsers] = useLoad(usersEndpoint);
// Handlers -------------------
const handleDelete = (user) =>
setUsers(users.filter((item) => item.UserID !== user.UserID));
const onDelete = (user) => {
handleDelete(user);
navigation.goBack();
};
const handleAdd = (user) => setUsers([...users, user]);
const handleModify = (updatedUser) => setUsers(
users.map((user) => (user.UserID === updatedUser.UserID) ? updatedUser : user),
);
const onAdd = (user) => {
handleAdd(user);
navigation.goBack();
};
const onModify = (user) => {
handleModify(user);
navigation.replace('UserViewScreen', {user, onDelete, onModify});
};
const goToViewScreen = (user) =>
navigation.navigate('UserViewScreen', {user, onDelete, onModify});
const goToAddScreen = () =>
navigation.navigate('UserAddScreen', {onAdd});
const handleSwitchToModules = () => {
navigation.navigate("ModuleListScreen");
};
// View -----------------------
return (
<Screen>
<ButtonTray>
<Button label='Add' icon={<Icons.Add/>} onClick={goToAddScreen}/>
</ButtonTray>
{isLoading && <Text>Loading Records...</Text>}
<UserList users={users} onSelect={goToViewScreen}/>
</Screen>
);
};
export default UserListScreen;
@@ -0,0 +1,25 @@
import Screen from "../layout/Screen";
import UserForm from "../entity/users/UserForm";
const UserModifyScreen = ({navigation, route}) => {
// Initialisation -------------
const {user, onModify} = route.params;
// State ----------------------
// Handlers -------------------
const handleCancel = () => navigation.goBack();
// View -----------------------
return (
<Screen>
<UserForm
originalUser={user}
onSubmit={onModify}
onCancel={handleCancel}
/>
</Screen>
);
};
export default UserModifyScreen;
+28
View File
@@ -0,0 +1,28 @@
import {StyleSheet} from "react-native";
import Screen from "../layout/Screen";
import UserView from "../entity/users/UserView";
const UserViewScreen = ({navigation, route}) => {
// Initialisation -------------
const {user, onDelete, onModify} = route.params;
// State ----------------------
// Handlers -------------------
const goToModifyScreen = () => navigation.replace("UserModifyScreen", {user, onModify});
// View -----------------------
return (
<Screen>
<UserView
user={user}
onDelete={onDelete}
onModify={goToModifyScreen}
/>
</Screen>
);
};
const styles = StyleSheet.create({});
export default UserViewScreen;
+84
View File
@@ -0,0 +1,84 @@
const users = [
{
UserID: 820,
UserFirstname: 'Graeme',
UserLastname: 'Jones',
UserEmail: 'Ku06696@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/Zx-gNUWFq9NPQDPRLEJQQPWx19QhpKGSAnzIPFUDz3k/rs:fit:512:512/wm:0.95:sowe:18:18:0.33/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92Ml8w/MDM4MjcxLmpwZw.jpg',
UserType: 'Staff',
UserYear: null,
},
{
UserID: 824,
UserFirstname: 'Paul',
UserLastname: 'Knave',
UserEmail: 'K1234567@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/IitdqDaBBrr4auYZFuuC_dplp3OtwU2cCuN4q35rN8M/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92Ml8w/NDcyMTg5LmpwZw.jpg',
UserType: 'Staff',
UserYear: null,
},
{
UserID: 527,
UserFirstname: 'Justin',
UserLastname: 'TWUMASI',
UserEmail: 'K3346661@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/1zzVOGo2BC7zlIbuqtT9E03KbcRrCFw0CeIkCRH-pQo/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/OTU2NTI4LmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
{
UserID: 287,
UserFirstname: 'Faris',
UserLastname: 'AHMED',
UserEmail: 'K3265517@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/BdRHDOlzfO2nY3cm9SJuBaf_LPCCv0tzUq3hSv6-Ub8/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/NDYzODY2LmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
{
UserID: 467,
UserFirstname: 'Tobi',
UserLastname: 'OLU',
UserEmail: 'K8769678@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/JaFMncOoAFAerEyw_xWIeFMjzgfkkseT_oGEyP6CgQI/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/MzI5MTY3LmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
{
UserID: 534,
UserFirstname: 'Artur',
UserLastname: 'WAGNER',
UserEmail: 'K6664833@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/XRfSKWBVup-KW5T_lvS0tyce6rGaEBKUDWvk6lxZ8x8/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/MDIwNTQwLmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
{
UserID: 515,
UserFirstname: 'Luke',
UserLastname: 'STEVENS',
UserEmail: 'K6845215@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/XtsBlRxQLgWzFAtycpalUkvVdf9tAvCfC9vEPtSVaxY/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/MzQ2ODE2LmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
{
UserID: 276,
UserFirstname: 'Hashim',
UserLastname: 'ABDALLAH',
UserEmail: 'K1083353@kingston.ac.uk',
UserImageURL:
'https://images.generated.photos/eL1-OlKDqGf1IaL_b2O8aSj7osDX_eFVHZEoJ0f3ZV0/rs:fit:256:256/czM6Ly9pY29uczgu/Z3Bob3Rvcy1wcm9k/LnBob3Rvcy92M18w/NzYwNjc0LmpwZw.jpg',
UserType: 'Student',
UserYear: '2022-23',
},
];
export default users;