diff --git a/backendAWS/contacts.http b/backendAWS/contacts.http index c1111eefe3998094bd73e534d3e99f48a4197b48..702b48249bc642dfeef66d75d964a97fd8def457 100644 --- a/backendAWS/contacts.http +++ b/backendAWS/contacts.http @@ -1,6 +1,6 @@ @url = https://ntwa190r07.execute-api.eu-north-1.amazonaws.com/prod/ -@token = eyJraWQiOiJ4c0pOcXpNejlQZkczWkpcL3V6bVZZcUZrbTM5SUNlekFzTXlGMDAzWjFSYz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzMjFlZjFkYi1hYzRjLTQ4OTctYjg5Yi0yZmRiMWM3MTU5NGEiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmV1LW5vcnRoLTEuYW1hem9uYXdzLmNvbVwvZXUtbm9ydGgtMV9FblZzZ01VcnYiLCJjb2duaXRvOnVzZXJuYW1lIjoiMzIxZWYxZGItYWM0Yy00ODk3LWI4OWItMmZkYjFjNzE1OTRhIiwib3JpZ2luX2p0aSI6IjQ2NzA0NjBjLTAzYmItNGM1Yi04YjE4LTM5NzBiMzgzNmIxMCIsImF1ZCI6IjI2azNtaHA4aWl0cDVvb25nMmVhNjdnM2xkIiwiZXZlbnRfaWQiOiJiYzRhMGFhMi1lYzkyLTRhYzMtYWE5NC1hZTc3ZmIwMzM0OTkiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcwMzQ5Njc3NSwiZXhwIjoxNzAzNTAwMzc1LCJpYXQiOjE3MDM0OTY3NzUsImp0aSI6ImY2ZTljODdlLTBjODItNDVlMy04MTc5LTY1MzMyYWI4MTM1NiIsImVtYWlsIjoic3RlZmFuLnBhbmR6aWNAZW5saWdodGl0LnJzIn0.X9Ra9vABMbwvKob14sGEa-eM54X8YVU--T7kzWrOgMOr8nWLRUe2s6DHXHez5PZARgXr_KAJViP2PMQtLKRShJQHg0Huj4DEai_bU16cVi5oWaCAhFGhU8lDt5GDA72-BsIk5liPA0PiO3ndjyQ2cjHm2uvJJzqle07jJwI-IYy7g0V8Yy2Zc67xIMZKNlKenHEAS3TuyVOnAjiUrh6Ldl7X_5x7w_xJq4yML0Odh9l__YS5Bj7gOPyqs6K2Vu_zDI4JoPij09adupwTDhQRBHvPQqSYqk4pW-pm7akcOroaNIy6Odo43g-Cfit08ddvucHCYjpXjbMKAk-3qQBOHA +@token = eyJraWQiOiJ4c0pOcXpNejlQZkczWkpcL3V6bVZZcUZrbTM5SUNlekFzTXlGMDAzWjFSYz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzMjFlZjFkYi1hYzRjLTQ4OTctYjg5Yi0yZmRiMWM3MTU5NGEiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmV1LW5vcnRoLTEuYW1hem9uYXdzLmNvbVwvZXUtbm9ydGgtMV9FblZzZ01VcnYiLCJjb2duaXRvOnVzZXJuYW1lIjoiMzIxZWYxZGItYWM0Yy00ODk3LWI4OWItMmZkYjFjNzE1OTRhIiwib3JpZ2luX2p0aSI6IjgxZDAyYzk4LTZlNmMtNGFlZC04MjVmLTRiMTBhYzViMTJlYiIsImF1ZCI6IjI2azNtaHA4aWl0cDVvb25nMmVhNjdnM2xkIiwiZXZlbnRfaWQiOiI3ZTI5NjQ3NS0wZWRhLTQ4NzgtYjE3NS02Y2Q4ZGMzYjRiNTEiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcwMzUwNTg2NSwiZXhwIjoxNzAzNTA5NDY1LCJpYXQiOjE3MDM1MDU4NjUsImp0aSI6IjY0ODg3NzBjLTg1MTQtNDg5MS1hMDU4LTEwNjViYTY2ZjliYiIsImVtYWlsIjoic3RlZmFuLnBhbmR6aWNAZW5saWdodGl0LnJzIn0.dpO1UmtwFW094ZuEBsQZoP8TxnlJqAJ8gYHJ-zLe-rkqagk3F_VWgu_5OULPGV0OqyDjOUVPX13nwGC5Bbe9mf6GqL_GOWtXoZLVpGRI0JLxV0uAIOKk6ajyKlzgLvrquoJm8AShV2HyJG0ejZCCVazirwTv_ljStaySFv8riRmteh1ZfOspD8Zj1fGbRHnY0qNoY7WhYV8pZwtTchvhtxP16HPvukrV-x2UhqVKzxqlTGerLpluUbzNYNmEJmajW0YrQXmqxE-lwm-T362ozUTc6gopUA91nGMkXPTPBpdGCaO6prrcnBOtsEJYMv8xhdlnmLXylrX3Qb3iIIOcYg //Get All Contacts GET {{url}}/contacts Authorization: {{token}} @@ -17,7 +17,7 @@ content-type: application/json Authorization: {{token}} { - "name": "Stefan Pandzic", + "name": "HHHH Pandzic", "image": "https://robohash.org/bobjohnson.png", "email": "pandzic.johnson@example.com", "phoneNumber": "+1122334455", diff --git a/frontend/src/__mocks__/mockedContacts.json b/frontend/src/__mocks__/mockedContacts.json deleted file mode 100644 index 3097c224af073a1d2c00397e3e51dcfc4cce94a1..0000000000000000000000000000000000000000 --- a/frontend/src/__mocks__/mockedContacts.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "_id": "1", - "image": "https://robohash.org/johndoe.png", - "name": "Work Contact: John Doe", - "email": "john.doe@example.com", - "phoneNumber": "+1234567890", - "isFavorite": true, - "labels": [{ "_id": "l1", "labelName": "work" }] - }, - { - "_id": "2", - "image": "https://robohash.org/alicesmith.png", - "name": "Work Contact: Alice Smith", - "email": "alice.smith@example.com", - "phoneNumber": "+9876543210", - "isFavorite": false, - "labels": [{ "_id": "l1", "labelName": "work" }] - }, - { - "_id": "3", - "image": "https://robohash.org/bobjohnson.png", - "name": "Work Contact: Bob Johnson", - "email": "bob.johnson@example.com", - "phoneNumber": "+1122334455", - "isFavorite": true, - "labels": [{ "_id": "l1", "labelName": "work" }] - }, - { - "_id": "4", - "image": "https://robohash.org/evawilliams.png", - "name": "Family Member: Eva Williams", - "email": "eva.williams@example.com", - "phoneNumber": "+9988776655", - "isFavorite": false, - "labels": [{ "_id": "l2", "labelName": "family" }] - }, - { - "_id": "5", - "image": "https://robohash.org/mikebrown.png", - "name": "Family Member: Mike Brown", - "email": "mike.brown@example.com", - "phoneNumber": "+7654321098", - "isFavorite": true, - "labels": [{ "_id": "l2", "labelName": "family" }] - }, - { - "_id": "6", - "image": "https://robohash.org/sarahjohnson.png", - "name": "Family Member: Sarah Johnson", - "email": "sarah.johnson@example.com", - "phoneNumber": "+1122336677", - "isFavorite": false, - "labels": [{ "_id": "l2", "labelName": "family" }] - }, - { - "_id": "7", - "image": "https://robohash.org/davidwilson.png", - "name": "My Best Friend: David Wilson", - "email": "david.wilson@example.com", - "phoneNumber": "+8877665544", - "isFavorite": true, - "labels": [{ "_id": "l3", "labelName": "friends" }] - }, - { - "_id": "8", - "image": "https://robohash.org/lindadavis.png", - "name": "My Best Friend: Linda Davis", - "email": "linda.davis@example.com", - "phoneNumber": "+4455667788", - "isFavorite": false, - "labels": [{ "_id": "l3", "labelName": "friends" }] - }, - { - "_id": "9", - "image": "https://robohash.org/kevinmoore.png", - "name": "My Best Friend: Kevin Moore", - "email": "kevin.moore@example.com", - "phoneNumber": "+1122998877", - "isFavorite": true, - "labels": [{ "_id": "l3", "labelName": "friends" }] - }, - { - "_id": "10", - "image": "https://robohash.org/emilywhite.png", - "name": "Work Contact: Emily White", - "email": "emily.white@example.com", - "phoneNumber": "+5544332211", - "isFavorite": false, - "labels": [{ "_id": "l1", "labelName": "work" }] - } -] diff --git a/frontend/src/__mocks__/mockedLabels.json b/frontend/src/__mocks__/mockedLabels.json deleted file mode 100644 index 5d956d5541aa4debad539558061b1efd08d3c80e..0000000000000000000000000000000000000000 --- a/frontend/src/__mocks__/mockedLabels.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "_id": "l1", - "labelName": "work" - }, - { - "_id": "l2", - "labelName": "family" - }, - { - "_id": "l3", - "labelName": "friends" - } -] diff --git a/frontend/src/__mocks__/types.ts b/frontend/src/__mocks__/types.ts deleted file mode 100644 index 7f32c078497e1382f540c90bd44048a2f94ef4de..0000000000000000000000000000000000000000 --- a/frontend/src/__mocks__/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type SingleContact = { - _id: string; - image: string; - name: string; - email: string; - phoneNumber: string; - isFavorite: boolean; - labels: Label[]; -}; - -export type Label = { - _id: string; - labelName: string; -}; - -export type SingeContactFormValues = Omit<SingleContact, "_id">; diff --git a/frontend/src/api/asyncFunctions/contacts/index.tsx b/frontend/src/api/asyncFunctions/contacts/index.tsx index 214e67ddbce2cfb4788ee19964943151f1c43a61..65232d7d38abcac293543dfba8b5e4f87c9ab179 100644 --- a/frontend/src/api/asyncFunctions/contacts/index.tsx +++ b/frontend/src/api/asyncFunctions/contacts/index.tsx @@ -1,10 +1,11 @@ import { API } from "aws-amplify"; import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query/fetchBaseQuery"; import { apiName, resourcePath } from "../../../constants"; +import { ContactEntry, UnsavedContactEntry } from "types"; export async function getAllContacts( jwtToken: string -): Promise<{ data: any } | { error: FetchBaseQueryError }> { +): Promise<{ data: ContactEntry[] } | { error: FetchBaseQueryError }> { try { const data = await API.get(apiName, `${resourcePath}`, { headers: { @@ -25,7 +26,7 @@ export async function getAllContacts( export async function getContactById( contactId: string, jwtToken: string -): Promise<{ data: any } | { error: FetchBaseQueryError }> { +): Promise<{ data: ContactEntry } | { error: FetchBaseQueryError }> { try { const data = await API.get(apiName, `${resourcePath}?id=${contactId}`, { headers: { @@ -42,3 +43,22 @@ export async function getContactById( }; } } + +export async function createContact( + contact: UnsavedContactEntry, + jwtToken: string +): Promise<{ data: any } | { error: FetchBaseQueryError }> { + try { + const data = await API.post(apiName, `${resourcePath}`, { + headers: { + Authorization: `Bearer ${jwtToken}`, + }, + body: contact, + }); + return { data: data }; + } catch (error) { + return { + error: error as FetchBaseQueryError, + }; + } +} diff --git a/frontend/src/api/contacts/index.tsx b/frontend/src/api/contacts/index.tsx index 0d77d849a2bbda88b0fd7737f034d6f7abb3db24..40fb362becb53356f524334a159fb022074a76f0 100644 --- a/frontend/src/api/contacts/index.tsx +++ b/frontend/src/api/contacts/index.tsx @@ -1,7 +1,7 @@ import { api } from "api"; import { - /* createContact, - deleteImageFromS3, + createContact, + /*deleteImageFromS3, deleteManyContacts, deleteOneContact, editContact, */ @@ -26,6 +26,13 @@ const contactsApiEndpoints = api.injectEndpoints({ }, providesTags: ["Contact"], }), + createContact: build.mutation({ + queryFn: async ({ contact, jwtToken }) => { + const result = await createContact(contact, jwtToken); + return result; + }, + invalidatesTags: ["Contact"], + }), }), }); @@ -70,8 +77,8 @@ const contactsApiEndpoints = api.injectEndpoints({ export const { useGetAllContactsQuery, useGetContactByIdQuery, - /*useCreateContactMutation, - useUploadImageToS3Mutation, + useCreateContactMutation, + /*useUploadImageToS3Mutation, useDeleteImageFromS3Mutation, useEditContactMutation, useDeleteOneContactMutation, diff --git a/frontend/src/pages/contactsForm/ContactForm.tsx b/frontend/src/pages/contactsForm/ContactForm.tsx index fe9b94bb6220ed970879008598fc15cc71ab0d9c..76a2641fc16b7696059408f174e7bac817afde9e 100644 --- a/frontend/src/pages/contactsForm/ContactForm.tsx +++ b/frontend/src/pages/contactsForm/ContactForm.tsx @@ -16,7 +16,7 @@ import InputLabel from "@mui/material/InputLabel"; import Resizer from "react-image-file-resizer"; import FormProvider from "components/hook-form/FormProvider"; -import { SingeContactFormValues } from "__mocks__/types"; +import { SingeContactFormValues } from "types"; import mockedLabels from "__mocks__/mockedLabels.json"; import RHFUploadAvatar from "components/hook-form/RHFUpload"; import { extractExtensions } from "utils/extractExtensions"; @@ -70,7 +70,7 @@ export default function ContactForm({ title, value, onSubmit }: Props) { name: value?.name || "", email: value?.email || "", phoneNumber: value?.phoneNumber || "", - isFavorite: value?.isFavorite || false, + /* isFavorite: value?.isFavorite || false, */ labels: value?.labels || [], }, }); @@ -146,14 +146,14 @@ export default function ContactForm({ title, value, onSubmit }: Props) { )} /> </Stack> - <FormControl> + {/* <FormControl> <InputLabel id="select-label">Labels</InputLabel> <Controller name="labels" control={control} render={({ field, fieldState: { error } }) => { const selectedValues = field.value - ? field.value.map((item) => item._id) + ? field.value.map((item) => item.id) : []; const handleChange = (e: any) => { @@ -197,7 +197,7 @@ export default function ContactForm({ title, value, onSubmit }: Props) { ); }} /> - </FormControl> + </FormControl> */} </Stack> </Stack> <Stack direction="row" gap={2}> diff --git a/frontend/src/pages/contactsForm/CreateContact.tsx b/frontend/src/pages/contactsForm/CreateContact.tsx index 2cd1c64b1472f906eca8562d1632b5d03d10e3bf..4cdafb78ada6d2defce7e13466dbd5a545b49cc1 100644 --- a/frontend/src/pages/contactsForm/CreateContact.tsx +++ b/frontend/src/pages/contactsForm/CreateContact.tsx @@ -1,10 +1,23 @@ import Page from "components/Page"; import ContactForm from "./ContactForm"; -import { SingeContactFormValues } from "__mocks__/types"; +import { SingeContactFormValues, UnsavedContactEntry } from "types"; +import { useCreateContactMutation } from "api/contacts"; +import { useAppSelector } from "hooks/storeHooks"; export default function CreateContact() { + const [createContact, { data, isLoading, isError }] = + useCreateContactMutation(); + + const jwtToken = useAppSelector( + (state) => state.user.authUser.signInUserSession.idToken.jwtToken + ); + const handleCreate = (value: SingeContactFormValues) => { - console.log("Create contact:", value); + const unsavedContact: UnsavedContactEntry = { + ...value, + isFavorite: false, + }; + createContact({ contact: unsavedContact, jwtToken: jwtToken }); }; return ( <Page title="Create new contact"> diff --git a/frontend/src/pages/contactsForm/EditContact.tsx b/frontend/src/pages/contactsForm/EditContact.tsx index a67e25de6688df3a85abf8130b97a0d9debd9c2c..6fd780ed2c7fc44a4f26967b1124fba43e6f2e51 100644 --- a/frontend/src/pages/contactsForm/EditContact.tsx +++ b/frontend/src/pages/contactsForm/EditContact.tsx @@ -2,10 +2,11 @@ import { useParams } from "react-router-dom"; import Page from "components/Page"; import ContactForm from "./ContactForm"; -import { SingeContactFormValues } from "__mocks__/types"; + import { useAppSelector } from "hooks/storeHooks"; import { useGetContactByIdQuery } from "api/contacts"; import { skipToken } from "@reduxjs/toolkit/query"; +import { SingeContactFormValues } from "types"; export default function EditContact() { const { id } = useParams(); @@ -20,16 +21,31 @@ export default function EditContact() { }); const handleEdit = (value: SingeContactFormValues) => { - console.log("Edit contact:", data); + console.log("Edit contact:", value); }; + if (isLoading) { + return <>Loading</>; + } + + if (!data) { + return <>No Data</>; + } + return ( <Page title="Edit contact"> - {isLoading ? ( - <></> - ) : ( - <ContactForm title="Edit contact" onSubmit={handleEdit} value={data} /> - )} + <ContactForm + title="Edit contact" + onSubmit={handleEdit} + value={{ + name: data.name, + email: data.email, + phoneNumber: data.phoneNumber, + //labels: data?., + image: data?.image, + /* imageKey: data.imageKey, */ + }} + /> </Page> ); } diff --git a/frontend/src/pages/contactsList/index.tsx b/frontend/src/pages/contactsList/index.tsx index 53e9b556311be54351357e932f720c2480043bf1..9fc7808b35346c1e24adbef2a9a132a67c4b891d 100644 --- a/frontend/src/pages/contactsList/index.tsx +++ b/frontend/src/pages/contactsList/index.tsx @@ -70,8 +70,6 @@ export default function AllContacts() { handleEdit, }); - console.log(data); - return ( <Page title="All contacts"> <Card sx={{ m: 5 }}> diff --git a/frontend/src/types.ts b/frontend/src/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..928bb1d226663609b9b290a833316ff760858b8f --- /dev/null +++ b/frontend/src/types.ts @@ -0,0 +1,21 @@ +export type ContactEntry = { + id: string; + name: string; + email: string; + image?: string; + phoneNumber?: string; + isFavorite: boolean; + //labels: [{ "_id": "l1", "labelName": "work" }] +}; + +export type UnsavedContactEntry = Omit<ContactEntry, "id">; + +export type SingeContactFormValues = { + name: string; + email: string; + phoneNumber?: string; + labels?: string[]; + image?: string; + imageKey?: string; + imageForUpload?: File; +};