Application Android et IOS pour l'amicale des élèves https://play.google.com/store/apps/details?id=fr.amicaleinsat.application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EquipmentListScreen.tsx 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import React, { useRef, useState } from 'react';
  20. import { StyleSheet, View } from 'react-native';
  21. import { Button } from 'react-native-paper';
  22. import i18n from 'i18n-js';
  23. import EquipmentListItem from '../../../components/Lists/Equipment/EquipmentListItem';
  24. import MascotPopup from '../../../components/Mascot/MascotPopup';
  25. import { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
  26. import GENERAL_STYLES from '../../../constants/Styles';
  27. import { ApiRejectType } from '../../../utils/WebData';
  28. import WebSectionList from '../../../components/Screens/WebSectionList';
  29. import { useAuthenticatedRequest } from '../../../context/loginContext';
  30. export type DeviceType = {
  31. id: number;
  32. name: string;
  33. caution: number;
  34. booked_at: Array<{ begin: string; end: string }>;
  35. };
  36. export type RentedDeviceType = {
  37. device_id: number;
  38. device_name: string;
  39. begin: string;
  40. end: string;
  41. };
  42. type ResponseType = {
  43. devices: Array<DeviceType>;
  44. locations?: Array<RentedDeviceType>;
  45. };
  46. const LIST_ITEM_HEIGHT = 64;
  47. const styles = StyleSheet.create({
  48. headerContainer: {
  49. width: '100%',
  50. marginTop: 10,
  51. marginBottom: 10,
  52. },
  53. });
  54. function EquipmentListScreen() {
  55. const userRents = useRef<undefined | Array<RentedDeviceType>>();
  56. const [mascotDialogVisible, setMascotDialogVisible] = useState(false);
  57. const requestAll = useAuthenticatedRequest<{ devices: Array<DeviceType> }>(
  58. 'location/all'
  59. );
  60. const requestOwn = useAuthenticatedRequest<{
  61. locations: Array<RentedDeviceType>;
  62. }>('location/my');
  63. const getRenderItem = ({ item }: { item: DeviceType }) => {
  64. return (
  65. <EquipmentListItem
  66. item={item}
  67. userDeviceRentDates={getUserDeviceRentDates(item)}
  68. height={LIST_ITEM_HEIGHT}
  69. />
  70. );
  71. };
  72. const getUserDeviceRentDates = (
  73. item: DeviceType
  74. ): [string, string] | null => {
  75. let dates = null;
  76. if (userRents.current) {
  77. userRents.current.forEach((device: RentedDeviceType) => {
  78. if (item.id === device.device_id) {
  79. dates = [device.begin, device.end];
  80. }
  81. });
  82. }
  83. return dates;
  84. };
  85. const getListHeader = () => {
  86. return (
  87. <View style={styles.headerContainer}>
  88. <Button
  89. mode="contained"
  90. icon="help-circle"
  91. onPress={showMascotDialog}
  92. style={GENERAL_STYLES.centerHorizontal}
  93. >
  94. {i18n.t('screens.equipment.mascotDialog.title')}
  95. </Button>
  96. </View>
  97. );
  98. };
  99. const keyExtractor = (item: DeviceType): string => item.id.toString();
  100. const createDataset = (data: ResponseType | undefined) => {
  101. if (data) {
  102. if (data.locations) {
  103. userRents.current = data.locations;
  104. }
  105. return [{ title: '', data: data.devices }];
  106. } else {
  107. return [];
  108. }
  109. };
  110. const showMascotDialog = () => setMascotDialogVisible(true);
  111. const hideMascotDialog = () => setMascotDialogVisible(false);
  112. const request = () => {
  113. return new Promise(
  114. (
  115. resolve: (data: ResponseType) => void,
  116. reject: (error: ApiRejectType) => void
  117. ) => {
  118. requestAll()
  119. .then((devicesData) => {
  120. requestOwn()
  121. .then((rentsData) => {
  122. resolve({
  123. devices: devicesData.devices,
  124. locations: rentsData.locations,
  125. });
  126. })
  127. .catch(() =>
  128. resolve({
  129. devices: devicesData.devices,
  130. })
  131. );
  132. })
  133. .catch(reject);
  134. }
  135. );
  136. };
  137. return (
  138. <View style={GENERAL_STYLES.flex}>
  139. <WebSectionList
  140. request={request}
  141. createDataset={createDataset}
  142. keyExtractor={keyExtractor}
  143. renderItem={getRenderItem}
  144. renderListHeaderComponent={getListHeader}
  145. />
  146. <MascotPopup
  147. visible={mascotDialogVisible}
  148. title={i18n.t('screens.equipment.mascotDialog.title')}
  149. message={i18n.t('screens.equipment.mascotDialog.message')}
  150. icon="vote"
  151. buttons={{
  152. cancel: {
  153. message: i18n.t('screens.equipment.mascotDialog.button'),
  154. icon: 'check',
  155. onPress: hideMascotDialog,
  156. },
  157. }}
  158. emotion={MASCOT_STYLE.WINK}
  159. />
  160. </View>
  161. );
  162. }
  163. export default EquipmentListScreen;