From e807e2cae44b07b6efab18e1e59af3f4ece5288e Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:52:35 -0400 Subject: [PATCH 1/8] feat: initial commit --- client/screens/FileUpload.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 client/screens/FileUpload.tsx diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx new file mode 100644 index 0000000..e69de29 From 0d83559fd4d216381f8cdc1a58919a0e2457727d Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Thu, 28 Mar 2024 13:55:46 -0400 Subject: [PATCH 2/8] feat: all components for file uploading + styling and impl --- client/assets/arrow-left.svg | 4 +- client/components/ChooseFileButton.tsx | 13 ++ client/components/nav_buttons/BackButton.tsx | 2 +- .../navigation/AppStackBottomTabNavigator.tsx | 3 +- client/screens/FileUpload.tsx | 133 ++++++++++++++++++ 5 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 client/components/ChooseFileButton.tsx diff --git a/client/assets/arrow-left.svg b/client/assets/arrow-left.svg index 99dedf4..7a7d75d 100644 --- a/client/assets/arrow-left.svg +++ b/client/assets/arrow-left.svg @@ -1,4 +1,4 @@ - - + + diff --git a/client/components/ChooseFileButton.tsx b/client/components/ChooseFileButton.tsx new file mode 100644 index 0000000..29b995f --- /dev/null +++ b/client/components/ChooseFileButton.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Text, TouchableOpacity } from 'react-native'; + +export function ChooseFileButton({ onPress }: { onPress: () => void }) { + return ( + + Choose File + + ); +} diff --git a/client/components/nav_buttons/BackButton.tsx b/client/components/nav_buttons/BackButton.tsx index a34fa00..a73de7b 100644 --- a/client/components/nav_buttons/BackButton.tsx +++ b/client/components/nav_buttons/BackButton.tsx @@ -11,7 +11,7 @@ export function BackButton() { return ( } onPress={() => navigation.goBack()} diff --git a/client/navigation/AppStackBottomTabNavigator.tsx b/client/navigation/AppStackBottomTabNavigator.tsx index 48e1a01..deab285 100644 --- a/client/navigation/AppStackBottomTabNavigator.tsx +++ b/client/navigation/AppStackBottomTabNavigator.tsx @@ -9,6 +9,7 @@ import Calendar from '../assets/bottom-nav/calendar.svg'; import Home from '../assets/bottom-nav/home.svg'; import User from '../assets/bottom-nav/user.svg'; import TimelineCalendarScreen from '../screens/Calendar'; +import FileUploadScreen from '../screens/FileUpload'; import MedicationList from '../screens/MedicationList'; import PatientView from '../screens/Profile/PatientView'; import Profile from '../screens/Profile/Profile'; @@ -61,7 +62,7 @@ export function AppStackBottomTabNavigator() { tabBarIcon: ({ color }) => , tabBarLabel: () => }} - component={ProfileNavigation} + component={FileUploadScreen} /> ); diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index e69de29..1b9706a 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; +import { Text, TextInput, TouchableOpacity, View } from 'react-native'; + +import { DocumentPickerAsset, getDocumentAsync } from 'expo-document-picker'; +import DropDownPicker from 'react-native-dropdown-picker'; + +import { ChooseFileButton } from '../components/ChooseFileButton'; +import { BackButton } from '../components/nav_buttons/BackButton'; +import { useCareWalletContext } from '../contexts/CareWalletContext'; +import { useFile } from '../services/file'; + +// TODO: Add SVGs to background and button, and add functionality for Title, Label, Notes +export default function FileUploadScreen() { + const { user, group } = useCareWalletContext(); + const { uploadFileMutation } = useFile(); + const [open, setOpen] = useState(false); + const [fileTitle, setFileTitle] = useState(''); + const [label, setLabel] = useState('Medication'); + const [additionalNotes, setAdditionalNotes] = useState(''); + const [pickedFile, setPickedFile] = useState( + null + ); + + const handleFileTitleChange = (text: string) => { + setFileTitle(text); + }; + + const handleAdditionalNotesChange = (text: string) => { + setAdditionalNotes(text); + }; + + const pickDocument = async () => { + try { + await getDocumentAsync({ + type: '*/*', + copyToCacheDirectory: false + }).then((res) => { + if (!res.canceled) { + setPickedFile(res.assets[0]); + } + }); + } catch (err) { + console.log('err', err); + } + }; + + const submitFile = async () => { + try { + if (pickedFile) { + uploadFileMutation({ + file: pickedFile, + userId: user.userID, + groupId: group.groupID + }); + } + } catch (err) { + console.log('err', err); + } + }; + + // TODO: Choosefile border color, dropdown styling, fonts, choose file svg + return ( + + + + + + File Upload + + + + + + + File Title + + + + File Label + + + + + + + Additional Notes + + + + + + + + + Submit + + + + + + ); +} From 72939f25ef69df18356bfdbab8dc2a03ca6f9020 Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:08:58 -0400 Subject: [PATCH 3/8] feat: navigation + styling --- client/assets/file-upload/circle.svg | 3 +++ client/assets/file-upload/heart.svg | 3 +++ client/navigation/AppStackBottomTabNavigator.tsx | 7 ++++++- client/navigation/types.ts | 1 + client/screens/Profile/Profile.tsx | 6 ++++++ 5 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 client/assets/file-upload/circle.svg create mode 100644 client/assets/file-upload/heart.svg diff --git a/client/assets/file-upload/circle.svg b/client/assets/file-upload/circle.svg new file mode 100644 index 0000000..990cac9 --- /dev/null +++ b/client/assets/file-upload/circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/assets/file-upload/heart.svg b/client/assets/file-upload/heart.svg new file mode 100644 index 0000000..bedcfd7 --- /dev/null +++ b/client/assets/file-upload/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/navigation/AppStackBottomTabNavigator.tsx b/client/navigation/AppStackBottomTabNavigator.tsx index deab285..1802c3d 100644 --- a/client/navigation/AppStackBottomTabNavigator.tsx +++ b/client/navigation/AppStackBottomTabNavigator.tsx @@ -62,7 +62,7 @@ export function AppStackBottomTabNavigator() { tabBarIcon: ({ color }) => , tabBarLabel: () => }} - component={FileUploadScreen} + component={ProfileNavigation} /> ); @@ -81,6 +81,11 @@ export function ProfileNavigation() { options={{ headerShown: false }} component={PatientView} /> + ); } diff --git a/client/navigation/types.ts b/client/navigation/types.ts index 320ce2f..dbe5994 100644 --- a/client/navigation/types.ts +++ b/client/navigation/types.ts @@ -8,6 +8,7 @@ export type AppStackParamList = { Profile: undefined; PatientView: undefined; ProfileScreens: undefined; + FileUploadScreen: undefined; Landing: undefined; Calendar: undefined; Notifications: undefined; diff --git a/client/screens/Profile/Profile.tsx b/client/screens/Profile/Profile.tsx index 09643b2..e1236b2 100644 --- a/client/screens/Profile/Profile.tsx +++ b/client/screens/Profile/Profile.tsx @@ -74,6 +74,12 @@ export default function Profile() { + + navigation.navigate('FileUploadScreen')} + /> + signOutMutation()} /> From 7b50bae40ed449774156d670686311a4b9fd2696 Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:05:57 -0400 Subject: [PATCH 4/8] feat: add label, additional notes to backend routes + tanstack on FE --- backend/db/migrations/5.files.sql | 5 ++++- backend/docs/docs.go | 19 +++++++++++++++++++ backend/docs/swagger.json | 19 +++++++++++++++++++ backend/docs/swagger.yaml | 13 +++++++++++++ backend/models/file.go | 2 ++ backend/schema/files/routes.go | 4 ++++ backend/schema/files/transactions.go | 4 ++-- client/screens/FileUpload.tsx | 8 ++++++-- client/services/file.ts | 10 ++++++++-- 9 files changed, 77 insertions(+), 7 deletions(-) diff --git a/backend/db/migrations/5.files.sql b/backend/db/migrations/5.files.sql index 82b38cf..0c7ac28 100644 --- a/backend/db/migrations/5.files.sql +++ b/backend/db/migrations/5.files.sql @@ -8,8 +8,11 @@ CREATE TABLE IF NOT EXISTS files ( upload_date timestamp, file_size integer NOT NULL, task_id integer, + notes varchar, + label_name varchar NOT NULL, PRIMARY KEY (file_id), FOREIGN KEY (group_id) REFERENCES care_group (group_id), FOREIGN KEY (upload_by) REFERENCES users (user_id), - FOREIGN KEY (task_id) REFERENCES task (task_id) + FOREIGN KEY (task_id) REFERENCES task (task_id), + FOREIGN KEY (group_id, label_name) REFERENCES label (group_id, label_name) ); diff --git a/backend/docs/docs.go b/backend/docs/docs.go index df089af..1d33deb 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -43,6 +43,19 @@ const docTemplate = `{ "name": "group_id", "in": "formData", "required": true + }, + { + "type": "string", + "description": "Notes for the file", + "name": "notes", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Label name for the file", + "name": "label_name", + "in": "formData" } ], "responses": { @@ -1223,6 +1236,12 @@ const docTemplate = `{ "group_id": { "type": "integer" }, + "label_name": { + "type": "string" + }, + "notes": { + "type": "string" + }, "task_id": { "type": "integer" }, diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index cf5ea38..86db0ca 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -36,6 +36,19 @@ "name": "group_id", "in": "formData", "required": true + }, + { + "type": "string", + "description": "Notes for the file", + "name": "notes", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Label name for the file", + "name": "label_name", + "in": "formData" } ], "responses": { @@ -1216,6 +1229,12 @@ "group_id": { "type": "integer" }, + "label_name": { + "type": "string" + }, + "notes": { + "type": "string" + }, "task_id": { "type": "integer" }, diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 657f177..3edd7f5 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -33,6 +33,10 @@ definitions: type: integer group_id: type: integer + label_name: + type: string + notes: + type: string task_id: type: integer upload_by: @@ -237,6 +241,15 @@ paths: name: group_id required: true type: integer + - description: Notes for the file + in: formData + name: notes + required: true + type: string + - description: Label name for the file + in: formData + name: label_name + type: string responses: "200": description: OK diff --git a/backend/models/file.go b/backend/models/file.go index b0878e4..b931270 100644 --- a/backend/models/file.go +++ b/backend/models/file.go @@ -8,4 +8,6 @@ type File struct { UploadDate string `json:"upload_date"` FileSize int64 `json:"file_size"` TaskID int `json:"task_id"` + Notes string `json:"notes"` + LabelName string `json:"label_name"` } diff --git a/backend/schema/files/routes.go b/backend/schema/files/routes.go index 3e426fb..6db4f46 100644 --- a/backend/schema/files/routes.go +++ b/backend/schema/files/routes.go @@ -32,6 +32,8 @@ func FileGroup(v1 *gin.RouterGroup, c *PgModel) *gin.RouterGroup { // @param file_data formData file true "Body with file zip" // @param upload_by formData string true "The userId of the uploader" // @param group_id formData int true "The groupId of the uploader" +// @param notes formData string true "Notes for the file" +// @param label_name formData string false "Label name for the file" // // @success 200 {object} models.File // @failure 400 {object} string @@ -48,6 +50,8 @@ func (pg *PgModel) UploadFile(c *gin.Context) { fileResponse := form.File["file_data"][0] file.UploadBy = form.Value["upload_by"][0] file.GroupID, err = strconv.Atoi(form.Value["group_id"][0]) + file.Notes = form.Value["notes"][0] + file.LabelName = form.Value["label_name"][0] if err != nil { c.JSON(http.StatusBadRequest, "Failed to parse groupid") diff --git a/backend/schema/files/transactions.go b/backend/schema/files/transactions.go index 2085a9e..348ba07 100644 --- a/backend/schema/files/transactions.go +++ b/backend/schema/files/transactions.go @@ -30,8 +30,8 @@ func UploadFile(pool *pgxpool.Pool, file models.File, data *multipart.FileHeader } // Insert file into database - err := pool.QueryRow(context.Background(), "INSERT INTO files (file_name, group_id, upload_by, upload_date, file_size) VALUES ($1, $2, $3, $4, $5) RETURNING file_id;", - file.FileName, file.GroupID, file.UploadBy, file.UploadDate, data.Size).Scan(&file.FileID) + err := pool.QueryRow(context.Background(), "INSERT INTO files (file_name, group_id, upload_by, upload_date, file_size, notes, label_name) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING file_id;", + file.FileName, file.GroupID, file.UploadBy, file.UploadDate, data.Size, file.Notes, file.LabelName).Scan(&file.FileID) if err != nil { fmt.Println(err.Error()) return err diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index 1b9706a..2d8e2de 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -15,7 +15,7 @@ export default function FileUploadScreen() { const { uploadFileMutation } = useFile(); const [open, setOpen] = useState(false); const [fileTitle, setFileTitle] = useState(''); - const [label, setLabel] = useState('Medication'); + const [label, setLabel] = useState('Financial'); const [additionalNotes, setAdditionalNotes] = useState(''); const [pickedFile, setPickedFile] = useState( null @@ -23,10 +23,12 @@ export default function FileUploadScreen() { const handleFileTitleChange = (text: string) => { setFileTitle(text); + console.log('fileTitle', fileTitle); }; const handleAdditionalNotesChange = (text: string) => { setAdditionalNotes(text); + console.log('notes', additionalNotes); }; const pickDocument = async () => { @@ -50,7 +52,9 @@ export default function FileUploadScreen() { uploadFileMutation({ file: pickedFile, userId: user.userID, - groupId: group.groupID + groupId: group.groupID, + label: label, + notes: additionalNotes }); } } catch (err) { diff --git a/client/services/file.ts b/client/services/file.ts index 6a25312..7fd93a7 100644 --- a/client/services/file.ts +++ b/client/services/file.ts @@ -13,12 +13,16 @@ interface UploadFileProps { file: DocumentPickerAsset; userId: string; groupId: number; + label: string; + notes: string; } const uploadFile = async ({ file, userId, - groupId + groupId, + label, + notes }: UploadFileProps): Promise => { const uploadResumable = createUploadTask( `${api_url}/files/upload`, @@ -29,7 +33,9 @@ const uploadFile = async ({ fieldName: 'file_data', parameters: { upload_by: userId, - group_id: groupId.toString() + group_id: groupId.toString(), + label_name: label, + notes: notes } } ); From be9660d6710a0d0dc9e02cad9a861d65740e69cf Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:08:38 -0400 Subject: [PATCH 5/8] feat: fetch labels from BE using group id + scroll view --- client/components/ChooseFileButton.tsx | 2 +- client/screens/FileUpload.tsx | 171 +++++++++++++++---------- client/services/task.ts | 7 + 3 files changed, 110 insertions(+), 70 deletions(-) diff --git a/client/components/ChooseFileButton.tsx b/client/components/ChooseFileButton.tsx index 29b995f..b0c7354 100644 --- a/client/components/ChooseFileButton.tsx +++ b/client/components/ChooseFileButton.tsx @@ -5,7 +5,7 @@ export function ChooseFileButton({ onPress }: { onPress: () => void }) { return ( Choose File diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index 2d8e2de..77d0edd 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -1,5 +1,11 @@ -import React, { useState } from 'react'; -import { Text, TextInput, TouchableOpacity, View } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { + ScrollView, + Text, + TextInput, + TouchableOpacity, + View +} from 'react-native'; import { DocumentPickerAsset, getDocumentAsync } from 'expo-document-picker'; import DropDownPicker from 'react-native-dropdown-picker'; @@ -8,8 +14,8 @@ import { ChooseFileButton } from '../components/ChooseFileButton'; import { BackButton } from '../components/nav_buttons/BackButton'; import { useCareWalletContext } from '../contexts/CareWalletContext'; import { useFile } from '../services/file'; +import { getLabelsByGroup } from '../services/task'; -// TODO: Add SVGs to background and button, and add functionality for Title, Label, Notes export default function FileUploadScreen() { const { user, group } = useCareWalletContext(); const { uploadFileMutation } = useFile(); @@ -20,15 +26,34 @@ export default function FileUploadScreen() { const [pickedFile, setPickedFile] = useState( null ); + const [allLabels, setAllLabels] = useState([]); // Store all unique labels as a list of strings + DropDownPicker.setTheme('DARK'); + + // Fetch all labels by the group id + useEffect(() => { + const fetchLabels = async () => { + try { + console.log('Group:', group); + const labels = await getLabelsByGroup(group.groupID); + const uniqueLabels = Array.from( + new Set(labels.map((label) => label.label_name)) + ); + setAllLabels(uniqueLabels); + console.log('Labels:', allLabels); + } catch (error) { + console.error('Error fetching labels:', error); + } + }; + + fetchLabels(); + }, [group]); const handleFileTitleChange = (text: string) => { setFileTitle(text); - console.log('fileTitle', fileTitle); }; const handleAdditionalNotesChange = (text: string) => { setAdditionalNotes(text); - console.log('notes', additionalNotes); }; const pickDocument = async () => { @@ -64,74 +89,82 @@ export default function FileUploadScreen() { // TODO: Choosefile border color, dropdown styling, fonts, choose file svg return ( - - - - - - File Upload - - - - - - - File Title - - - - File Label - + + + + + + + File Upload + + - - - - - Additional Notes - - + + + + + File Title + + + + + + File Label + + ({ + label, + value: label + }))} + setOpen={setOpen} + setValue={setLabel} + placeholder="Select Label" + style={{ + backgroundColor: '#1A56C4', + borderColor: 'transparent' + }} + textStyle={{ + color: 'white' + }} + disabledStyle={{ + opacity: 0.5 + }} + /> + - - - - - - Submit + + + + Additional Notes - + + + + + + + + Submit + + + - + ); } diff --git a/client/services/task.ts b/client/services/task.ts index 68744b3..d14087b 100644 --- a/client/services/task.ts +++ b/client/services/task.ts @@ -21,6 +21,13 @@ const getTask = async (taskID: string): Promise => { return data; }; +export const getLabelsByGroup = async ( + groupID: number +): Promise => { + const { data } = await axios.get(`${api_url}/group/${groupID}/labels`); + return data; +}; + const getTaskByAssigned = async (userId: string): Promise => { const { data } = await axios.get( `${api_url}/tasks/assigned?userIDs=${userId}` From dec424fdcca0ff052d6af236885d4b313b3718c0 Mon Sep 17 00:00:00 2001 From: Chris <125088905+wyattchris@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:38:37 -0400 Subject: [PATCH 6/8] refactor(styling-+-navigation): styling + navigation --- client/assets/arrow-left.svg | 4 +-- client/components/ChooseFileButton.tsx | 7 ++++- client/components/nav_buttons/BackButton.tsx | 4 +-- .../navigation/AppStackBottomTabNavigator.tsx | 6 ++++ client/screens/FileUpload.tsx | 29 ++++++++++--------- client/screens/Profile/PatientView.tsx | 9 +++++- package.json | 6 ---- 7 files changed, 39 insertions(+), 26 deletions(-) delete mode 100644 package.json diff --git a/client/assets/arrow-left.svg b/client/assets/arrow-left.svg index 7a7d75d..54e84eb 100644 --- a/client/assets/arrow-left.svg +++ b/client/assets/arrow-left.svg @@ -1,4 +1,4 @@ - - + + diff --git a/client/components/ChooseFileButton.tsx b/client/components/ChooseFileButton.tsx index b0c7354..5f0db8b 100644 --- a/client/components/ChooseFileButton.tsx +++ b/client/components/ChooseFileButton.tsx @@ -1,13 +1,18 @@ import React from 'react'; import { Text, TouchableOpacity } from 'react-native'; +import Upload from '../assets/profile/upload.svg'; + export function ChooseFileButton({ onPress }: { onPress: () => void }) { return ( - Choose File + + + CHOOSE FILE + ); } diff --git a/client/components/nav_buttons/BackButton.tsx b/client/components/nav_buttons/BackButton.tsx index a73de7b..1ce4968 100644 --- a/client/components/nav_buttons/BackButton.tsx +++ b/client/components/nav_buttons/BackButton.tsx @@ -11,9 +11,9 @@ export function BackButton() { return ( } + icon={() => } onPress={() => navigation.goBack()} /> ); diff --git a/client/navigation/AppStackBottomTabNavigator.tsx b/client/navigation/AppStackBottomTabNavigator.tsx index 44a4d6d..0efe99d 100644 --- a/client/navigation/AppStackBottomTabNavigator.tsx +++ b/client/navigation/AppStackBottomTabNavigator.tsx @@ -9,6 +9,7 @@ import Calendar from '../assets/bottom-nav/calendar.svg'; import HomeIcon from '../assets/bottom-nav/home.svg'; import User from '../assets/bottom-nav/user.svg'; import TimelineCalendarScreen from '../screens/Calendar'; +import FileUploadScreen from '../screens/FileUpload'; import Home from '../screens/Home'; import PatientView from '../screens/Profile/PatientView'; import Profile from '../screens/Profile/Profile'; @@ -89,6 +90,11 @@ export function ProfileNavigation() { options={{ headerShown: false }} component={Settings} /> + ); } diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index 77d0edd..abb3bfb 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -90,31 +90,31 @@ export default function FileUploadScreen() { // TODO: Choosefile border color, dropdown styling, fonts, choose file svg return ( - - + + - - - File Upload + + + Upload File - - File Title + + FILE TITLE - - File Label + + FILE LABEL - - Additional Notes + + ADDITIONAL NOTES - + Submit diff --git a/client/screens/Profile/PatientView.tsx b/client/screens/Profile/PatientView.tsx index 3eb90ea..afdbb25 100644 --- a/client/screens/Profile/PatientView.tsx +++ b/client/screens/Profile/PatientView.tsx @@ -1,12 +1,15 @@ import React from 'react'; import { Pressable, Text, View } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; + import FileDock from '../../assets/profile/filedock.svg'; import Upload from '../../assets/profile/upload.svg'; import { Header } from '../../components/profile/Header'; import { HealthStats } from '../../components/profile/HealthStats'; import { PatientHeader } from '../../components/profile/PatientHeader'; import { useCareWalletContext } from '../../contexts/CareWalletContext'; +import { AppStackNavigation } from '../../navigation/types'; import { useGroup } from '../../services/group'; import { useUsers } from '../../services/user'; import { Role } from '../../types/group'; @@ -16,6 +19,7 @@ export default function PatientView() { const { roles } = useGroup(group.groupID); const { users } = useUsers(roles?.map((role) => role.user_id) ?? []); const patientId = roles?.find((role) => role.role === Role.PATIENT)?.user_id; + const navigation = useNavigation(); return ( @@ -31,7 +35,10 @@ export default function PatientView() { - + navigation.navigate('FileUploadScreen')} + className="ml-10 mt-5 flex h-10 w-[38vw] flex-row items-center justify-center space-x-2 rounded-lg border border-carewallet-lightgray bg-carewallet-white" + > Upload Files diff --git a/package.json b/package.json deleted file mode 100644 index 284b17d..0000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "@expo-google-fonts/manrope": "^0.2.3", - "@expo-google-fonts/montserrat": "^0.2.3" - } -} From e617922e89545d4ef5bf781b0f9154a83cfa73d3 Mon Sep 17 00:00:00 2001 From: Matt McCoy Date: Wed, 3 Apr 2024 19:11:18 -0400 Subject: [PATCH 7/8] fix: show image on select, virtualized list error --- client/assets/filledarrowdown.svg | 3 ++ client/components/ChooseFileButton.tsx | 40 +++++++++++++--- client/components/Dropdown.tsx | 64 ++++++++++++++++++++++++++ client/screens/FileUpload.tsx | 44 ++++++------------ 4 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 client/assets/filledarrowdown.svg create mode 100644 client/components/Dropdown.tsx diff --git a/client/assets/filledarrowdown.svg b/client/assets/filledarrowdown.svg new file mode 100644 index 0000000..f9611b3 --- /dev/null +++ b/client/assets/filledarrowdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/components/ChooseFileButton.tsx b/client/components/ChooseFileButton.tsx index 5f0db8b..d317ddb 100644 --- a/client/components/ChooseFileButton.tsx +++ b/client/components/ChooseFileButton.tsx @@ -1,18 +1,44 @@ import React from 'react'; -import { Text, TouchableOpacity } from 'react-native'; +import { Dimensions, Image, Text, TouchableOpacity, View } from 'react-native'; + +import { clsx } from 'clsx'; +import { DocumentPickerAsset } from 'expo-document-picker'; import Upload from '../assets/profile/upload.svg'; -export function ChooseFileButton({ onPress }: { onPress: () => void }) { +export function ChooseFileButton({ + onPress, + picked +}: { + onPress: () => void; + picked: DocumentPickerAsset | null; +}) { return ( - - - CHOOSE FILE - + {picked ? ( + + + + ) : ( + <> + + + CHOOSE FILE + + + )} ); } diff --git a/client/components/Dropdown.tsx b/client/components/Dropdown.tsx new file mode 100644 index 0000000..02461bc --- /dev/null +++ b/client/components/Dropdown.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import { Text, View } from 'react-native'; + +import { clsx } from 'clsx'; + +import ArrowDown from '../assets/filledarrowdown.svg'; + +export function CWDropdown({ + selected, + items, + setLabel +}: { + selected: string; + items?: string[]; + setLabel: (label: string) => void; +}) { + const [isOpen, setIsOpen] = useState(false); + return ( + + setIsOpen(!isOpen)} + > + + {selected} + + + {isOpen ? ( + + + + ) : ( + + )} + + + + {isOpen && ( + + {items?.map( + (item, index) => + item !== selected && ( + { + setLabel(item); + setIsOpen(false); + }} + > + + {item} + + + ) + )} + + )} + + ); +} diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index abb3bfb..8e5c548 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -8,9 +8,9 @@ import { } from 'react-native'; import { DocumentPickerAsset, getDocumentAsync } from 'expo-document-picker'; -import DropDownPicker from 'react-native-dropdown-picker'; import { ChooseFileButton } from '../components/ChooseFileButton'; +import { CWDropdown } from '../components/Dropdown'; import { BackButton } from '../components/nav_buttons/BackButton'; import { useCareWalletContext } from '../contexts/CareWalletContext'; import { useFile } from '../services/file'; @@ -19,7 +19,6 @@ import { getLabelsByGroup } from '../services/task'; export default function FileUploadScreen() { const { user, group } = useCareWalletContext(); const { uploadFileMutation } = useFile(); - const [open, setOpen] = useState(false); const [fileTitle, setFileTitle] = useState(''); const [label, setLabel] = useState('Financial'); const [additionalNotes, setAdditionalNotes] = useState(''); @@ -27,7 +26,6 @@ export default function FileUploadScreen() { null ); const [allLabels, setAllLabels] = useState([]); // Store all unique labels as a list of strings - DropDownPicker.setTheme('DARK'); // Fetch all labels by the group id useEffect(() => { @@ -87,10 +85,9 @@ export default function FileUploadScreen() { } }; - // TODO: Choosefile border color, dropdown styling, fonts, choose file svg return ( - - + + @@ -99,7 +96,7 @@ export default function FileUploadScreen() { - + @@ -113,36 +110,21 @@ export default function FileUploadScreen() { /> - + FILE LABEL - ({ - label, - value: label - }))} - setOpen={setOpen} - setValue={setLabel} - placeholder="Select Label" - style={{ - backgroundColor: '#1A56C4', - borderColor: 'transparent' - }} - textStyle={{ - fontFamily: 'Manrope_400Regular', - color: 'white' - }} - disabledStyle={{ - opacity: 0.5 - }} - /> + + + - + ADDITIONAL NOTES Date: Wed, 3 Apr 2024 19:38:40 -0400 Subject: [PATCH 8/8] fix: allow a label to not have to be selected --- backend/db/migrations/5.files.sql | 5 ++--- client/screens/FileUpload.tsx | 36 ++++++++++--------------------- client/services/label.ts | 27 +++++++++++++++++++++++ client/services/task.ts | 11 ++-------- 4 files changed, 42 insertions(+), 37 deletions(-) create mode 100644 client/services/label.ts diff --git a/backend/db/migrations/5.files.sql b/backend/db/migrations/5.files.sql index 0c7ac28..ae14204 100644 --- a/backend/db/migrations/5.files.sql +++ b/backend/db/migrations/5.files.sql @@ -9,10 +9,9 @@ CREATE TABLE IF NOT EXISTS files ( file_size integer NOT NULL, task_id integer, notes varchar, - label_name varchar NOT NULL, + label_name varchar, PRIMARY KEY (file_id), FOREIGN KEY (group_id) REFERENCES care_group (group_id), FOREIGN KEY (upload_by) REFERENCES users (user_id), - FOREIGN KEY (task_id) REFERENCES task (task_id), - FOREIGN KEY (group_id, label_name) REFERENCES label (group_id, label_name) + FOREIGN KEY (task_id) REFERENCES task (task_id) ); diff --git a/client/screens/FileUpload.tsx b/client/screens/FileUpload.tsx index 8e5c548..6ed2249 100644 --- a/client/screens/FileUpload.tsx +++ b/client/screens/FileUpload.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { ScrollView, Text, @@ -14,37 +14,18 @@ import { CWDropdown } from '../components/Dropdown'; import { BackButton } from '../components/nav_buttons/BackButton'; import { useCareWalletContext } from '../contexts/CareWalletContext'; import { useFile } from '../services/file'; -import { getLabelsByGroup } from '../services/task'; +import { useLabelsByGroup } from '../services/label'; export default function FileUploadScreen() { const { user, group } = useCareWalletContext(); const { uploadFileMutation } = useFile(); const [fileTitle, setFileTitle] = useState(''); - const [label, setLabel] = useState('Financial'); + const [label, setLabel] = useState('Select Label'); const [additionalNotes, setAdditionalNotes] = useState(''); const [pickedFile, setPickedFile] = useState( null ); - const [allLabels, setAllLabels] = useState([]); // Store all unique labels as a list of strings - - // Fetch all labels by the group id - useEffect(() => { - const fetchLabels = async () => { - try { - console.log('Group:', group); - const labels = await getLabelsByGroup(group.groupID); - const uniqueLabels = Array.from( - new Set(labels.map((label) => label.label_name)) - ); - setAllLabels(uniqueLabels); - console.log('Labels:', allLabels); - } catch (error) { - console.error('Error fetching labels:', error); - } - }; - - fetchLabels(); - }, [group]); + const { labels } = useLabelsByGroup(group.groupID); const handleFileTitleChange = (text: string) => { setFileTitle(text); @@ -76,9 +57,14 @@ export default function FileUploadScreen() { file: pickedFile, userId: user.userID, groupId: group.groupID, - label: label, + label: label === 'Select Label' ? '' : label, notes: additionalNotes }); + + setFileTitle(''); + setLabel('Select Label'); + setAdditionalNotes(''); + setPickedFile(null); } } catch (err) { console.log('err', err); @@ -116,7 +102,7 @@ export default function FileUploadScreen() { label.label_name)} setLabel={setLabel} /> diff --git a/client/services/label.ts b/client/services/label.ts new file mode 100644 index 0000000..ae15706 --- /dev/null +++ b/client/services/label.ts @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; + +import { TaskLabel } from '../types/label'; +import { api_url } from './api-links'; + +// This is used to get unique labels used by a group it should not be used for fetching labels for a task or other items +const getLabelsByGroup = async (groupID: number): Promise => { + const { data } = await axios.get(`${api_url}/group/${groupID}/labels`); + + const uniques = (data as TaskLabel[]).filter((item, index, self) => { + return ( + self.findIndex((obj) => obj.label_name === item.label_name) === index + ); + }); + + return uniques; +}; + +export const useLabelsByGroup = (groupID: number) => { + const { data: labels, isLoading: labelsIsLoading } = useQuery({ + queryKey: ['labels', groupID], + queryFn: () => getLabelsByGroup(groupID) + }); + + return { labels, labelsIsLoading }; +}; diff --git a/client/services/task.ts b/client/services/task.ts index 367593f..44dcdb8 100644 --- a/client/services/task.ts +++ b/client/services/task.ts @@ -21,13 +21,6 @@ const getTask = async (taskID: string): Promise => { return data; }; -export const getLabelsByGroup = async ( - groupID: number -): Promise => { - const { data } = await axios.get(`${api_url}/group/${groupID}/labels`); - return data; -}; - const getTaskByAssigned = async (userId: string): Promise => { const { data } = await axios.get( `${api_url}/tasks/assigned?userIDs=${userId}` @@ -112,7 +105,7 @@ export const addNewTaskMutation = () => { const { mutate: addTaskMutation } = useMutation({ mutationFn: (newTask: Task) => addNewTask(newTask), onSuccess: () => { - queryClient.invalidateQueries('filteredTaskList'); + queryClient.invalidateQueries({ queryKey: ['filteredTaskList'] }); }, onError: (err) => { console.error('ERROR: Failed to Add Task. Code:', err); @@ -134,7 +127,7 @@ export const editTaskMutation = () => { updatedTask: Task; }) => editTask(taskId, updatedTask), onSuccess: () => { - queryClient.invalidateQueries('filteredTaskList'); + queryClient.invalidateQueries({ queryKey: ['filteredTaskList'] }); }, onError: (err) => { console.error('ERROR: Failed to Edit Task. Code:', err);