Skip to content

Commit

Permalink
Add new Places
Browse files Browse the repository at this point in the history
  • Loading branch information
Prabhat Pal committed Feb 3, 2021
1 parent 46001c1 commit 73c20df
Show file tree
Hide file tree
Showing 24 changed files with 577 additions and 41 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ FIREBASE_PRIVATE_KEY=""
CLOUDINARY_SECRET=""
NEXT_PUBLIC_CLOUDINARY_KEY=""
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=""
NEXT_PUBLIC_CLOUDINARY_API_BASE_URL=""

DATABASE_URL=""
3 changes: 2 additions & 1 deletion env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare namespace NodeJS {
CLOUDINARY_SECRET: string;
NEXT_PUBLIC_CLOUDINARY_KEY: string;
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME: string;
NEXT_PUBLIC_CLOUDINARY_API_BASE_URL: string;
DATABASE_URL: string;
}
}
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"codegen": "apollo client:codegen --target typescript --localSchemaFile schema.gql --outputFlat --includes \"{pages,src}/**\" --excludes \"src/generated/**\" src/generated",
"codegen": "apollo client:codegen --target typescript --localSchemaFile schema.gql --outputFlat --includes \"{pages,src}/gql/**\" --excludes \"src/generated/**\" src/generated",
"codegen:watch": "yarn codegen --watch",
"db:init": "yarn prisma init",
"db:generate": "yarn prisma generate",
Expand All @@ -19,6 +19,9 @@
"@hookform/resolvers": "1.3.0",
"@prisma/client": "2.12.0",
"apollo-server-micro": "^2.19.2",
"class-validator": "^0.13.1",
"cloudinary": "^1.24.0",
"cloudinary-react": "^1.6.8",
"firebase": "^8.2.4",
"firebase-admin": "^9.4.2",
"framer-motion": "^3.2.2-rc.1",
Expand All @@ -31,6 +34,7 @@
"react-icons": "^4.1.0",
"react-map-gl": "5.2.10",
"react-select": "^4.0.2",
"react-toast-notifications": "^2.4.0",
"reflect-metadata": "^0.1.13",
"sass": "^1.32.5",
"type-graphql": "^1.1.1",
Expand All @@ -50,6 +54,7 @@
"@types/react-dom": "^17.0.0",
"@types/react-map-gl": "^5.2.9",
"@types/react-select": "^4.0.8",
"@types/react-toast-notifications": "^2.4.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/eslint-plugin-tslint": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
Expand Down
13 changes: 10 additions & 3 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { NextComponentType, NextPageContext } from "next";
import { Router } from "next/router";
import { ApolloProvider } from "@apollo/client";
import { useApollo } from "src/apollo";
import { ToastProvider } from "react-toast-notifications";

type additionalType = {
title: string;
Expand All @@ -33,9 +34,15 @@ function MyApp({ Component, pageProps }: AppProps) {
</Head>
<AuthProvider>
<ApolloProvider client={client}>
<Layout withMapView={Component.withMapView}>
<Component {...pageProps} />
</Layout>
<ToastProvider
autoDismiss={true}
autoDismissTimeout={4000}
transitionDuration={150}
>
<Layout withMapView={Component.withMapView}>
<Component {...pageProps} />
</Layout>
</ToastProvider>
</ApolloProvider>
</AuthProvider>
</>
Expand Down
163 changes: 143 additions & 20 deletions pages/places/add.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ import * as Yup from "yup";
import LocationSearch from "src/components/LocationSearch";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { useMutation } from "@apollo/client";
import {
CREATE_IMAGE_SIGNATURE_MUTATION,
CREATE_PLACE_MUTATION,
} from "src/gql";
import { CreateSignatureMutation } from "src/generated/CreateSignatureMutation";
import { CreatePlaceMutation } from "src/generated/CreatePlaceMutation";
import { useToasts } from "react-toast-notifications";

interface IFormData {
placeName: string;
placeType: string;
description: string;
address: string;
lat: number;
lng: number;
images: FileList;
latitude: number;
longitude: number;
image: FileList;
}

const validationSchema = Yup.object({
Expand All @@ -31,15 +39,15 @@ const validationSchema = Yup.object({
"Please provide a description for this place"
),
address: Yup.string().required("Please select an address"),
lat: Yup.number()
latitude: Yup.number()
.required("Please select an address")
.min(-90, "")
.max(90, ""),
lng: Yup.number()
longitude: Yup.number()
.required("Please select an address")
.min(-180, "")
.max(180, ""),
images: Yup.mixed()
image: Yup.mixed()
.required("Please add an image")
.test("fileRequired", "Please add an image", (value) => {
return value.length > 0;
Expand All @@ -56,10 +64,46 @@ const validationSchema = Yup.object({
return value?.type?.match(/^image\/.*$/);
}),
});
interface IUploadImageResponse {
secure_url?: string;
error?: {
message: string;
};
}

const uploadImage = async (
image: File,
signature: string,
timestamp: number
): Promise<IUploadImageResponse> => {
const url = `${process.env.NEXT_PUBLIC_CLOUDINARY_API_BASE_URL}/upload`;

const formData = new FormData();
formData.append("file", image);
formData.append("signature", signature);
formData.append("timestamp", timestamp.toString() + "sf");
formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY ?? "");

try {
const response = await fetch(url, {
method: "POST",
body: formData,
});
const data = await response.json();

if (data.error) {
throw new Error(data.error.message);
}
return data;
} catch (err) {
throw new Error(err.message ?? err);
}
};

const Add = () => {
const { addToast } = useToasts();
const [previewImage, setPreviewImage] = useState<string>("");

const [submitting, setSubmitting] = useState<boolean>(false);
const {
handleSubmit,
register,
Expand All @@ -73,17 +117,23 @@ const Add = () => {
mode: "onTouched",
});

const [createSignature] = useMutation<CreateSignatureMutation>(
CREATE_IMAGE_SIGNATURE_MUTATION
);

const [createPlace] = useMutation<CreatePlaceMutation>(CREATE_PLACE_MUTATION);

useEffect(() => {
register({ name: "address" });
register({ name: "lat" });
register({ name: "lng" });
register({ name: "latitude" });
register({ name: "longitude" });
register({ name: "placeType" });
}, [register]);

useEffect(() => {
const images = watch("images");
if (images.length > 0) {
const file = images[0] as File;
const image = watch("image");
if (image.length > 0) {
const file = image[0] as File;
const reader = new FileReader();
reader.onloadend = () => {
setPreviewImage(reader.result as string);
Expand All @@ -92,15 +142,88 @@ const Add = () => {
} else {
setPreviewImage("");
}
}, [watch("images")]);
}, [watch("image")]);

const onSubmit = async (data: IFormData) => {
setSubmitting(true);
try {
const {
data: signatureData,
errors: signatureErrors,
} = await createSignature();

if (signatureErrors && signatureErrors.length) {
signatureErrors.map((error) => {
throw new Error(
error.message ?? "Something went wrong! Please try again later"
);
});
}

if (signatureData) {
const { signature, timestamp } = signatureData.createImageSignature;
const imageData = await uploadImage(
data.image[0] as File,
signature,
timestamp
);

if (imageData.error) {
throw new Error(
imageData.error.message ??
"Something went wrong! Please try again later"
);
} else {
const { data: placeData, errors: placceErrors } = await createPlace({
variables: {
input: {
placeName: data.placeName,
placeType: data.placeType,
description: data.description,
address: data.address,
image: imageData.secure_url,
coordinates: {
latitude: data.latitude,
longitude: data.longitude,
},
},
},
});

if (placceErrors && placceErrors.length) {
placceErrors.map((error) => {
throw new Error(
error.message ?? "Something went wrong! Please try again later"
);
});
}

if (placeData?.createPlace?.id) {
addToast("Place added successfully", {
appearance: "success",
});
} else {
throw new Error("Something went wrong! Please try again later");
}
}
}
} catch (err) {
console.error(`😱: ${err}`);
addToast(err.message ?? "Something went wrong! Please try again later.", {
appearance: "error",
});
} finally {
setSubmitting(false);
}
};

return (
<div
className="overflow-y-scroll pb-8"
style={{ height: "calc(100vh - 75px)" }}
>
<Form
onSubmit={handleSubmit((data) => console.log("data", data))}
onSubmit={handleSubmit(onSubmit)}
className="mx-auto flex flex-col items-center"
style={{ maxWidth: "480px" }}
register={register}
Expand All @@ -114,21 +237,21 @@ const Add = () => {
shouldValidate: true,
shouldDirty: true,
});
setValue("lat", lat, {
setValue("latitude", lat, {
shouldValidate: true,
shouldDirty: true,
});
setValue("lng", lng, {
setValue("longitude", lng, {
shouldValidate: true,
shouldDirty: true,
});
}}
error={errors?.address || errors?.lat || errors?.lng}
error={errors?.address || errors?.latitude || errors?.longitude}
/>
<FileInput
name="images"
name="image"
label="Click to add image"
onBlur={() => trigger("images")}
onBlur={() => trigger("image")}
/>
{previewImage ? (
<img
Expand All @@ -154,7 +277,7 @@ const Add = () => {
/>
<TextArea name="description" placeholder="About this place" />

<Button type="submit" className="mt-4 w-full">
<Button type="submit" className="mt-4 w-full" disabled={submitting}>
Submit
</Button>
</Form>
Expand Down
36 changes: 36 additions & 0 deletions schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,42 @@
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

input Coordinates {
latitude: Float!
longitude: Float!
}

type ImageSignature {
signature: String!
timestamp: Int!
}

type Mutation {
createImageSignature: ImageSignature!
createPlace(input: PlaceInput!): Place
}

type Place {
address: String!
description: String!
id: ID!
image: String!
latitude: Int!
longitude: Int!
placeName: String!
placeType: String!
publicId: String!
}

input PlaceInput {
address: String!
coordinates: Coordinates!
description: String!
image: String!
placeName: String!
placeType: String!
}

type Query {
hello: String!
}
4 changes: 2 additions & 2 deletions src/components/LocationSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ const PlacesAutoComplete: FC<ILocationSearch> = ({
const { lat, lng } = await getLatLng(results[0]);

onSelectAddress({ address, lat, lng });
} catch (error) {
console.error(`😱 Error:`, error);
} catch (err) {
console.error(`😱: ${err}`);
}
} else {
clearSuggestions();
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const Button: FC<IButton> = ({
rel,
...props
}) => {
const btnClass = `border-none bg-purple-700 hover:bg-purple-800 transition duration-150 ease text-white px-5 py-3 rounded-md select-none outline-none focus:outline-none active:bg-purple-900 ${className}`;
const btnClass = `border-none bg-purple-700 hover:bg-purple-800 transition duration-150 ease text-white px-5 py-3 rounded-md select-none outline-none focus:outline-none active:bg-purple-900 disabled:bg-purple-400 disabled:cursor-not-allowed ${className}`;

return (
<>
Expand Down
Loading

0 comments on commit 73c20df

Please sign in to comment.